linux源代码分析实验报告格式.docx
- 文档编号:26766322
- 上传时间:2023-06-22
- 格式:DOCX
- 页数:26
- 大小:318.66KB
linux源代码分析实验报告格式.docx
《linux源代码分析实验报告格式.docx》由会员分享,可在线阅读,更多相关《linux源代码分析实验报告格式.docx(26页珍藏版)》请在冰豆网上搜索。
linux源代码分析实验报告格式
linux源代码分析实验报告格式
LT
个父进程.而一个进程可以有多个子进程.父进程创建一个子进程完成一定的工作时,往往希望子进程结束后,还要把控制权交给父进程,因此子进程不应把父进程覆盖掉.fork系统调用创建子进程的做法,是把自己复制给子进程,也就是说,新创建的子进程是父进程的一个副本.继而子进程通过exec系统调用,用一个新的程序来覆盖子进程的内存空间,从而执行那个新程序.系统调用exit可以终止一个进程的执行,子进程也常常用exit系统调用来自我终止.子进程终止之后,进入僵死(zombie)状态,父进程可通过执行wait系统调用来实现与子进程的终止同步,接受子进程的返回状态和返回参数.
二)、代码分析
intdo_fork(unsignedlongclone_flags,unsignedlongstack_start,
structpt_regs*regs,unsignedlongstack_size)
{
intretval;
unsignedlongflags;
structtask_struct*p;
structcompletionvfork;
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/fork.c:
get_pid()为子进程分配一个pid.若是clone系统调用且
*clone_flags中CLONE_PID位为1,那么父子进程共享一个pid号;否则要分配给子进
*程一个从未用过的pid*/
if(p->pid==0&¤t->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;
init_completion(&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->real_timer.data=(unsignedlong)p;
p->leader=0;/*sessionleadershipdoesn'tinherit*/
p->tty_old_pgrp=0;
p->times.tms_utime=p->times.tms_stime=0;
p->times.tms_cutime=p->times.tms_cstime=0;
#ifdefCONFIG_SMP
{
inti;
/*?
?
shouldwejustmemsetthis?
?
*/
for(i=0;i p->per_cpu_utime[cpu_logical_map(i)]= p->per_cpu_stime[cpu_logical_map(i)]=0; spin_lock_init(&p->sigmask_lock); } #endif p->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复制或共享父进程的打开文件表*/ gotobad_fork_cleanup; 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; p->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,¤t->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) wait_for_completion(&vfork); else current->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); bad_fork_cleanup_count: /*若正在执行的代码属于全局执行文件结构格式则减少相对应模块的引用数目*/ atomic_dec(&p->user->processes); free_uid(p->user);/*清除子进程在user队列中的信息*/ bad_fork_free: free_task_struct(p);/*释放子进程的task_struct结构*/ gotofork_out; } 三)、程序框图如下: 2、exec.c 一)、概述 进程通常是由父进程复制出来的(由fork()或clone())。 假若子进程只是父进程的“影子”,那么没有什么意义了。 所以,执行一个新的可执行程序才是创建进程的意义所在。 在Linux中,提供了一个系统调用execve(),其内核入口是sys_execve()。 二)、代码分析 asmlinkageintsys_execve(structpt_regsregs) { interror; char*filename; filename=getname((char*)regs.ebx); error=PTR_ERR(filename); if(IS_ERR(filename)) gotoout; error=do_execve(filename,(char**)regs.ecx,(char**)regs.edx,®s); if(error==0) current->ptrace&=~PT_DTRACE; putname(filename); out: returnerror; } regs.ebx中的内容为应用程序中调用相应库函数时的第一个参数。 getname()把regs.ebx所指向的字符串从用户空间拷贝到系统空间,在系统空间建立起一个副本。 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; file=open_exec(filename);//找到给定的可执行文件并打开; retval=PTR_ERR(file); if(IS_ERR(file)) returnretval;//检测打开文件是否有错; 首先将给定路径名的可执行文件找到并打开,因而调用open_exec()来实现。 函数返回一个file结构指针,代表读入可执行文件的上下文,保存在变量bprm中。 代码开始定义了一个linux_binprm结构的变量bprm,用于将运行一个可执行文件所需的信息组织在一起。 linux_binprm结构定义(在include/linux/binfmts.h中)如下: 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()中,接下来的代码是: bprm.p=PAGE_SIZE*MAX_ARG_PAGES-sizeof(void*);//初始化bprm结构的128k页表除去第一个argv[0]); memset(bprm.page,0,MAX_ARG_PAGES*sizeof(bprm.page[0])); //将参数页面指针数组初始化为零; bprm.file=file;//可执行文件的上下文; bprm.filename=filename;//可执行文件的路径名; bprm.sh_bang=0;//可执行文件的性质; bprm.loader=0; bprm.exec=0; if((bprm.argc=count(argv,bprm.p/sizeof(void*)))<0){ allow_write_access(file); fput(file); returnbprm.argc; }//根据argv数组计算非空指针个数并赋给argc成员; if((bprm.envc=count(envp,bprm.p/sizeof(void*)))<0){ allow_write_access(file); fput(file); returnbprm.envc; }//统计环境变量个数并且赋值给envc的各个成员; retval=prepare_binprm(&bprm);////进行访问权限等内容的安全检测后,读入可执行文件前128字节; if(retval<0) gotoout; 变量bprm.sh_bang表示可执行文件的性质,此时初始化为0。 其他两个变量也设置为0,因为现在还不知道文件性质。 Bprm中定义了一个参数页面指针数组,通过memset()将此数组全设置为0;将bprm.p设置为这些页面的总和减去一个指针的大小,原因是argv[0]是可执行文件的路径名。 函数count()对用户空间作为参数传过来的字符串指针数组argv[]和环境变量envp[]进行计数。 完成计数后,do_execve()调用prepare_binprm()对bprm中的其他成员准备信息。 可执行文件的开头128个字节包含了文件属性的一些重要信息,并将这128个信息读入到bprm的缓冲区中。 retval=copy_strings_kernel(1,&bprm.filename,&bprm);//从系统空间中拷贝可执行文件路径名; if(retval<0) gotoout; bprm.exec=bprm.p; retval=copy_strings(bprm.envc,envp,&bprm);//从用户空间拷贝环境信息; if(retval<0) gotoout; retval=copy_strings(bprm.argc,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)//找到能够识别的二进制处理程序; /*
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux 源代码 分析 实验 报告 格式