linux源代码分析实验报告格式
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
linux源代码分析实验报告格式
Linux的fork、exec、wait代码的分析
指导老师:景建笃
组员:王步月
张少恒
完成日期:2005-12-16
一、 设计目的
1.通过对Linux 的fork 、exec 、wait 代码的分析,了解一个操作系统进程的创建、执行、等待、退出的过程,锻炼学生分析大型软件代码的能力;
2.通过与同组同学的合作,锻炼学生的合作能力。
二、准备知识
由于我们选的是题目二,所以为了明确分工,我们必须明白进程的定义。经过查阅资料,我们得知进程必须具备以下四个要素:
1、有一段程序供其执行。这段程序不一定是进程专有,可以与其他进程共用。
2、有起码的“私有财产”,这就是进程专用的系统堆栈空间
3、有“户口”,这就是在内核中有一个task_struct 结构,操作系统称为“进程控制块”。有了这个结构,进程才能成为内核调度的一个基本单位。同时,这个结构又是进程的“财产登记卡”,记录着进程所占用的各项资源。
4、有独立的存储空间,意味着拥有专有的用户空间:进一步,还意味着除前述的系统空间堆栈外,还有其专用的用户空间堆栈。系统为每个进程分配了一个task_struct 结构,实际分配了两个连续的物理页面(共8192字节),其图如下: Struct task_struct (大约1K)
系统空间堆栈 (大约7KB )两个
连续
的物
理页
面
对这些基本的知识有了初步了解之后,我们按老师的建议,商量分工。如下:
四、 小组成员以及任务分配
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 -EINV AL;
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->rlim[RLIMIT_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);