1 / 13
文档名称:

【实验】linux源代码分析实验报告格式.pdf

格式:pdf   大小:1,172KB   页数:13页
下载后只包含 1 个 PDF 格式的文档,没有任何的图纸或源代码,查看文件列表

如果您已付费下载过本站文档,您可以点这里二次下载

分享

预览

【实验】linux源代码分析实验报告格式.pdf

上传人:青山代下 2024/5/21 文件大小:1.14 MB

下载得到文件列表

【实验】linux源代码分析实验报告格式.pdf

相关文档

文档介绍

文档介绍:该【【实验】linux源代码分析实验报告格式 】是由【青山代下】上传分享,文档一共【13】页,该文档可以免费在线阅读,需要了解更多关于【【实验】linux源代码分析实验报告格式 】的内容,可以使用淘豆网的站内搜索功能,选择自己适合的文档,以下文字是截取该文章内的部分文字,如需要获得完整电子版,请下载此文档到您的设备,方便您编辑和打印。:..文档来源为:.【关键字】实验Linux的fork、exec、wait代码的分析指导老师:景建笃组员:王步月张少恒完成日期:一、、exec、wait代码的分析,了解一个操作系统进程的创建、执行、等待、退出的过程,锻炼学生分析大型软件代码的能力;,锻炼学生的合作能力。二、准备知识由于我们选的是题目二,所以为了明确分工,我们必须明白进程的定义。经过查阅资料,我们得知进程必须具备以下四个要素:1、有一段程序供其执行。这段程序不一定是进程专有,可以与其他进程共用。2、有起码的“私有财产”,这就是进程专用的系统堆栈空间3、有“户口”,这就是在内核中有一个task_struct结构,操作系统称为“进程控制块”。有了这个结构,进程才能成为内核调度的一个基本单位。同时,这个结构又是进程的“财产登记卡”,记录着进程所占用的各项资源。4、有独立的存储空间,意味着拥有专有的用户空间:进一步,还意味着除前述的系统空间堆栈外,还有其专用的用户空间堆栈。系统为每个进程分配了一个task_struct结构,实际分配了两个连续的物理页面(共8192字节),其图如下:对这些基本的知识有了初步了解之后,我们按老师的建议,商量分工。如下:四、小组成员以及任务分配1、王步月:,其中包含了get_pid和do_forkget_pid,写出代码分析结果,并画出流程图来表示相关函数之间的相互调用关系。所占工作比例35%。2、张少恒:,其中包含了do_execve。写出代码分析结果,并画出流程图来表示相关函数之间的相互调用关系。所占工作比例35%。3、余波:,其中包含了do_exit、sys_wait4。写出代码分析结果,并画出流程图来表示相关函数之间的相互调用关系。所占工作比例30%。五、各模块分析:1、)、,两个进程外,,,往往希望子进程结束后,还要把控制权交给父进程,,是把自己复制给子进程,也就是说,,用一个新的程序来覆盖子进程的内存空间,,子进程也常常用exit1文档来源为:.:..文档来源为:,进入僵死(zombie)状态,父进程可通过执行wait系统调用来实现与子进程的终止同步,)、代码分析intdo_fork(unsignedlongclone_flags,unsignedlongstack_start,structpt_regs*regs,unsignedlongstack_size){intretval;unsignedlongflags;structtask_struct*p;pletionvfork;if((clone_flags&(CLONE_NEWNS|CLONE_FS))==(CLONE_NEWNS|CLONE_FS))return-EINVAL;retval=-EPERM;/*将retval赋值-ENOMEM,作为task_struct结构申请失败时的返回值*/if(clone_flags&CLONE_PID){/*若clone_flags的位是置位的*//*若调用do_fork的当前(父)进程不是idle进程(其pid=0)*/if(current->pid)gotofork_out;}retval=-ENOMEM;/*返回错误信息*/p=alloc_task_struct();/*申请一个新的task_struct结构*/if(!p)gotofork_out;*p=*current;/*将当前(父)进程task_struct结构值赋给新创建的(子)进程*/p->tux_info=NULL;p->cpus_allowed_mask&=p->cpus_allowed;retval=-EAGAIN;/*若子(新)进程所属的用户拥有的进程数已达到规定的限制值,*则跳转至bad_fork_fre*/?if(atomic_read(&p->user->processes)>=p->rlim[RLIMIT_NPROC].rlim_cur&&!capable(CAP_SYS_ADMIN)&&!capable(CAP_SYS_RESOURCE))gotobad_fork_free;/*user->__count增一,user->processes(用户拥有的进程数)增一*/atomic_inc(&p->user->__count);atomic_inc(&p->user->processes);/*若系统进程数超过最大进程数则跳转至bad_fork_cleanup_count*/if(nr_threads>=max_threads)gotobad_fork_cleanup_count;get_exec_domain(p->exec_domain);/*若正在执行的代码是符合iBCS2标准的程序,则增加相对应模块的引用数目*//*若正在执行的代码属于全局执行文件结构格式则增加相对应模块的引用数目*/if(p->binfmt&&p->binfmt->module)__MOD_INC_USE_COUNT(p->binfmt->module);p->did_exec=0;/*将子进程标志为尚未执行*/p->swappable=0;/*清标志,使内存页面不可换出*/p->state=TASK_UNINTERRUPTIBLE;/*将子进程的状态置为uninterruptible*/copy_flags(clone_flags,p);/*将clone_flags略加修改写入p->flags*/p->pid=get_pid(clone_flags);/*调用kernel/:get_pid():.:..文档来源为:.*clone_flags中CLONE_PID位为1,那么父子进程共享一个pid号;否则要分配给子进*程一个从未用过的pid*/if(p->pid==0&&current->pid!=0)gotobad_fork_cleanup;/*对运行队列接口初始化*/INIT_LIST_HEAD(&p->run_list);p->p_cptr=NULL;init_waitqueue_head(&p->wait_chldexit);/*初始化wait_chldexit等待队列wait_chldexit用于在进程结束时,或发出*系统调用wait4后,为了等待子进程结束,而将自己(父进程)睡眠在该队列上*/p->vfork_done=NULL;if(clone_flags&CLONE_VFORK){p->vfork_done=&vfork;pletion(&vfork);}spin_lock_init(&p->alloc_lock);p->sigpending=0;init_sigpending(&p->pending);p->it_real_value=p->it_virt_value=p->it_prof_value=0;p->it_real_incr=p->it_virt_incr=p->it_prof_incr=0;init_timer(&p->real_timer);p->=(unsignedlong)p;p->leader=0;/*sessionleadershipdoesn'tinherit*/p->tty_old_pgrp=0;p->=p->=0;p->=p->=0;#ifdefCONFIG_SMP{inti;/*??shouldwejustmemsetthis??*/for(i=0;i<smp_num_cpus;i++)p->per_cpu_utime[cpu_logical_map(i)]=p->per_cpu_stime[cpu_logical_map(i)]=0;spin_lock_init(&p->sigmask_lock);}#endifp->array=NULL;p->lock_depth=-1;/*-1=没有锁*/p->start_time=jiffies_64;/*将当前的jiffies值作为子进程的创建时间*//*task_struct结构初始化完毕*/retval=-ENOMEM;/*copyalltheprocessinformation*/if(copy_files(clone_flags,p))/*复制所有的进程信息,根据clone_flags复制或共享父进程的打开文件表*/3文档来源为:.:..文档来源为:;if(copy_fs(clone_flags,p))/*根据clone_flags复制或共享父进程的系统信息*/gotobad_fork_cleanup_files;if(copy_sighand(clone_flags,p))/*根据clone_flags复制或共享父进程的信号处理句柄*/gotobad_fork_cleanup_fs;if(copy_mm(clone_flags,p))/*根据clone_flags复制或共享父进程的存储管理信息*/gotobad_fork_cleanup_sighand;if(copy_namespace(clone_flags,p))/*为子进程复制父进程系统空间堆栈*/gotobad_fork_cleanup_mm;/*若系统空间堆栈复制失败跳转至bad_fork_cleanup_mm*/retval=copy_thread(0,clone_flags,stack_start,stack_size,p,regs);if(retval)gotobad_fork_cleanup_namespace;p->semundo=NULL;/*将子进程task_struct结构的self_exec_id赋给parent_exec_id*/p->parent_exec_id=p->self_exec_id;p->swappable=1;/*新进程已经完成初始化,可以换出内存,所以将p->swappable赋1*/p->exit_signal=clone_flags&CSIGNAL;/*设置系统强行退出时发出的信号*/p->pdeath_signal=0;/*设置p->pdeath_signal*//**Sharethetimeslicebetweenparentandchild,thusthe*totalamountofpendingtimeslicesinthesystemdoesntchange,*resultinginmoreschedulingfairness.*/__save_flags(flags);__cli();if(!current->time_slice)/*将父进程的时间片减半*/BUG();p->time_slice=(current->time_slice+1)>>1;p->first_time_slice=1;current->time_slice>>=1;p->sleep_timestamp=jiffies;if(!current->time_slice){current->time_slice=1;scheduler_tick(0,0);}__restore_flags(flags);retval=p->pid;/*如果一切顺利,将子进程的pid作为返回值*/p->tgid=retval;INIT_LIST_HEAD(&p->thread_group);/*Needtasklistlockforparentetchandling!*/write_lock_irq(&tasklist_lock);/*给进程队列加锁*//*CLONE_PARENTre-usestheoldparent*/p->p_opptr=current->p_opptr;4文档来源为:.:..文档来源为:->p_pptr=current->p_pptr;if(!(clone_flags&CLONE_PARENT)){p->p_opptr=current;if(!(p->ptrace&PT_PTRACED))p->p_pptr=current;}if(clone_flags&CLONE_THREAD){p->tgid=current->tgid;list_add(&p->thread_group,&current->thread_group);}SET_LINKS(p);/*将子进程的task_struct结构链入进程队列*/hash_pid(p);/*将子进程的task_struct结构链入进程hash表*/nr_threads++;/*系统进程计数递增一*/write_unlock_irq(&tasklist_lock);/*解除对进程队列的封锁*/if(p->ptrace&PT_PTRACED)send_sig(SIGSTOP,p,1);wake_up_forked_process(p);/*最后做这件事,唤醒子进程*/++total_forks;/*total_forks增一*/if(clone_flags&CLONE_VFORK)pletion(&vfork);elsecurrent->need_resched=1;fork_out:/*若是vfork()调用do_fork,发down信号*/returnretval;/*退出do_fork(),返回retval值*/bad_fork_cleanup_namespace:exit_namespace(p);bad_fork_cleanup_mm:exit_mm(p);bad_fork_cleanup_sighand:/*处理子进程task_struct结构与信号处理相关的数据成员,并删除信号队列中与子进程相*关的信号量*/exit_sighand(p);bad_fork_cleanup_fs:/*处理子进程task_struct结构与文件系统信息相关的数据成员*/exit_fs(p);/*blocking*/bad_fork_cleanup_files:/*处理子进程task_struct结构与打开文件表相关的数据成员,并释放子进程的files_struct*结构*/exit_files(p);/*blocking*/bad_fork_cleanup:/*若正在执行的代码是符合iBCS2标准的程序,则减少相对应模块的引用数目*/put_exec_domain(p->exec_domain);if(p->binfmt&&p->binfmt->module)__MOD_DEC_USE_COUNT(p->binfmt->module);5文档来源为:.:..文档来源为::/*若正在执行的代码属于全局执行文件结构格式则减少相对应模块的引用数目*/atomic_dec(&p->user->processes);free_uid(p->user);/*清除子进程在user队列中的信息*/bad_fork_free:free_task_struct(p);/*释放子进程的task_struct结构*/gotofork_out;}三)、程序框图如下:2、)、概述进程通常是由父进程复制出来的(由fork()或clone())。假若子进程只是父进程的“影子”,那么没有什么意义了。所以,执行一个新的可执行程序才是创建进程的意义所在。在Linux中,提供了一个系统调用execve(),其内核入口是sys_execve()。二)、代码分析asmlinkageintsys_execve(structpt_regsregs){interror;char*filename;filename=getname((char*));error=PTR_ERR(filename);if(IS_ERR(filename))gotoout;error=do_execve(filename,(char**),(char**),&regs);if(error==0)current->ptrace&=~PT_DTRACE;putname(filename);out:returnerror;}。getname(),在系统空间建立起一个副本。getname()通过dogetname()从用户空间拷贝字符串。建立起一个可执行文件路径名的副本后,sys_execve()调用do_execve()以完成其主体部分的工作。intdo_execve(char*filename,char**argv,char**envp,structpt_regs*regs){structlinux_binprmbprm;//用于组织运行可执行文件所需的信息,通过此变量与负责处理其部分工作的其他//函数通信;在do_execve返回时废弃;structfile*file;intretval;inti;6文档来源为:.:..文档来源为:=open_exec(filename);//找到给定的可执行文件并打开;retval=PTR_ERR(file);if(IS_ERR(file))returnretval;//检测打开文件是否有错;首先将给定路径名的可执行文件找到并打开,因而调用open_exec()来实现。函数返回一个file结构指针,代表读入可执行文件的上下文,保存在变量bprm中。代码开始定义了一个linux_binprm结构的变量bprm,用于将运行一个可执行文件所需的信息组织在一起。linux_binprm结构定义(在include/linux/)如下:structlinux_binprm{charbuf[BINPRM_BUF_SIZE];structpage*page[MAX_ARG_PAGES];unsignedlongp;/*currenttopofmem*/intsh_bang;structfile*file;inte_uid,e_gid;kernel_cap_tcap_inheritable,cap_permitted,cap_effective;intargc,envc;char*filename;/*Nameofbinary*/unsignedlongloader,exec;};在do_execve()中,接下来的代码是:=PAGE_SIZE*MAX_ARG_PAGES-sizeof(void*);//初始化bprm结构的128k页表除去第一个argv[0]);memset(,0,MAX_ARG_PAGES*sizeof([0]));//将参数页面指针数组初始化为零;=file;//可执行文件的上下文;=filename;//可执行文件的路径名;=0;//可执行文件的性质;=0;=0;if((=count(argv,(void*)))<0){ess(file);fput(file);;}//根据argv数组计算非空指针个数并赋给argc成员;if((=count(envp,(void*)))<0){ess(file);fput(file);;}//统计环境变量个数并且赋值给envc的各个成员;retval=prepare_binprm(&bprm);////进行访问权限等内容的安全检测后,读入可执行文件前128字节;if(retval<0)gotoout;7文档来源为:.:..文档来源为:,此时初始化为0。其他两个变量也设置为0,因为现在还不知道文件性质。Bprm中定义了一个参数页面指针数组,通过memset()将此数组全设置为0;,原因是argv[0]是可执行文件的路径名。函数count()对用户空间作为参数传过来的字符串指针数组argv[]和环境变量envp[]进行计数。完成计数后,do_execve()调用prepare_binprm()对bprm中的其他成员准备信息。可执行文件的开头128个字节包含了文件属性的一些重要信息,并将这128个信息读入到bprm的缓冲区中。retval=copy_strings_kernel(1,&,&bprm);//从系统空间中拷贝可执行文件路径名;if(retval<0)gotoout;=;retval=copy_strings(,envp,&bprm);//从用户空间拷贝环境信息;if(retval<0)gotoout;retval=copy_strings(,argv,&bprm);//从用户空间拷贝参数信息;if(retval<0)gotoout;由于可执行文件的路径名已经在系统空间中了,所以调用copy_strings_kernel()拷贝到bprm中;其他的argv[]和envp[]还存在于用户空间,调用copy_strings()拷贝到bprm中。至此,可执行文件运行所需的所有信息都已组织到变量bprm中了,接下来的代码就是装入并运行了。retval=search_binary_handler(&bprm,regs);//从formats中搜索能够识别的二进制处理程序并将其装入内核,以便执行;if(retval>=0)//找到能够识别的二进制处理程序;/*ess*/returnretval;out:/*Somethingwentwrong,returntheinodeandfreetheargumentpages*/ess();if()fput();for(i=0;i<MAX_ARG_PAGES;i++){structpage*page=[i];if(page)__free_page(page);//释放为参数和环境所分配的物理页面,并返回一个负数通知调用者调用失败;}returnretval;}在此段代码中,最重要的的是函数search_binary_handler(),其中有两个for()循环。8文档来源为:.:..文档来源为:(每个成员是认识并处理唯一一种文件格式的二进制处理程序)成员的循环,让每个成员调用自己的load_binary()去识别;若能识别则装入可执行文件并运行,返回一个正数或零;不能则返回一个负数。内层寻常结束后,如果返回的负数是-ENOEXEC,表示所有的formats成员都不能识别此文件格式。如果内核支持动态安装模块,就根据目标文件的第2、3个字节生成一个binfmt模块名,通过request_module()试着将模块装入,以便外层for()循环在装入模块后再来一次内层循环。三)、程序框图如下:3、)、-exit(),cpu不会从do-exit()返回,中断服务程序不调用do-exit,in-interrupt(),先浏览整段代码,然后开始分析do-exit(),分别对这些函数进行分析,,已打开文件,工作目录,信号处理表等是通过函数_exit_mm(),_exit_files(),_exit_fs(),_exit_sighand()()存储空间的释放,调用mm_release()唤醒睡眠中的父进程,当前进程状态改成TASK_ZOMBIE,调用exit-notify(),-notify()后,回到do-exit(),调用schedule().Sys_wait4()函数中,参数pid为某一子进程的进程号,--struct结构释放后,,()把子进程删除,())、代码分析NORET_TYPEvoiddo_exit(longcode)//将退出代码作为参数处理,在其类型前用到特殊符号NORET_TYPE{structtask_struct*tsk=current;if(in_interrupt())if(!tsk->pid)if(tsk->pid==1)tsk->flags|=PF_EXITING;del_timer_sync(&tsk->real_timer);fake_volatile:#Tacct_process(code);#endifif(current->tux_info){#ifdefCONFIG_TUX_DEBUGcode,__builtin_return_address(0));#endifcurrent->tux_exit();}__exit_mm(tsk);//释放分配给它的内存9文档来源为:.:..();sem_exit();//释放信号量和其他systemVIPC结构__exit_files(tsk);//释放分配给它的文件__exit_fs(tsk);//释放文件系统的数据exit_namespace(tsk);exit_sighand(tsk);//释放信号量处理程序表