收藏 分享(赏)

实验三 进程的创建实验.doc

上传人:scg750829 文档编号:6876718 上传时间:2019-04-25 格式:DOC 页数:36 大小:198KB
下载 相关 举报
实验三  进程的创建实验.doc_第1页
第1页 / 共36页
实验三  进程的创建实验.doc_第2页
第2页 / 共36页
实验三  进程的创建实验.doc_第3页
第3页 / 共36页
实验三  进程的创建实验.doc_第4页
第4页 / 共36页
实验三  进程的创建实验.doc_第5页
第5页 / 共36页
点击查看更多>>
资源描述

1、实验三 进程的创建实验实验目的及要求1.掌握进程的概念,明确进程的含义;2.认识并了解并发执行的实质;3.理解 LINUX 下进程创建的基本方法和过程。实验原理一、进程UNIX 中,进程既是一个独立拥有资源的基本单位,又是一个独立调度的基本单位。一个进程实体由若干个区(段)组成,包括程序区、数据区、栈区、共享存储区等。每个区又分为若干页,每个进程配置有唯一的进程控制块 PCB,用于控制和管理进程。PCB 的数据结构如下:1进程表项(Process Table Entry) 。包括一些最常用的核心数据:进程标识符 PID、用户标识符 UID、进程状态、事件描述符、进程和 U 区在内存或外存的地址

2、、软中断信号、计时域、进程的大小、偏置值 nice、指向就绪队列中下一个 PCB的指针 P_Link、指向 U 区进程正文、数据及栈在内存区域的指针。2U 区(U Area) 。用于存放进程表项的一些扩充信息。每一个进程都有一个私用的 U 区,其中含有:进程表项指针、真正用户标识符 u-ruid(read user ID)、有效用户标识符 u-euid(effective user ID)、用户文件描述符表、计时器、内部 I/O 参数、限制字段、差错字段、返回值、信号处理数组。由于 UNIX 系统采用段页式存储管理,为了把段的起始虚地址变换为段在系统中的物理地址,便于实现区的共享,所以还有:3

3、系统区表项。以存放各个段在物理存储器中的位置等信息。系统把一个进程的虚地址空间划分为若干个连续的逻辑区,有正文区、数据区、栈区等。这些区是可被共享和保护的独立实体,多个进程可共享一个区。为了对区进行管理,核心中设置一个系统区表,各表项中记录了以下有关描述活动区的信息:区的类型和大小、区的状态、区在物理存储器中的位置、引用计数、指向文件索引结点的指针。4进程区表系统为每个进程配置了一张进程区表。表中,每一项记录一个区的起始虚地址及指向系统区表中对应的区表项。核心通过查找进程区表和系统区表,便可将区的逻辑地址变换为物理地址。二、进程映像UNIX 系统中,进程是进程映像的执行过程,也就是正在执行的进

4、程实体。它由三部分组成:1用户级上、下文。主要成分是用户程序;2寄存器上、下文。由 CPU 中的一些寄存器的内容组成,如 PC,PSW,SP 及通用寄存器等;3系统级上、下文。包括 OS 为管理进程所用的信息,有静态和动态之分。三、所涉及的系统调用1fork( ) 创建一个新进程。 系统调用格式: pid=fork( )参数定义:int fork( )fork( )返回值意义如下:0:在子进程中,pid 变量保存的 fork( )返回值为 0,表示当前进程是子进程。0:在父进程中,pid 变量保存的 fork( )返回值为子进程的 pid 值(进程唯一标识符) 。-1:表示进程创建失败。如果

5、fork( )调用成功,它向父进程返回子进程的 PID,并向子进程返回 0,即 fork( )被调用了一次,但返回了两次。此时 OS 在内存中建立一个新进程,所建的新进程是调用fork( )父进程( parent process)的副本,称为子进程(child process) 。子进程继承了父进程的许多特性,并具有与父进程完全相同的用户级上下文。父进程与子进程并发执行。核心为 fork( )完成以下操作:(1)为新进程分配一进程表项和进程标识符进入 fork( )后,核心检查系统是否有足够的资源来建立一个新进程。若资源不足,则fork( )系统调用失败;否则,核心为新进程分配一进程表项和唯一

