




已阅读5页,还剩12页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
操作系统报告fork、exec、wait代码的分析Linux的fork、exec、wait代码的分析指导老师:景建笃组员:王步月张少恒完成日期:2005-12-16一、 设计目的1.通过对Linux的fork、exec、wait代码的分析,了解一个操作系统进程的创建、执行、等待、退出的过程,锻炼学生分析大型软件代码的能力;2.通过与同组同学的合作,锻炼学生的合作能力。二、准备知识由于我们选的是题目二,所以为了明确分工,我们必须明白进程的定义。经过查阅资料,我们得知进程必须具备以下四个要素:1、有一段程序供其执行。这段程序不一定是进程专有,可以与其他进程共用。2、有起码的“私有财产”,这就是进程专用的系统堆栈空间3、有“户口”,这就是在内核中有一个task_struct结构,操作系统称为“进程控制块”。有了这个结构,进程才能成为内核调度的一个基本单位。同时,这个结构又是进程的“财产登记卡”,记录着进程所占用的各项资源。4、有独立的存储空间,意味着拥有专有的用户空间:进一步,还意味着除前述的系统空间堆栈外,还有其专用的用户空间堆栈。系统为每个进程分配了一个task_struct结构,实际分配了两个连续的物理页面(共8192字节),其图如下:对这些基本的知识有了初步了解之后,我们按老师的建议,商量分工。如下:四、 小组成员以及任务分配1、王步月:分析进程的创建函数fork.c,其中包含了get_pid和do_fork get_pid,写出代码分析结果,并画出流程图来表示相关函数之间的相互调用关系。所占工作比例35%。2、张少恒:分析进程的执行函数exec.c,其中包含了do_execve。写出代码分析结果,并画出流程图来表示相关函数之间的相互调用关系。所占工作比例35% 。3、余波:分析进程的退出函数exit.c,其中包含了do_exit、sys_wait4。写出代码分析结果,并画出流程图来表示相关函数之间的相互调用关系。所占工作比例30% 。五、各模块分析:1、fork.c一)、概述进程大多数是由FORK系统调用创建的.fork能满足非常高效的生灭机制.除了0进程等少数一,两个进程外,几乎所有的进程都是被另一个进程执行fork系统调用创建的.调用fork的进程是父进程,由fork创建的程是子进程.每个进程都有一个父进程.而一个进程可以有多个子进程.父进程创建一个子进程完成一定的工作时,往往希望子进程结束后,还要把控制权交给父进程,因此子进程不应把父进程覆盖掉.fork系统调用创建子进程的做法,是把自己复制给子进程,也就是说,新创建的子进程是父进程的一个副本.继而子进程通过exec系统调用,用一个新的程序来覆盖子进程的内存空间,从而执行那个新程序.系统调用exit可以终止一个进程的执行,子进程也常常用exit系统调用来自我终止.子进程终止之后,进入僵死(zombie)状态,父进程可通过执行wait系统调用来实现与子进程的终止同步,接受子进程的返回状态和返回参数. 二)、代码分析int do_fork(unsigned long clone_flags, unsigned long stack_start, struct pt_regs *regs, unsigned long stack_size)int retval;unsigned long flags;struct task_struct *p;struct completion vfork;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)goto fork_out;retval = -ENOMEM;/*返回错误信息*/p = alloc_task_struct(); /* 申请一个新的task_struct结构*/ if (!p)goto fork_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-rlimRLIMIT_NPROC.rlim_cur & !capable(CAP_SYS_ADMIN) & !capable(CAP_SYS_RESOURCE)goto bad_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)goto bad_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 & current-pid != 0)goto bad_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 = (unsigned long) p;p-leader = 0;/* session leadership doesnt inherit */p-tty_old_pgrp = 0;p-times.tms_utime = p-times.tms_stime = 0;p-times.tms_cutime = p-times.tms_cstime = 0;#ifdef CONFIG_SMPint i;/* ? should we just memset this ? */for(i = 0; i per_cpu_utimecpu_logical_map(i) =p-per_cpu_stimecpu_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;/* copy all the process information */if (copy_files(clone_flags, p)/* 复制所有的进程信息,根据clone_flags复制或共享父进程的打开文件表*/ goto bad_fork_cleanup;if (copy_fs(clone_flags, p)/* 根据clone_flags复制或共享父进程的系统信息 */ goto bad_fork_cleanup_files;if (copy_sighand(clone_flags, p)/* 根据clone_flags复制或共享父进程的信号处理句柄 */ goto bad_fork_cleanup_fs;if (copy_mm(clone_flags, p)/* 根据clone_flags复制或共享父进程的存储管理信息 */ goto bad_fork_cleanup_sighand;if (copy_namespace(clone_flags, p)/* 为子进程复制父进程系统空间堆栈 */ goto bad_fork_cleanup_mm;/* 若系统空间堆栈复制失败跳转至bad_fork_cleanup_mm */ retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);if (retval)goto bad_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 */ /* * Share the timeslice between parent and child, thus the * total amount of pending timeslices in the system doesnt change, * resulting in more scheduling fairness.*/_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);/* Need tasklist lock for parent etc handling! */write_lock_irq(&tasklist_lock);/* 给进程队列加锁 */ /* CLONE_PARENT re-uses the old parent */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);elsecurrent-need_resched = 1;fork_out:/* 若是vfork()调用do_fork,发down信号*/ return retval;/* 退出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结构 */ goto fork_out;三)、程序框图如下:2、exec.c一)、概述进程通常是由父进程复制出来的(由fork()或clone()。假若子进程只是父进程的“影子”,那么没有什么意义了。所以,执行一个新的可执行程序才是创建进程的意义所在。在Linux中,提供了一个系统调用execve(),其内核入口是sys_execve()。二)、代码分析asmlinkage int sys_execve(struct pt_regs regs ) int error;char *filename;filename=getname(char*)regs.ebx);error=PTR_ERR(filename);if(IS_ERR(filename) goto out; error=do_execve(filename,(char*)regs.ecx,(char* )regs.edx,®s); if(error0) current-ptrace &= PT_DTRACE; putname(filename); out: return error; regs.ebx中的内容为应用程序中调用相应库函数时的第一个参数。getname()把regs.ebx所指向的字符串从用户空间拷贝到系统空间,在系统空间建立起一个副本。getname()通过dogetname()从用户空间拷贝字符串。建立起一个可执行文件路径名的副本后,sys_execve()调用do_execve()以完成其主体部分的工作。int do_execve(char * filename, char * argv, char * envp, struct pt_regs * regs)struct linux_binprm bprm; /用于组织运行可执行文件所需的信息,通过此变量与负责处理其部分工作的其他/函数通信;在do_execve返回时废弃;struct file *file; int retval;int i;file = open_exec(filename); /找到给定的可执行文件并打开;retval = PTR_ERR(file);if (IS_ERR(file)return retval; /检测打开文件是否有错;首先将给定路径名的可执行文件找到并打开,因而调用open_exec()来实现。函数返回一个file结构指针,代表读入可执行文件的上下文,保存在变量bprm中。代码开始定义了一个linux_binprm结构的变量bprm,用于将运行一个可执行文件所需的信息组织在一起。linux_binprm结构定义(在include/linux/binfmts.h中)如下:struct linux_binprmchar bufBINPRM_BUF_SIZE;struct page *pageMAX_ARG_PAGES;unsigned long p; /* current top of mem */int sh_bang;struct file * file;int e_uid, e_gid;kernel_cap_t cap_inheritable, cap_permitted, cap_effective;int argc, envc;char * filename;/* Name of binary */unsigned long loader, exec;在do_execve()中,接下来的代码是: bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);/初始化bprm结构的128k页表除去第一个argv0);memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page0); /将参数页面指针数组初始化为零;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);return bprm.argc; /根据argv数组计算非空指针个数并赋给argc成员; if (bprm.envc = count(envp, bprm.p / sizeof(void *) 0) allow_write_access(file);fput(file);return bprm.envc; /统计环境变量个数并且赋值给envc的各个成员;retval = prepare_binprm(&bprm); /进行访问权限等内容的安全检测后,读入可执行文件前128字节;if (retval 0) goto out; 变量bprm.sh_bang表示可执行文件的性质,此时初始化为0。其他两个变量也设置为0,因为现在还不知道文件性质。Bprm中定义了一个参数页面指针数组,通过memset()将此数组全设置为0;将bprm.p设置为这些页面的总和减去一个指针的大小,原因是argv0是可执行文件的路径名。函数count()对用户空间作为参数传过来的字符串指针数组argv和环境变量envp进行计数。 完成计数后,do_execve()调用prepare_binprm()对bprm中的其他成员准备信息。可执行文件的开头128个字节包含了文件属性的一些重要信息,并将这128个信息读入到bprm的缓冲区中。retval = copy_strings_kernel(1, &bprm.filename, &bprm); /从系统空间中拷贝可执行文件路径名;if (retval 0) goto out; bprm.exec = bprm.p;retval = copy_strings(bprm.envc, envp, &bprm); /从用户空间拷贝环境信息;if (retval 0) goto out; retval = copy_strings(bprm.argc, argv, &bprm); /从用户空间拷贝参数信息;if (retval = 0) /找到能够识别的二进制处理程序;/* execve success */return retval;out:/* Something went wrong, return the inode and free the argument pages*/allow_write_access(bprm.file);if (bprm.file)fput(bprm.file);for (i = 0 ; i pid)panic(Attempted to kill the idle task!);if (tsk-pid = 1)panic(Attempted to kill init!);tsk-flags |= PF_EXITING;del_timer_sync(&tsk-real_timer);fake_volatile:#ifdef CONFIG_BSD_PROCESS_ACCTacct_process(code);#endifif (current-tux_info) #ifdef CONFIG_TUX_DEBUGprintk(Possibly unexpected TUX-thread exit(%ld) at %p?n,code, _builtin_return_address(0);#endifcurrent-tux_exit();_exit_mm(tsk);/释放分配给它的内存lock_kernel();sem_exit();/释放信号量和其他system VIPC结构_exit_files(tsk);/释放分配给它的文件_exit_fs(tsk);/释放文件系统的数据exit_namespace(tsk);exit_sighand(tsk);/释放信号量处理程序表exit_thread();if (current-leader)disassociate_ctty(1);put_exec_domain(tsk-exec_domain);if (tsk-binfmt & tsk-binfmt-module)_MOD_DEC_USE_COUNT(tsk-binfmt-module);tsk-exit_code = code;exit_notify();/调用exit_notify,它会警告当前退出任务的祖先进程和其进程组中的所有成员该进程正在退出schedule();/*调用schedule(),释放CPU,这对schedule() 的调用从来不返回因为它跳转到下一个进程的上下文, 不会再跳转回来,这是现在正在退出的进程最后一次 拥有CPU的机会*/BUG();asmlinkage long sys_exit(int error_code)do_exit(error_code&0xff)wait_chldexit,&wait);/增加到等待队列中repeat:flag = 0;current-state = TASK_INTERRUPTIBLE;read_lock(&tasklist_lock);tsk = current;do struct task_struct *p; for (p = tsk-p_cptr ; p ; p = p-p_osptr)/循环遍历该进程的直接子进程if (pid0) /根据pid参数的值筛选出不匹配的PIDif (p-pid != pid)continue; else if (!pid) if (p-pgrp != current-pgrp)continue; else if (pid != -1) if (p-pgrp != -pid)continue;if (p-exit_signal != SIGCHLD) (options & _WCLONE) != 0) & !(options & _WALL)continue;flag = 1;switch (p-state) case TASK_STOPPED:if (!p-exit_code)continue;if (!(options & WUNTRACED) & !(p-ptrace & PT_PTRACED)continue;read_unlock(&tasklist_lock);retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0; if (!retval & stat_addr) retval = put_user(p-exit_code exit_code = 0;retval = p-pid;goto end_wait4;case TASK_ZOMBIE:/祖先进程正在等待一个已经结束了的进程current-times.tms_cutime += p-times.tms_utime + p-times.tms_cutime;current-times.tms_cstime += p-times.tms_stime + p-times.tms_cstime;read_unlock(&tasklist_lock);retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;/*其他的资源使用信息被收集起来,子孙进程的退出状态被传递到特定的地址中*/if (!retval & stat_addr)retval = put_user(p-exit_code, stat_addr);if (retval)goto end_wait4; retval = p-pid;/设置retval为当前得到的死亡子孙进程的PID;retval不会在改变if (p-p_opptr != p-p_pptr) /*如果这个垂死进程的当前祖先进程不是原来的祖先进程,进程就会离开进程图表中的当前位置,在其原始祖先的控制下,重新安装自己,接着给其祖先进程发送SIGCHLD信号量 ,这样祖先进程就知道其子孙进程已推出*/write_lock_irq(&tasklist_lock);REMOVE_LINKS(p);p-p_pptr = p-p_opptr;SET_LINKS(p);do_notify_parent(p, SIGCHLD);write_unlock_irq(&tasklist_lock); elserelease_task(p);/调用release释放所得子孙进程的struct task_struct结构goto end_wait4;/成功获取了子孙进程,sys_wait4返回到retvaldefault:/*执行前面的for循环,因为只有既没有停止运行,也不是僵进程 的进程才会执行default的情况*/continue;if (opt
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 济南市2025-2026学年八年级下学期语文月考测试试卷
- 高速铁路的运输组织
- 电表行业知识培训课件
- 文库发布:高血压课件
- 高考病句类型课件
- 输电线路迁改工程资金补偿协议
- SHE考试题及答案
- ppe考试试题及答案
- 河南省南阳市方城县2022-2023学年九年级上学期期中化学试题(含答案)
- 电站防汛知识培训内容课件
- SC/T 9010-2000渔港总体设计规范
- 乡镇应急信息报送制度
- 实验动物遗传学及质量控制课件
- 认识电影走进电影课件
- 新能源概论全解课件
- 服务器虚拟化资源调研表
- 2022年杭州市中小学教师职称考试卷
- 《中国公民科学素质基准》题库500题(精品)
- 第三章药物治疗的基本过程
- Minitab教程源数据及六西格玛绿带手册相关工作表 鱼骨图
- 大客户销售技巧与客户关系管理(共94页).ppt
评论
0/150
提交评论