《操作系统进程管理课程设计报告.doc》由会员分享,可在线阅读,更多相关《操作系统进程管理课程设计报告.doc(16页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、 分析Linux操作系统代码 要求:1、给c语言源程序加上注释, 2、划分程序的模块结构,并画出主要模块的流程图3、用另外一种程序设计语言java,vb,或c#实现主要模块的主要功能 南华大学计算机科学与技术学院课程设计报告 20 10 20 11 学年度 第 2 学期 课程名称操作系统设计题目进程管理专业船本09计算机班级01班分组成员负责任务骆兵 王荣 欧亚琪代码收集和注释刘利霞 肖明星流程图设计总结1.题目要求描述在计算机中要执行一个程序,首先将程序的代码从硬盘读入到内存,然后CPU根据程序代码指令一条一条的执行,而这时候的程序就叫进程,计算机中同时存在多个进程在等待被执行,比方你在用w
2、indow/Linux上网的时候,你一边用QQ和朋友聊天,一边在用MP3播放器听音乐,一边在浏览网页.那么这时候在计算机中就有三个进程在同时运行,一是QQ进程,二是MP3播放器进程,三是浏览器进程.但是计算机中只有一个CPU,在一个时间点上,它只能运行一个进程的指令,比方在2005/11/29/日 08/23/42/秒时计算机的CPU执行的是QQ进程的一条加法(mov)指令.那么计算机是怎样让三个进程同时运行起来的呢(至少我们看来是这样).在计算机内部所有的正在运行的进程都在等待被CPU执行,操作系统按一定的规律让所有的进程都得到CPU的执行.方法是一会儿让QQ进程执行,过了一定时间后又让MP
3、3播放器进程得到CPU的执行,再过一定时间后让浏览器进程得到CPU的执行.相对于人的反响,CPU的执行速度太快,所以人根本感觉不到这些进程的切换过程,也不会对人的操作产生影响现在的问题是操作系统是按照什么样的规律给个各进程分配CPU资源的.操作系统是按照个各进程的属性来分配CPU资源的.那进程的属性在什么呢?当操作系统创立一个进程时都就为这个进程分配了一定的内存空间来存放它的属性.在Linux操作系统中,操作系统定义了一个数据结构task_struct来描述各个进程的进程属性,task_struct结构叫进程控制块(PCB), 它是操作系统中重要的数据结构之一.具体定义在中,注意在Linux操
4、作系统中所有的操作系统源代码都在/usr/src目录下.2.程序流程图和源程序源码分析 Linux进程调度,系统管理 继续监听 Linux进程管理命令 Linux进程管理以及文本编辑3.程序设计task_struct结构的代码如下所示:/include/linux/sched.h struct task_struct volatile long state; /* 进程的状态,在代码后面有说明*/unsigned long flags; /* 进程标志 */int sigpending; mm_segment_t addr_limit; /* 线性地址空间: 0-0xBFFFFFFF 为用户线
5、性空间地址; 0-0xFFFFFFFF 为内核线性空间地址 */struct exec_domain *exec_domain; volatile long need_resched; unsigned long ptrace; int lock_depth; long counter; /* 进程的动态优先级,在代码后面有说明 */long nice; /* 进程的静态优先级,在代码后面有说明 */unsigned long policy; /* 进程采用的调度策略,在代码后面有说明 */struct mm_struct *mm; /* 进程属性中指向内存管理的数据结构mm_structd的
6、指针,在代码后面有说明 */int has_cpu, processor; unsigned long cpus_allowed; struct list_head run_list; unsigned long sleep_time; struct task_struct *next_task , *prev_task; /* 所以进程通过这两个指针组成一个双向链表*/struct mm_struct *active_mm; /* 指向活动地址空间,在后面的代码有说明 */ /* task state */struct linux_binfmt *binfmt; int exit_code,
7、 exit_signal;int pdeath_signal; unsigned long personality; int did_exec:1; int dumpable:1; pid_t pid; /* 进程标志符,在代码后面有说明 */pid_t pgrp; /* 进程组标号,在代码后面有说明 */pid_t tty_old_pgrp; pid_t session; pid_t tgid; int leader; struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr; /* 这五个标志表示一个进程的在计算机中
8、的亲属关系,分别标志祖先进程,父进程,子进程,弟进程和兄进程,为了在两个进程之间共享方便而设立 */struct list_head thread_group; struct task_struct *pidhist_next; struct task_struct *pidhist_pprev; /* 上面两个指针是为了在计算机中快速查一个进程而设立,具体方式以后讲*/wait_queue_head_t wait_chldexit; struct completion *vfork_sem; unsigned long rt_priority; /* 实时进程的优先级标志*/ unsigne
9、d long it_real_value, it_prof_value, it_virt_value; unsigned long it_real_incr, it_prof_incr, it_virt_incr; struct timer_list real_timer; struct tms times; struct tms group_times;unsigned long start_time; long per_cpu_utimeNR_CPUS, per_cpu_stimeNR_CPUS; unsigned long min_flt, maj_flt, nswap, cmin_fl
10、t, cmaj_flt, cnswap; int swappable:1; uid_t uid,euid,suid,fsuid; gid_t gid,egid,sgid,fsgid; int ngroups; gid_t groupsNGROUPS; kernel_cap_t cap_effective, cap_inheritable, cap_permitted; int keep_capabilities:1; struct user_struct *user; struct rlimit rlimRLIM_NLIMITS; unsigned short used_math; char
11、comm16; int link_count; struct tty_struct *tty; /* NULL if no tty */unsigned int locks; /* How many file locks are being held */struct sem_undo *semundo; struct sem_queue *semsleeping; struct thread_struct thread; struct fs_struct *fs; /* 进程属性中指向和文件管理有关的数据结构*/ struct files_struct *files; spinlock_t
12、sigmask_lock; struct signal_struct *sig; sigset_t blocked; struct sigpending pending; unsigned long sas_ss_sp; size_t sas_ss_size; int (*notifier)(void *priv); void *notifier_data; sigset_t *notifier_mask; u32 parent_exec_id; u32 self_exec_id; spinlock_t alloc_lock; spinlock_t switch_lock;程序说明如下: vo
13、latile long state 定义了进程的状态,进程总共有6种状态标志,分别是:一. TASK_RUNING. (正在运行状态或者是可运行状态)二. TASK_INTERRUPTIBLE,(可打断睡眠状态)三. TASK_UNINTERRUPTIBLE,(不可打断睡眠状态)四. TASK_ZOMBLE. (僵死状态)五. TAKS_STOPPED.(暂停状态)六. 交换状态.这六种属性标志了此进程现在的状态,各种不同的状态决定了进程什么时候获得CPU而被执行.正在运行状态是当前正在运行进程的状态.也就是说他的volatile long state被标志成TASK_RUANING.可运行状
14、态是那些正在等待CPU资源的进程的状态,这些进程在就绪队列run-queqe中.这些进程只要得到CPU在个资源就马上可以被运行.其也标志也为TASK_RUANING.可打断睡眠状态是哪些在等待队列中等待除CPU之外的其它资源的进程,只要它们所等待的资源到达就马上可以从等待队列进入就绪队列,状态标志也从TASK_INTERRUPTIBLE状态变成TASK_RUANING可运行状态.不可打断睡眠状态和可睡眠状态类似,区别在于前者可以用信号唤醒,而后者不可以.僵死状态是标志那些已经运行完毕释放了大局部资源并退出但还没有释放进程控制快task_struct的进程.暂停状态是那些遇到突发情况或者收到暂停
15、信号而暂时停止运行的进程的状态. counter是进程的动态优先级,它定义了一个在就绪队列的进程当它得到CPU后可运行的时间,用静态优先级初始化,当然计算机是以时钟中断做为时间的计数器,每发送一个时钟中断,动态优先级上的时间片就减少一个时钟中断的时间,时间片减到0的时候就退出该进程而调度另一个进程获得CPU. nice是进程的静态优先级,当一个在就绪队列中的进程获得CPU之后,它被赋予此进程可占有CPU的时间.这个时间被称为时间片. policy是进程的调度策略标志.有三种调度标志: SCHED_OTHER 普通进程的调度策略,基于优先权的轮转法. SCHED_FIFO 实时进程的调度策略,基
16、于先进先出的算法. SCHED_RR 实时进程的调度策略,基于优先权的轮转法.在linux中有两种进程,一种是普通进程,一种的实时进程,实时进程的优先级总是高于普通进程,也就是说当就绪队列中即有实时进程也有普通进程时,总是先运行实时进程.不同的进程采用不同的调度策略. rt_priority 实时进程的优先级. need_resched 标志下一次有调度时机的时候是否调用此进程.如果此进程没有没有被用户关闭或者其代码全被执行完了,在下一次调度时机应该还被调用.如果被用户关闭那么直接退出该进程. mm_struct *mm mm_struct数据结构是描述内存存储信息的数据结构,进程控制块tas
17、k_struct中用mm指针指想mm_struct数据结构.也就是在进程的属性中通过mm指针来管理起对应的内存区.mm_struct *active_active 内核线程用来指向调用它的普通进程的内存地址空间.当普通进程在运行时如果发生系统调用,程序就会从用户态转为内核态,内核态中执行的是内核线程,内核线程没有内存空间地址结构mm_struct,当他需要内存空间地址的时候就会调用用户态对应进程的用以空间地址结构mm_struct.内核线程就是就是通过active_mm指针来指向用户态进程的mm_struct结构. pid_t pid pid是进程标志符,操作系统每创立一个新的进程就要为这个新
18、进程分配一个进程控制快(PCB),那么系统内核是怎样区分这些进程的呢?就是通过进程标志符pid,系统在为新的进程分配进程控制块的候,它不是自己去创立,而是直接从上一个进程中复制它的进程控制块,其中里面的大局部东西保存下来,只做少量的改动,然后它的进程标志符加1赋值给新的进程控制块. 进程控制块的总共有80多项,在这里就不一一介绍了,以后我们在学习过程用到什么属性就介绍什么属性,这样在宏观上把握,不至于被一个小小的进程控制块给挡住了. 接着上面的进程说明,前面我们说了一下进程的根本概念和它的属性,接下来我们介绍一下在操作系统内部是怎样通过调度函数调度进程的.我们在使用计算机的时候,可能同时翻开了
19、很多程序,比方同时翻开IE浏览器,QQ,word文档等,那么这就意味着内核要同时管理多个用户进程.这些进程中每个进程每隔一段时间就要被CPU执行一次,所有的进程都是这样轮流执行,在Linux操作系统中一个进程占有CPU的时间为50ms(也就是5个时间滴答,关于时间滴答的概念后面介绍),这个时间对于人的反响来讲是相当快的,所以人更不感觉不到进程的调度和切换,各个进程也有轻重缓急之分,后面还会说一个goodness函数,这个函数比拟简单,主要作用是综合进程属性(进程控制快)中的各种因素和数据算出每个进程的权值,权值最大的进程就是调度程序(schedule()最应该调用的进程.在计算机中进程以六种状
20、态存在,这在进程的属性里已经有了介绍,其中存在于就绪队列TASK_RUNNING中处于运行状态的进程是等待计算机中唯一的资源CPU的进程,它们只要一得到CPU就马上可以被执行,那么操作系统是安什么样的规那么来让这些进程合理和公平的得到CPU的呢,这就要用到计算机的调度函数schedule(),_/kernel/sched。casmlinkage void schedule(void) struct schedule_data *sched_data; struct task_struct *prev, *next, *p; struct list_struct *tmp; int this_c
21、pu,c;if(!current-active_mm) BUG(); /* current指当前进程,当前进程所指的内存空间如果为空,那么进程必定有问题,出错返回 */need_resched_back: prev=current; this_cpu=prev-processor; if(in_interrupt() goto scheduling_in_interrupt; /*上面代码的意思是: 将局部变量prev和this_cpu初始化为当前进程和当前CPU.然后检查schedule()是否中断调用,假设是那么直接返回,其中goto后面的转到的地方在本程序中有定义,比方本进程后面的sch
22、eduling_in_interrupt在本程序的最后有定义,意思是: 打印出Scheduling in interrupt字符后退出 */release_kernel_lock(prev,this_cpu); if(softirq_active(this_cpu) & softirq_mask(this_cpu) goto handle_softirq; /* 检查是否有软中断请求,软中断softirq将在后面讲解 */hand_softirp_back: sched_data= & aligned_datathis_cpu.schedule_data; spin_lock_irp(&run
23、queue_lock);/* 局部变量sched_data中保存当前进程的数据区,*/ if(prev-policy = SCHED_RR) goto move_rr_last;* 当前基于优先权轮转法的调度策略实时进程,如果要调用它,先检查其count是否0假设是,那么将其挂到运行队列的最后面.具体做法在后面的 move_rr_last中有定义 */move_rr_back: switch(prev-state) case TESK_INTERRUPTIBLE: if(sign_pending(prev) prev-state = TASK_RUNNING; break; default:
24、del_form_runqueue(prev); case TASK_RUNNING; prev-need_resched=0; /* 上面这段代码意思是: 如果进程的状态为可中断睡眠状态且唤醒它的信号正在迫近,那么将其置为可运行状态,如果状态为运行状态那么忽略,如果状态除上述两种之外的其它状态那么直接删除.最后将neet_resched置为0,neet_resched置0意味着本能启动调度函数shchedule(); */repeat_schedule: next = idlt_task(this_cpu); c = -1000; if(prev-state = TASK_RUNNING)
25、goto still_running; /* 上面的代码中的c是优先级运算函数goodness的一个变量,优先级运算函数 goodness将是我们介绍的下一个函数,它的返回值是进程的权值,调度函数判断绪队列中权值最大的进程就是最优先要被调用的进程.goodness的具体算法看下一个函数.上面代码的意思是: 将next设置为当前cpu的idlt_task.将当前优先级的变量c赋值为-1000,其中-1000是goodness中最小的权值.然后判断当前进程是否是可运行进程,假设是那么转到still_running处执行,still_running在本函数的后面有解释.意思是: 将当前进程的good
26、ness值赋予goodness优先级变量c并返回still_running_back: list_for_each(tmp,&runqueue_head) p = list_entry(tmp, struct task_struct, run_list); if(can_schedule(p,this_cpu) int weight = goodness(p,this_cpu,prev-active_mm); if(weightc) c=weight,next=p; /* 上面代码的意思是: 遍历整个就绪队列,计算出每一个进程的goodness权值并且与当前进程做比拟,选择goodness权值
27、最大的那个进程获得cpu资源. */if(! c) goto recalculate; /* 上面代码的意思是: 如果当前goodness权值为0,那么运行recalculate:,起代码在后面有说明,意思是:重新计算每一进程的优先权,最后转到repeat_schedule:处执行,repeat_schedule:局部在前面已经有讲过,自己看有会过头去看一下吧 */sched_data-curr = next; /* 将当前进程信息保存到局部变量sched_data中*/#ifdef CONFIG_SMPnext-has_cpu = 1; next-processor = this_cpu;
28、/* 意思为: 将调度数据区指针指向下一个要调度的进程并将当前进程的cpu设置为下一个进程的当前cpu. */#endifspin_unlock_irq(&runqueue_lock);if(prev = next) goto same_processor; /* 意思为: 如果当前运行的进程的goodness还是队列中所以进程中最高的,也就是说下一个要调度的进程还是当前进程.那么只要重新得到cpu控制权返回need_resched_back:处执行. */ #ifdef CONFIG_SMPsched_data-last_schedule = get_cycles();#endifkstat
29、.context_swtch +;prepare_to_switch()struct mm_struct *mm = next-mm; struct mm_struct *oldmm = prev-active_mm; if(! mm) if(next-active_mm) BUG();next-active = oldmm; atomic_inc(&old_mm-mm_count); enter_lazy_tlb(oldmm,next,this_cpu); elseif(next-active_mm != mm) BUG();switch_mm(oldmm, mm, next, this_c
30、pu); if(! prev-mm)prev-active_mm = NULL; mmdrop(oldmm); switch_to(prev,next,prev); _scheduel_tail(prev); /* 上面的代码是进行进程切换 */* 下面的代码是前面代码中goto要转到的地方 */same_processor: reacquire_kernel_lock(current); if(current - need_resched) goto need_resched_back; return; recalculate: struct task_struct *p; spin_unl
31、ock_irq(&runqueue_lock); read_lock(&tasklist_lock); fot_each_task(p) p-counter = (p-counter 1) + NICE_TO_TICKS(p_nice); read_unlock(&tasklist_lock); spin_lock_irq(&runqueue_lock); goto repeat_schedule; still_running: c = goodness(prev, this_cpu, precv-active_mm); next=prev; goto still_running_back;h
32、andle_softirq: do_softirq();goto handle_softirq_back;move_rr_last: if(! prev-counter) prev-counter = NICE_TO_TICKS(prev_nice); move_last_runqueue(prev); goto move_rr_back; scheduling_in_interrupt: printf(“Scheduling in interrupt n); BUG();return();在上面的我们说了进程调度程序schedule(),接着说一下上面的函数中用到的进程优先级的权值的运行函数
33、googness();这个函数的作用很简单,就是算出就绪队列中的进程的优先级,然后调度程序调度优先级最高的进程获得当前CPU,计算机中进程分为两种,一为普通进程,它的优先级用进程控制块中的counter表示,二为实时进程,实时进程的优先级用rt_priority表示,它在进程控制块中有定义,当就绪队列中同时存在两种进程的时候,实时进程总是先于普通进程运行,它的实现机制是实时进程的权值以1000做为根底值,也就是说实时进程的权值是1000加上它的进程优先级,而普通进程的优先级就是它的进程优先级.整个函数比拟简单,但却是操作系统内核最为频繁调用的函数之一,也直接关系到操作系统的性能.此函数如下:
34、_static inline int goodness(struct task_struct * p, int this_cpu, struct mm_struct this_mm) int weigth; weight=-1; / * 定义函数返回的权值weight并进行初始化 */* 下面是普通进程的处理情况 */ if(p-* & SCHED_YIELD) goto out; / * 如果要放弃本次调度,那么直接返回, 转到的out处在后面有定义 */ if(p-policy = SCHED_OTHER) weight = p- counter;if(!weight) goto out;
35、 /* 函数是普通进程的情况,在这种情况下,进程的权值直接为本进程的剩余时间,如果剩余时间不存在或为0,那么直接返回 */#ifdef CONFIG_SMPif(p-processor = this_cpu) weight += PROC_CHANGE_PENALTY; #endifif(p-mm = this_mm | !p-mm) weight +=1; weight +=20-p-nice; goto out; /* 下面是实时进程的处理情况 */ weight = 1000 + p-re_priority;/* 实时进程的权值至少为1000,也就说它的权值是它是基数1000加上实时进程
36、的优先级,其中实时进程的优先级用re_priority表示,re_priority在进程的属性中有定义, out: return weight; /* 前面的代码中有调用 */前面我们说了关于进程的概念,属性以及调度它的函数.接下来我们讲一下进程的创立,进程创立就是当系统接收到一个要求创立进程的信号时,系统为新进程所做的一系列工作.比方当你在使用计算机上网的时候如果翻开了QQ聊天的效劳程序的话.当你点击QQ图标的时候,系统就接收到了一个创立QQ进程的信号,接下来系统就要做一系列的工作来完成这个过程,这就是我们下面要讲的内容: 根底创立.创立进程的系统调用函数(系统调用是下一个要讲的内容)有三个
37、,它们分别是: sys_fork(), sys_clone(), sys_vfork().由它们三个函数来完成进程的创立,这三个函数都比拟简单,它们只是都同时调用了一个函数do_fork(),由于前面三个函数都比拟简单,我们在后面讲,我们先讲一下do_fork()函数,这才是创立进程的主体.它被前面三个函数调用int do_fork(unsigned long clone_flags, unsigned long stack_start, struct pt_regs, unsigned long stack_size) int retval = -ENOMEN; struct task_st
38、ruct *p; DECLARE_MUTEX_LOCKED(sem); if(clone_flags & CLONE_PID) if(current-pid) return -EPERM; /* 上面这段代码的意思是: 如果clone_flags的CLONE_PID置位,那么除非是0号进程否那么将返回错误信息EPERM. clone_flags的定义将在后面介绍,0号进程就是操作系统启动时创立的第一个进程.上面的代码中if(current-pid)要成立,除非是0号进程.其中pid是进程标志符 */current-vfork_sem = &sem; p=alloc_task_struct();
39、if(! p) goto fork_out;/ * 上面代码的意思是: 申请一个新的内存控制块,其中alloc_task_struct是在/include中的宏定义: #define alloc_task_struct() (struct task_struct * ) _get_free_pages (GFP_KERNEL, 1)其中_get_free_pages是要申请两个页面,页面是内存管理的有关内容,这儿大家只要一个页面占4个字节就可以了.两个页面就是8个字节,而大家查一下进程控制块的大小就可以知道它只有1600KB左右,有就时说它只有1M左右,那么还剩下将近的空间去有什么用呢?其实剩
40、余的空间是作为内核栈空间.至于内核栈的内容以后会讲到,但是如果申请内存空间失败那么直接栈道fork_out处执行,fork_out在后面有定义 */*p = * current; / * 代码意思: 前面申请了一个存放进程控制块的内存空间,接下创立新进程的进程控制块的内容,具体做法是先直接将其父进程的进程控制块的内容复制过来,再在后面对需要更新的局部做一些必要的赋值 */retval= -EAGAIN; if(atomic_read(&p-user-processor) = p-rlimRLIMIT_NPROC. rlim_cur) goto bad_fork_free; atomic_inc
41、(& p-user-_count); atomic_inc(& p-user-processes); /* 上面代码的意思: 如果父亲进程已经用了用户进程,那么修改p-user-_count和p-user-processes,将其数目加1,当然除非用户进程的 RLIMIT_NPROC已经超过额定数目,如果你对代码不理解,那就先不要理解了,等你学完后面的代码时就能理解了*/if(nr_threads = max_threads) goto bad_fork_cleanup_count; /* 上面的代码的意思是: 如果计算机中的进程的最大的进程数大于系统限定的最大数目就返回错误信号 */ get
42、_exec_domain(p-exec_domain); if(p-binfmt & p-binfmt-module)p-did_exec=NULL; p-swappable=NULL;p-state = TASK_UNINTERRUPTIBLE; / * 上面的代码比拟简单: 其中p是指向进程控制块task_struct的指针,did_exec和swappable以及state都在进程控制块中有定义,是对进程的这三个属性进行赋值,其意思如下: did_exec标志的意思是: 此进程是否被执行. swappable标志的意思是: 此进程占有的内存页面是否可以被换出到交换空间. state在前面
43、有讲过.那么上面代码的意思是刚创立的进程没有被执行并且内存空间页面不可以被换出内存,当前状态设置为不可打断睡眠状态 */ copy_flags(clone_flags, p); p-pid = get_pid(clone_flags); / * 上面代码涉及到进程控制块中的进程标志flags,在 task_struct中有定义,flags的意思是进程中有一些特殊的时候要靠它来决定进程调度函数的调度,上面的意思是: 将传进来的参数clone_flags通过copy_flags()函数转化为子进程的进程标志flags, 然后为新的子进程创立一个进程标志符pid */p-run_list.next=
44、NULL; p-run_list.prev=NULL; / * 对进程队列run_list进行初始化,进程队列run_list以后将 */if(clone_flags & CLONG_VFORK) | ! (clone_flags & CLONG_PARENT) p-p_opptr = current; if(! (p-ptrace & PT_PTRACED) p-p_pptr = current; / * 前面我们说过创立新进程的系统调用函数有三个,分别是sys_fork,sys_vfork, sys_clone.上面的意思是: 如果使用的sys_vfork()函数调用创立函数或者传人的参数
45、clone_flags的CLONE_PARENT位为1,那么将子进程的的p_opptr()指针指向当前进程.其中p_opptr指针在进程控制块task_struct中有定义,意思是它指向进程的祖先进程.如果ptrace的PT_PTRACED不为1,那么将进程的指向父进程的指针指向当前进程.其中p_pptr是进程指向其父亲进程的指针,同样在进程控制块task_struct中有定义 * /p-p_cptr = NULL; / * 进程的p_cptr指针指向它的儿子进程,在这里初始化为NULL * /init_waitqueue_head(&p-wait_chldexit); / * 意思是: 初始
46、化等待队列,等待队列是就绪队列一个根底,等待队列的进程当它得到它等待的资源(除CPU资源,CPUs是就绪队列中的进程等待的资源)后马上进入就绪队列,等待调度函数的调用以获得CPU资源 * / * 下面所以的代码都是对进程控制块task_struct一些变量做一些初始化,这儿先就不过多的介绍了,现在只要知道有这些东西就行了 * /p-vfork_sem=NULL; spin_lock_inin(& p-alloc_lock);p-sigpending = 0;init_sigpending(& p-pending);p-it_real_value = p-it_virt_value =p-it_p