6、的进程标识符。(2)检查同时运行的进程数目超过预先规定的最大数目时,fork( )系统调用失败。(3)拷贝进程表项中的数据将父进程的当前目录和所有已打开的数据拷贝到子进程表项中,并置进程的状态为“创建”状态。(4)子进程继承父进程的所有文件对父进程当前目录和所有已打开的文件表项中的引用计数加 1。(5)为子进程创建进程上、下文进程创建结束,设子进程状态为“内存中就绪”并返回子进程的标识符。(6)子进程执行虽然父进程与子进程程序完全相同,但每个进程都有自己的程序计数器 PC(注意子进程的 PC 开始位置 ),然后根据 pid 变量保存的 fork( )返回值的不同,执行了不同的分支语句。实验内容

7、及步骤1编写一段程序,使用系统调用 fork( )创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示a,子进程分别显示字符b和字符c。试观察记录屏幕上的显示结果,并分析原因。2修改上述程序,每一个进程循环显示一句话。子进程显示daughter 及son ,父进程显示 parent ,观察结果,分析原因。实验指导一、参考程序实验步骤一:#include main( )int p1,p2;printf(“n”);while(p1=fork( )= = -1); /*创建子进程 p1*/if (p1= =0) putchar(b); e

8、lse while(p2=fork( )= = -1); /*创建子进程 p2*/if(p2= =0) putchar(c); else putchar(a); 实验步骤二:#include main( )int p1,p2,i;while(p1=fork( )= = -1); /*创建子进程 p1*/if (p1= =0)for(i=0;i#INCLUDE #INCLUDE #INCLUDE #INCLUDE EXTERN VOID WRITE_VERIFY(UNSIGNED LONG ADDRESS) ;LONG LAST_PID = 0; /最新进程号,其值会由 GET_EMPTY_PR

9、OCESS 生成VOID VERIFY_AREA(VOID * ADDR , INT SIZE)UNSIGNED LONG START ;START = (UNSIGNED LONG) ADDR ;SIZE += START START START += GET_BASE(CURRENT-LDT2) ;WHILE(SIZE 0)SIZE -= 4096 ; WRITE_VERIFY(START) ;START += 4096 ;/该函数为新的进程在线性地址空间中设置新的代码段和数据段基地址和限长INT COPY_MEM(INT NR , STRUCT TASK_STRUCT * P)UNSIG

