当前位置:文档之家› linux源代码分析实验报告格式

linux源代码分析实验报告格式

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);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 doesn't 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_SMP{int i;/* ?? should we just memset this ?? */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;/* 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, &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)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;}三)、程序框图如下:。

相关主题