10、NED LONG OLD_DATA_BASE , NEW_DATA_BASE , DATA_LIMIT ;UNSIGNED LONG OLD_CODE_BASE , NEW_CODE_BASE , CODE_LIMIT ;CODE_LIMIT = GET_LIMIT(0X0F) ;DATA_LIMIT = GET_LIMIT(0X17) ;OLD_CODE_BASE = GET_BASE(CURRENT-LDT1) ;OLD_DATA_BASE = GET_BASE(CURRENT-LDT2) ;IF(OLD_CODE_BASE != OLD_DATA_BASE)PANIC(“WE DONOT

11、 SUPPORT SEPERATE ID“) ;IF(DATA_LIMIT START_CODE = NEW_CODE_BASE ;SET_BASE(P-LDT1 , NEW_CODE_BASE) ;SET_BASE(P-LDT2 , NEW_DATA_BASE) ;IF(COPY_PAGE_TABLES(OLD_DATA_BASE , NEW_DATA_BASE , DATA_LIMIT)FREE_PAGE_TABLES(NEW_DATA_BASE , DATA_LIMIT) ;RETURN -ENOMEM ;RETURN 0 ; /下面是主要的 FORK 程序,负责复制系统进程信息/并且设

12、置必要的寄存器,还整个地复制数据段INT COPY_PROCESS(INT NR , LONG EBP , LONG EDI , LONG ESI , LONG GS , LONG NONE ,LONG EBX , LONG ECX , LONG EDX , LONG FS , LONG ES , LONG DS , LONG EIP ,LONG CS , LONG EFLAGS , LONG ESP , LONG SS)STRUCT TASK_STRUCT * P ;INT I ;STRUCT FILE * F ;P = (STRUCT TASK_STRUCT *) GET_FREE_PAG

13、E() ;IF(!P)RETURN -EAGAIN ;TASKNR = P ; *P = * CURRENT ; /并不是指针的赋值 ,而是直接申请了一个空间 /下面开始修改任务数据结构的值P-STATE = TASK_UNINTERRUPTIBLE ; P-PID = LAST_PID ;P-FATHER = CURRENT-PID ;P-COUNTER = P-PRIORITY ;/设置时间片P-SIGNAL = 0 ; /信号位图置 0P-ALARM = 0 ; /报警定时器值P-LEADER = 0 ;/进程的领导权P-UTIME = P-STIME = 0 ; /用户态号和核心态的运

14、行时间P-CUTIME = P-CSTIME = 0 ;/子进程用户态和和核心态的运行时间 P-START_TIME = JIFFIES ; /进程当前的运行时间P-TSS.BACK_LINK = 0 ;P-TSS.ESP0 = PAGE_SIZE + (LONG) P ; /任务内核态栈指针P-TSS.SS0 = 0X10 ; /内核态的段选择符,与数据段选择符相同P-TSS.EIP = EIP ;P-TSS.EFLAGS = EFLAGS ; P-TSS.EAX = 0 ; /这是当 FORK()调用返回时 新进程会返回 0 的原因P-TSS.ECX = ECX ;P-TSS.EDX =

15、EDX ;P-TSS.EBX = EBX ; P-TSS.ESP = ESP ;P-TSS.EBP = EBP ;P-TSS.ESI = ESI ;P-TSS.EDI = EDI ;P-TSS.ES = ES P-TSS.CS = CS P-TSS.SS = SS P-TSS.DS = DS P-TSS.FS = FS P-TSS.GS = GS P-TSS.LDT = _LDT(NR) ; P-TSS.TRACE_BITMAP = 0X80000000 ;IF(LAST_TASK_USED_MATH = CURRENT)_ASM_(“CLTS ; FNSAVE %0“ :“M“(P-TSS.

16、I387) ;/接下来复制进程页表,即在线性地址空间设置新任务代码段和数据段描述符中的基地址和限长,并复制页表。 IF(COPY_MEM(NR , P)TASKNR = NULL ;FREE_PAGE(LONG)P) ;RETURN -EAGAIN ;/如果父进程中有些文件是打开的,则将对应文件的打开次数加 1 ,因为子进程会共享父进程打开的这些文件FOR(I = 0 ; I FILPI)F-F_COUNT+ ;IF(CURRENT-PWD)CURRENT-PWD-I_COUNT+ ;IF(CURRENT-ROOT)CURRENT-ROOT-I_COUNT+ ;IF(CURRENT-EXECU

17、TABLE)CURRENT-EXECUTABLE-I_COUNT+ ;/最后在 GDT 表中设置新任务 TSS 段和 LDT 段描述符SET_TSS_DESC(GDT + (NRTSS) ;SET_LDT_DESC(GDT + (NRLDT) ;P-STATE = TASK_RUNNING ; RETURN LAST_PID ;/为新进程取得不重复的进程号 LAST_PID ,函数返回在任务数组中的任务号INT FIND_EMPTY_PROCESS(VOID)INT I ; REPEAT:IF(+LAST_PID) PID = LAST_PID)GOTO REPEAT ;FOR(I = 1 ;

18、 I #include #include #include #include #include #include #include /* Priority of a process goes from 0 to 139.The 0-99 priority ranges is allocated to * RT tasks,the 100-139 range is for SCHED_OTHER tasks.Priority values are inverted:* lower p-prio value means higher priority.*/#define MAX_RT_PRIO 1

19、00#define MAX_PRIO (MAX_RT_PRIO+40)/* Convert user-nice values -20019 to static priority100.139* (MAX_PRIO-1),and back.*/#define NICE_TO_PRIO(nice) (MAX_RT_PRIO+(nice)+20)#define PRIO_TO_NICE(prio) (prio)-MAX_RT_PRID-20)#define TASK_NICE(p) PRIO_TO_NICE(p)-static_prio)/* User priority is the nice va

20、lue converted to something we can work with better* when scaling various scheduler parameters ,its a 0.39 range.*/#define USER_PRIO(p) (p)-MAX_RT_PRIO)#define TASK_USER_PRIO(p) USER_PRIO(p)-static_prio)#define MAX_USER_PRIO (USER_PRIO(MAX_PRIO)/* These are the tuning knobs of the scheduler:* Minimum

21、 timeslice is 10 msecs,default timeslice is 150 msecs.* maximum timeslice is 300 msecs.Timeslices get refilled after they expire.*/#define MIN_TIMESLICE (10*HZ/1000)#define MAX_TIMESLICE (300*HZ/1000)#define CHILD_PENALTY 95#define PARENT_PENALTY 100#define EXIT_WEIGHT 3#define PRIO_BONUS_RATIO 25#d

22、efine INTERACTIVE_DELTA 2#define MAX_SLEEP_AVG (2*HZ)#define STARVATION_LIMIT (2*HZ)/* If a task is interactive then we reinsert it in the active array after it has* expired its current timeslice.(it will not continue to run immediately,it will* still roundrobin with other interactive tasks.)* This

23、part scales the interactivity limit depending on niceness.* We scale it linearly,offset by the INTERACTIVE_DELTA delta.* Here are a few examples of defferent nice levels:* TASK_INTERACTIVE(-20): 1,1,1,1,1,1,1,1,1,0,0* TASK_INTERACTIVE(-10): 1,1,1,1,1,1,1,0,0,0,0* TASK_INTERACTIVE(0): 1,1,1,1,0,0,0,0

24、,0,0,0* TASK_INTERACTIVE(10): 1,1,0,0,0,0,0,0,0,0,0* TASK_INTERACTIVE(19): 0,0,0,0,0,0,0,0,0,0,0* (the X axis represents the possible -50+5 dynamic priority range a task can * explore,a value of 1 means the task is rated interactive.)* Ie.nice+19 tasks can never get interactive enough to be reinsert

25、ed into the active * array.And only heavily CPU-hog nice -20 tasks will be expired.Default nice 0 * tasks are somewhere between it takes some effort for them to get interactive,* but its not too hard.*/#define SCALE(v1,v1_max,v2_max)(v1)*(v2_max)/(v1_max)#define DELTA(D) (SCALE(TASK_NICE(p),40,MAX_U

26、SER_PRIO*PRIO_BONUS_RATIO/100)+INTERACTIVE_DELTA)#define TASK_INTERACTIVE(p)(p)-priostatic_prio-DELTA(p)/* TASK_TIMESLICE scales user-nice values-2019 to time slice values.* The higher a processs priority,the bigger timeslices it gets during one round of * execution.But even the lowest priority proc

27、ess gets MIN_TIMESLICE worth of * execution time.*/#define TASK_TIMESLICE(p) (MIN_TIMESLICE+(MAX_TIMESLICE-MIN_TIMESLICE)*(MAX_PRIO-1-(p)-static_prio)/39)/*These are the runqueue data structures:*/#define BITMAP_SIZE (MAX_PRIO+1+7)/8)+sizeof(long)-1)/sizeof(long)typedef struct runqueue runqueue_t;st

28、ruct prio_array int nr_active;spinlock_t *lock;runqueue_t *rq;unsigned long bitmapBITMAP_SIZE;list_t queueMAX_PRIO;/* This is the main,per-CPU runqueue data structure.* Locking rule:those places that want to lock multiple runqueues(such as the load)* balancing or the process migration code),lock acq

29、uire operations must be ordered* by ascending unsigned long nr_running,nr_switches,expired_timestamp;task_t *curr,*idle;prio_array_t *active,*expired,arrays2;int prev_nr_runningNR_CPUS; _ _ _ _cacheline_aligned;static struct runqueue runqueuesNR_CPUS _ _cacheline_aligned;#define cpu_rq(cpu) (runqueu

30、es+(cpu)#define this_rq( ) cpu_rq(smp_processor_id( )#define task_rq(p) cpu_rq(p)-cpu)#define cpu_curr(cpu) (cpu_rq(cpu)-curr)#define rt_task(p) (p)-priolock,*flags);if (unlikely(_ _rq!=task_rq(p) spin_unlock_irqrestroe(goto repeat_lock_task;return _ _rq;static inline void unlock_task_rq(runqueue_t

31、*rq,unsigned long *flags) spin_unlock_irqrestore(/* Adding/removing a task to/from a priority array: */static inline void dequeue_task(struct task_struct *p,prio_array_t *array) array-nr_active- -;list_del_init(if (list_empty(array-queue+p-prio)_ _clear_bit(p-prio,array-bitmap);static inline void en

32、queue_task(struct task_struct *p,prio_array_t *array); list_add_tail(_ _set_bit(p-prio,array-bitmap);array-nr_active+;p-array=array;static inline int effective_prio(task_t *p) int bonus,prio;/* Here we scale the actual sleep average0MAX_SLEEP_AVG into the -50+5* bonus/penalty range.We use 25% of the

33、 full 039 priority range so that:* (1)nice +19 interactive tasks do not preempt nice 0 CPU hogs* (2)nice -20 CPU hogs do not get preempted by nice 0 tasks.* Both properties are improtant to certain workloads.*/bonus=MAX_USER_PRIO*PRIO_BONUS_RATIO*p-sleep_avg/MAX_SLEEP_AVG/100-MAX_USER_PRIO*PRIO_BONU

34、S_RATIO/100/2;prio=p-static_prio-bonus;if (prioMAX_PRIO-1) prio=MAX_PRIO-1;return prio;static inline void activate_task(task_t *p,runqueue_t *rq) unsigned long sleep_time=jiffies-p-sleep_timestamp;prio_array_t *array=rq-active;if (!rt_task(p) if (p-sleep_avgMAX_SLEEP_AVG) p-sleep_avg=MAX_SLEEP_AVG;p

35、-prio=effective_prio(p);enqueue_task(p,array);rq-nr_running+;static inline void deactivate_task(struct task_struct *p,runqueue_t *rq) rq-nr_running- -;dequeue_task(p,p-array);p-array=NULL;static inline void resched_task(task_t *p) int need_resched;need_resched=p-need_resched;wmb( );p-need_resched=1;

36、if (!need_resched #ifdef CONFIG_SMP/* Wait for a process to unschedule. This is used by the exit( ) and ptrace( ) code.*/void wait_task_inactive(task_t *p) unsigned long flags;runqueue_t *rq;repeat:rq=task_rq(p);while (unlikely(rq-curr= =p) cpu_relax( );barrier( );rq=lock_task_rq(p,if (unlikely(rq-c

37、urr= =p) unlock_task_rq(rq,goto repeat;unlock_task_rq(rq,/* The SMP message passing code calls this function whenever the new task has arrived * at the target CPU.We move the new task into the local runqueue.* This function must be called with interrupts disabled.*/void sched_task_migrated(task_t *n

38、ew_task) wait_task_inactive(new_task);new_task-cpu=smp_processor_id( );wake_up_process(new_task);/* Kick the remote CPU if the task is running currently,this code is used by the signal * code to signal tasks which are in user-mode as quickly as possible.* (Note that we do this lockless-if the task d

39、oes anything while the message is in * flight then it will notice the sigpending condition anyway.)*/void kick_if_running(task_t *p) if (p= =task_rq(p) -curr) resched_task(p);#endif/* Wake up a process.Put it on the run-queue if its not already there.The * “current“ process is always on the run-queu

40、e(except when the actual re-schedule* is in progress),and as such youre allowed to do the simpler “current-state=* TASK_RUNNING“ to mark yourself runnable without the overhead of this.*/static int try_to_wake_up(task_t *p,int synchronous) unsigned long flags;int success=0;runqueue_t *rq;rq=lock_task

41、_rq(p, p-state=TASK_RUNNING;if (!p-array) activate_task(p,rq);if (rq-curr= =rq-idle) | (p-priocurr-prio)resched_task(rq-curr);success=1;unlock_task_rq(rq,return success;int wake_up_process(tsk_t *p) return try_to_wake_up(p,0);int wake_up_forked_process(task_t *p) runqueue_t *rq=this_rq( );p-state=TA

42、SK_RUNNING;if (!rt_task(p) /* We decrease the sleep average of forking parents and children as well,* to keep max-interactive tasks from forking tasks that are max-interactive.*/current-sleep_avg=current-sleep_avg*PARENT_PENALTY/100;p-sleep_avg=p-sleep_avg*CHILD_PENALTY/100;p-prio=effective_prio(p);

43、spin_lock_irq(p-cpu=smp_processor_id( );activate_task(p,rq);spin_unlock_irq(/* Potentially available exiting-child timeslices are retrieved here-this way* the parent does not get penalized for creating too many processes. (this cannot* be used to generate timeslices artificially,because any timeslic

44、e recovered* here was given away by the parent in the first place.)*/void sched_exit(task_t *p) _ _cli( );current-time_slice+=p-time_slice;if (unlikely(current-time_sliceMAX_TIMESLICE) current-time_slice=MAX_TIMESLICE;_ _sti( );/* If the child was a (relative-) CPU hog when decrease the sleep_avg of

45、 the * parent as well.*/if (p-sleep_avgsleep_avg)current-sleep_avg=(current-sleep_avg*EXIT_WEIGHT+p-sleep_avg)/(EXIT_WEIGHT+1);#if CONFIG_SMPasmlinkage void schedule_tail(task_t *prev) spin_unlock_irq(#endifstatic inline void context_switch(task_t *prev,task_t *next) struct mm_struct *mm=next-mm;str

46、uct mm_struct *oldmm=prev-active_mm;prepare_to_switch( );if (unlikely(!mm) next-active_mm=oldmm;atomic_inc(enter_lazy_tlb(oldmm,next,smp_processor_id( );elseswitch_mm(oldmm,mm,next,smp_processor_id( );if (unlikely(!prev-mm) prev-active_mm=NULL;mmdrop(oldmm);/* Here we just switch the register state

47、and the stack.There are 3 processes affected* by a context switch: prev= = =(last=next).Its the much more prvious* prev that is on nexts stack,but prev is set to(the just run) last process* by switch_to( ).This might sound slightly confusing but makes tons of sense.*/switch_to(prev,next,prev);unsign

48、ed long nr_running(void) unsigned long i,sum=0;for(i=0;inr_running;ruturn sum;unsigned long nr_context_switches(void) unsigned long i,sum=0;for(i=0;inr_switches);return sum;#if CONFIG_SMP/* Lock the busiest runqueue as well,this _rq is locked already,recalculate nr_running* if we have to drop the runqueue lock.*/static inline unsigned int double_lock_balance(runqueue_t *this_rq,runqueue_t *busiest,int this_cpu,int idle,unsigned int nr_running) if (unlikely(!spin_trylock(spin_lock(spin_lock(/* Need to recalculate nr_running */if (i

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报