Linux原理及应用.ppt
LINUX原理及应用原理及应用武汉大学计算机学院武汉大学计算机学院郑鹏郑鹏第第2章章Linux进程管理进程管理n程序是为了完成某种任务而设计的软件,是存储在程序是为了完成某种任务而设计的软件,是存储在磁盘上包含可执行的机器指令和数据的静态实体。磁盘上包含可执行的机器指令和数据的静态实体。进程是一个程序的一次执行的过程,进程是一个程序的一次执行的过程,在操作系统中在操作系统中执行特定的任务,是一个随执行过程不断变化的实执行特定的任务,是一个随执行过程不断变化的实体。进程是体。进程是Linux系统中基本的调度单位。系统中基本的调度单位。n进程具有独立的权限与职责,如果系统中某个进程进程具有独立的权限与职责,如果系统中某个进程崩溃,它不会影响到其余的进程。每个进程运行在崩溃,它不会影响到其余的进程。每个进程运行在其各自的虚拟地址空间中,通过内核控制下的通信其各自的虚拟地址空间中,通过内核控制下的通信机制,它们之间才能发生联系。机制,它们之间才能发生联系。n进程在生命期内将使用系统中的资源。它利用系统进程在生命期内将使用系统中的资源。它利用系统中的中的CPU来执行指令,用物理内存来放置指令和数来执行指令,用物理内存来放置指令和数据。使用文件系统提供的功能打开并使用文件,同据。使用文件系统提供的功能打开并使用文件,同时直接或者间接地使用物理设备。时直接或者间接地使用物理设备。2.1Linux进程进程n在在Linux系统中,进程被称为任务。系统中,进程被称为任务。n进程存在于系统的内存之中,是操作系统可进程存在于系统的内存之中,是操作系统可感知、可控制的动态实体。感知、可控制的动态实体。Linux的进程在处的进程在处理机上运行时,处理机提供了两种不同的执理机上运行时,处理机提供了两种不同的执行状态行状态:n内核态内核态(kernelmode)n用户态用户态(usermode)2.1Linux进程进程n内核态内核态又称系统态,它具有较高的特权,能执又称系统态,它具有较高的特权,能执行所有的机器指令,能访问所有的寄存器和存行所有的机器指令,能访问所有的寄存器和存储区域,能直接控制所有的系统资源。储区域,能直接控制所有的系统资源。Linux在执行内核程序时是处于在执行内核程序时是处于内核态内核态下。下。n用户态是进程的普通执行状态,在用户态下进用户态是进程的普通执行状态,在用户态下进程具有较低的特权,只能执行规定的机器指令,程具有较低的特权,只能执行规定的机器指令,不能执行特权指令。进程在用户态下只能访问不能执行特权指令。进程在用户态下只能访问进程的存储空间。在用户态下进程不能与系统进程的存储空间。在用户态下进程不能与系统硬件相互作用,不能访问系统资源。硬件相互作用,不能访问系统资源。n在在I386结构中,结构中,内核态的特权级为内核态的特权级为0,用户态,用户态的特权级为的特权级为3。2.1Linux进程进程n存放在磁盘上的可执行文件的代码和数据的集合称为存放在磁盘上的可执行文件的代码和数据的集合称为可执行映象可执行映象(ExecutableImage)。当它被装入系统中。当它被装入系统中运行时,它就形成了一个进程。运行时,它就形成了一个进程。Linux进程是由三部进程是由三部分组成:分组成:n(1)正文段(正文段(text):存放程序代码的数据,假如机器中有):存放程序代码的数据,假如机器中有数个进程运行相同的一个程序,那么它们就可以使用相同数个进程运行相同的一个程序,那么它们就可以使用相同的正文段,正文段具有只读的属性。的正文段,正文段具有只读的属性。n(2)用户数据段(用户数据段(usersegment):是进程在运行过程中处):是进程在运行过程中处理数据的集合,它们是进程直接进行操作的所有数据(包理数据的集合,它们是进程直接进行操作的所有数据(包括全部变量在内),以及进程使用的进程堆栈。括全部变量在内),以及进程使用的进程堆栈。n(3)系统数据段(系统数据段(systemsegment):存放着进程的控制信):存放着进程的控制信息,即进程控制块(息,即进程控制块(PCB),它存放了程序的运行环境。),它存放了程序的运行环境。Linux中进程控制块中进程控制块PCB是名字为是名字为task_struct的数据结构,的数据结构,它称为任务结构体。任务结构体是进程存在的唯一标志。它称为任务结构体。任务结构体是进程存在的唯一标志。2.1Linux进程进程n系统为每个进程分配一个独立的虚拟地址空间。系统为每个进程分配一个独立的虚拟地址空间。进程的虚拟地址空间被分做两个部分:进程的虚拟地址空间被分做两个部分:n用户空间。用户进程本身的程序和数据(可执行映用户空间。用户进程本身的程序和数据(可执行映象)映射到用户空间中。象)映射到用户空间中。n系统空间。内核被映射到所有进程的系统空间中。系统空间。内核被映射到所有进程的系统空间中。它们只允许在具有较高特权的内核态下访问。进程它们只允许在具有较高特权的内核态下访问。进程运行在特权较低的用户态下时,不允许它直接访问运行在特权较低的用户态下时,不允许它直接访问系统空间。进程只能通过系统调用系统空间。进程只能通过系统调用(systemcall)转换转换为内核态后,才能访问系统空间。一个进程在运行为内核态后,才能访问系统空间。一个进程在运行过程中,总是在两种执行状态之间不断地转换。过程中,总是在两种执行状态之间不断地转换。2.1Linux进程进程n进程上下文:系统提供给进程的处于动态变化进程上下文:系统提供给进程的处于动态变化的运行环境总和。的运行环境总和。n系统中的每一个进程都有它自己的上下文。系统中的每一个进程都有它自己的上下文。nLinux操作系统包括三种不同类型的进程,每操作系统包括三种不同类型的进程,每种进程都有自己的特点和属性。种进程都有自己的特点和属性。n(1)交互进程交互进程由一个由一个Shell启动的进程。交互进启动的进程。交互进程既可以在前台运行,也可以在后台运行。程既可以在前台运行,也可以在后台运行。n(2)批处理进程批处理进程这种进程和终端没有联系,是这种进程和终端没有联系,是一个进程序列。一个进程序列。n(3)守护进程守护进程Linux系统启动时启动的进程,系统启动时启动的进程,并在后台运行。并在后台运行。2.2描述进程的数据结构描述进程的数据结构nLinux的进程控制块用任务结构体的进程控制块用任务结构体task_struct描述。描述。Linux在内核空间专门开辟一个指针数在内核空间专门开辟一个指针数组组task,该数组的每一个元素是一个指向任务,该数组的每一个元素是一个指向任务结构体的指针,所以结构体的指针,所以task数组又称为数组又称为task向量。向量。将所有进程控制块将所有进程控制块task_struct的指针存储在的指针存储在task数组中,以便有效地管理。数组中,以便有效地管理。task数组大小数组大小限制了系统并发执行的进程总数,而物理内存限制了系统并发执行的进程总数,而物理内存的大小决定了系统中的最大进程数。在版本中,的大小决定了系统中的最大进程数。在版本中,每个每个task_struct结构占结构占1680字节。字节。2.2描述进程的数据结构描述进程的数据结构ntask_struct容纳了一个进程的所有信息,我们容纳了一个进程的所有信息,我们主要对如下几个方面的信息进行介绍。主要对如下几个方面的信息进行介绍。n(1)进程的状态和标志信息进程的状态和标志信息n(2)进程的调度信息进程的调度信息n(3)进程的标识信息进程的标识信息n(4)进程间通信信息进程间通信信息n(5)进程的家族关系进程的家族关系n(6)时间和定时信息时间和定时信息n(7)文件系统信息文件系统信息n(8)存储管理信息存储管理信息n(9)CPU现场保留信息现场保留信息进程的状态和标志信息进程的状态和标志信息进程的状态和标志信息进程的状态和标志信息n1state项项ntask_structtask_struct中的中的statestate项表示进程当前的状态。项表示进程当前的状态。nLinuxLinux系统的不同版本,其进程也略有不同。系统的不同版本,其进程也略有不同。进程的状态和标志信息进程的状态和标志信息nLinux系统版本系统版本)进程状态表进程状态表进程状态值说明TASK_RUNNING0运行态TASK_INTERRUPTIBLE1等待态,可中断TASK_UNINTERRUPTIBLE 2等待态,不可中断TASK_ZOMBIE4僵死态TASK_STOPPED8暂停态TASK_SWAPPING16交换态(2.4.x版本无)TASK_EXCLUSIVE32独占态进程的状态和标志信息进程的状态和标志信息nLinux系统系统(2.6版本版本)进程状态表进程状态表进程状态值说明TASK_RUNNING0运行态TASK_INTERRUPTIBLE1等待态,可中断TASK_UNINTERRUPTIBLE 2等待态,不可中断TASK_STOPPED4暂停态TASK_TRACED8跟踪态TASK_ZOMBIE16僵死态TASK_DEAD32已经退出且不需要父进程来回收的进程进程的状态和标志信息进程的状态和标志信息nTASK_RUNNING:进程当前正在运行,或正在运行队列中等待调度。nTASK_INTERRUPTIBLE:进程处于睡眠状态,正在等待某些事件发生。进程可以被信号中断。接收到信号或被显式的唤醒之后,进程将转换为TASK_RUNNING状态。nTASK_UNINTERRUPTIBLE:此进程状态类似于TASK_INTERRUPTIBLE,只是它不会处理信号。中断处于这种状态的进程是不合适的,因为它可能正在完成某些重要的任务。当它所等待的事件发生时,进程将被显式地唤醒。nTASK_STOPPED:进程已中止执行,它没有运行,并且不能运行。接收到SIGSTOP和SIGTSTP等信号时,进程将进入这种状态。接收到SIGCONT信号之后,进程将再次变得可运行。nTASK_TRACED:正被调试程序等其他进程监控时,进程将进入这种状态。nEXIT_ZOMBIE:进程已终止,它正等待其父进程收集关于它的一些统计信息。nEXIT_DEAD:最终状态(正如其名)。将进程从系统中删除时,它将进入此状态,因为其父进程已经通过wait4()或waitpid()调用收集了所有统计信息。进程的状态和标志信息进程的状态和标志信息n进程状态转换进程状态转换进程的状态和标志信息进程的状态和标志信息n2flags项项ntask_struct中的中的flags项表示进程的标志。项表示进程的标志。进程标志值含义PF_ALIGNWARN正在打印对齐警告信息PF_STARTING正在创建进程PF_EXITING进程正在退出PF_FORKNOEXEC进程刚创建,但还没执行PF_SUPERPRIV使用超级用户特权PF_DUMPCOREdumped corePF_SIGNALED进程被信号(Signal)终止PF_MEMALLOC正在分配内存PF_VFORK对于用vfork创建的进程,退出前正在唤醒父进程PF_USEDFPU该进程使用FPU(SMP only)进程的调度信息进程的调度信息n进程的类别、调度策略、优先级等调度属性反映了进进程的类别、调度策略、优先级等调度属性反映了进程的调度信息。程的调度信息。task_struct中的中的policy、priority、rt_priority、counter、nice等等项与进程调度有关。项与进程调度有关。npolicy表示表示进程的进程调度策略,可以通过系统调用进程的进程调度策略,可以通过系统调用sys_sched_setscheduler()更改更改(kernel/sched.c)。Linux操作系统采用的调度策略操作系统采用的调度策略见下表见下表。调度策略值说明SCHED_OTHER0非实时进程,基于优先权的轮转法SCHED_FIFO 1实时进程,先进先出算法SCHED_RR2实时进程,基于优先权的轮转法进程的调度信息进程的调度信息nprioritypriority表示进程优先级,其值给出了进程每次获取表示进程优先级,其值给出了进程每次获取CPUCPU后,可使用的时间(按后,可使用的时间(按jiffiesjiffies计)。计)。nrt_priorityrt_priority给出实时进程的优先级,给出实时进程的优先级,rt_priority+1000rt_priority+1000给出进程每次获取给出进程每次获取CPUCPU后,可使用的时间后,可使用的时间(同样按同样按jiffiesjiffies计计)。n在轮转法在轮转法(roundrobin)调度时表示进程当前还可运调度时表示进程当前还可运行多久。在进程开始运行时被赋为行多久。在进程开始运行时被赋为priority的值,以的值,以后每隔一个后每隔一个tick(时钟中断)递减(时钟中断)递减1,减到,减到0时引起新时引起新一轮调度。重新调度将从一轮调度。重新调度将从run-queue队列选出队列选出counter值最大的就绪进程获得值最大的就绪进程获得CPU,因此,因此counter起到了进起到了进程的动态优先级的作用(程的动态优先级的作用(priority则是静态优先级)则是静态优先级)。counter表示进程当前还拥有的时间片,表示进程当前还拥有的时间片,nice表示普表示普通进程的动态优先级,可对优先权进行动态调整。通进程的动态优先级,可对优先权进行动态调整。进程的标识信息进程的标识信息 ntask_struct中:中:npid、ppid等等项描述了进程的标识信息。项描述了进程的标识信息。pid是进程是进程标识号标识号,ppid是其父进程是其父进程标识号标识号。nuid和和gid:表示运行进程的用户标识号和组标识号。表示运行进程的用户标识号和组标识号。neuid和和egid:表示运行进程的有效用户标识号和有表示运行进程的有效用户标识号和有效组标识号。效组标识号。nfsuid和和fsgid:表示运行进程的文件系统用户标识表示运行进程的文件系统用户标识号和文件系统组标识号。号和文件系统组标识号。nsuid和和sgid:表示运行进程的备份用户标识号和备表示运行进程的备份用户标识号和备份组标识号。份组标识号。进程的通信信息进程的通信信息nLinux支持经典的支持经典的UnixIPC机制,如信号、管道以及机制,如信号、管道以及系统系统V中中IPC机制,包括共享内存、信号灯和消息队机制,包括共享内存、信号灯和消息队列。列。task_struct结构中存储了与进程通信有关的信息。结构中存储了与进程通信有关的信息。与进程通信有关的项有与进程通信有关的项有sigpending,signal,blocked,*sig,exit_signal,semundo,*semsleeping等。等。nsigpending本身也是一个结构体,包含关于本进程中本身也是一个结构体,包含关于本进程中未决信号的信息。未决信号的信息。signal域记录进程接收到的信号类域记录进程接收到的信号类型,在型,在I386体系结构中共体系结构中共32位。位。blocked表示阻塞信号表示阻塞信号的掩码,的掩码,*sig是指向信号处理函数表的指针。是指向信号处理函数表的指针。exit_signal表示进程终止的信号。表示进程终止的信号。semundo表示进程表示进程要释放的信号量,要释放的信号量,*semsleeping指向与信号量操作相指向与信号量操作相关的等待队列。关的等待队列。进程的家族关系进程的家族关系nLinux系统中所有进程都是相互联系的。系统中所有进程都是相互联系的。n*p_opptr项是指向祖先进程任务结构体的指针;项是指向祖先进程任务结构体的指针;n*p_pptr项是指向父进程任务结构体的指针;项是指向父进程任务结构体的指针;n*p_cptr项是指向子进程任务结构体的指针;项是指向子进程任务结构体的指针;n*p_ysptr项是指向弟进程任务结构体的指针;项是指向弟进程任务结构体的指针;n*p_osptr项是指向兄进程任务结构体的指针。项是指向兄进程任务结构体的指针。进程的家族关系进程的家族关系nLinux的所有进程还组成一个双向链表。的所有进程还组成一个双向链表。n*next_task项指向下一进程任务结构体的指针;项指向下一进程任务结构体的指针;n*prev_task项是指向上一进程任务结构体的指针。项是指向上一进程任务结构体的指针。链表的头和尾都是链表的头和尾都是init_task(即(即0号进程)。号进程)。nLinux还把所有处于可运行状态的进程通过还把所有处于可运行状态的进程通过两个指针两个指针*next_run和和*prev_run连接形成双连接形成双向循环队列向循环队列RUN_QUEUE。时间和定时信息时间和定时信息n进程是动态的,在进程是动态的,在task_struct结构中还有表示结构中还有表示时间的数据项。时间的数据项。nstart_time项表示进程创建的时间;项表示进程创建的时间;nutime项表示进程在用户态下耗费的时间;项表示进程在用户态下耗费的时间;nstime项表示进程在内核态下耗费的时间;项表示进程在内核态下耗费的时间;ncutime项表示所有子进程在用户态下耗费的时间;项表示所有子进程在用户态下耗费的时间;ncstime项表示所有子进程在内核态下耗费的时间;项表示所有子进程在内核态下耗费的时间;ntimeout项表示进程申请延时。项表示进程申请延时。文件系统信息文件系统信息ntask_struct结构保存了进程与文件系统相关的结构保存了进程与文件系统相关的信息。进程可以自由地打开或关闭文件。信息。进程可以自由地打开或关闭文件。n*fs指针指向进程的可执行映象所在的文件系统;指针指向进程的可执行映象所在的文件系统;n*files指针指向进程打开的文件。指针指向进程打开的文件。n下图表明系统中的每个进程有下图表明系统中的每个进程有2个数据结构描个数据结构描述文件系统相关的信息。述文件系统相关的信息。文件系统信息文件系统信息存储管理信息存储管理信息n进进程程是是和和内内存存联联系系在在一一起起的的,task_structtask_struct结结构中有如下几个与内存相关的数据项:构中有如下几个与内存相关的数据项:n*mm进程的虚存信息;进程的虚存信息;n*ldt进程的局部描述符表指针;进程的局部描述符表指针;nsaved_kernel_stack内核态下堆栈的指针;内核态下堆栈的指针;nkernel_stack_page内核态下堆栈的页表指针;内核态下堆栈的页表指针;存储管理信息存储管理信息n下图显示了一个简单进程的虚存的布局以及下图显示了一个简单进程的虚存的布局以及管理它的内核数据结构。管理它的内核数据结构。2.2.9CPU现场保留信息现场保留信息n进程运行时,它将使用处理器的寄存器以进程运行时,它将使用处理器的寄存器以及堆栈等等。进程被挂起时,进程的上下及堆栈等等。进程被挂起时,进程的上下文文所有的所有的CPU相关的状态必须保存在相关的状态必须保存在它的它的task_struct结构中。当调度器重新调度结构中。当调度器重新调度该进程时,所有上下文被重新设定。该进程时,所有上下文被重新设定。CPU现场保留信息包括现场保留信息包括CPU寄存器、堆栈等环寄存器、堆栈等环境。境。2.2.10task_struct的作用的作用ntask_struct是进程存在的唯一标志,是进程存在的唯一标志,用来描用来描述系统中的进程或任务。述系统中的进程或任务。n在在Linux系统中,用系统中,用NR_TASKS定义定义task数数组的大小,组的大小,NR_TASKS的的缺省值一般为缺省值一般为512。创建新进程时,创建新进程时,Linux将从系统内存中分配将从系统内存中分配一个一个task_struct结构并将其加入结构并将其加入task数组。数组。当前运行进程的结构用当前运行进程的结构用current指针来指示。指针来指示。2.3Linux的进程控制的进程控制n进进程程控控制制就就是是研研究究如如何何建建立立、撤撤消消、阻阻塞塞或或唤唤醒醒一一个个进进程程,从从而而使使进进程程状状态态发发生生变变化化。在在传传统统的的Unix环环境境下下,有有两两个个基基本本的的操操作作用用于创建和修改进程:于创建和修改进程:n函函数数fork()用用来来创创建建一一个个新新的的进进程程,该该进进程程几几乎乎是当前进程的一个完全拷贝;是当前进程的一个完全拷贝;n函函数数族族exec()用用来来启启动动另另外外的的进进程程以以取取代代当当前前运运行的进程。行的进程。nLinux的的进进程程控控制制和和传传统统的的Unix进进程程控控制制基基本一致,只在一些细节的地方有些区别。本一致,只在一些细节的地方有些区别。创建进程创建进程n系统启动时总是处于内核模式,此时只有一个进程:系统启动时总是处于内核模式,此时只有一个进程:初始化进程。在系统初始化的最后,初始化进程启初始化进程。在系统初始化的最后,初始化进程启动一个称为动一个称为init内核线程(或进程),然后保留在内核线程(或进程),然后保留在idle状态。如果没有任何事要做,调度管理器将运行状态。如果没有任何事要做,调度管理器将运行idle进程。进程。idle进程是唯一不是动态分配进程是唯一不是动态分配task_struct的的进程,它的进程,它的task_struct在内核构造时静态定义,叫在内核构造时静态定义,叫init_task,其标识号为,其标识号为0。ninit内核线程是系统中第一个真正有用的进程,其标内核线程是系统中第一个真正有用的进程,其标识号为识号为1。它负责完成系统的一些初始化设置任务,。它负责完成系统的一些初始化设置任务,以及执行系统初始化程序。以及执行系统初始化程序。init程序使用程序使用/etc/inittab作为脚本文件来创建系统中的新进程。作为脚本文件来创建系统中的新进程。这些新进程又创建各自的新进程。这些新进程又创建各自的新进程。创建进程创建进程nLinux系统中,进程是进程映像的执行过程,系统中,进程是进程映像的执行过程,也就是正在执行的进程实体。它由三部分组成:也就是正在执行的进程实体。它由三部分组成:n(1)用户级上、下文。主要成分是用户程序;)用户级上、下文。主要成分是用户程序;n(2)寄存器上、下文。由)寄存器上、下文。由CPU中的一些寄存器的中的一些寄存器的内容组成,如内容组成,如PC,PSW,SP及通用寄存器等;及通用寄存器等;n(3)系统级上、下文。包括)系统级上、下文。包括OS为管理进程所用的为管理进程所用的信息,有静态和动态之分。信息,有静态和动态之分。创建进程创建进程n可用可用fork()系统调用来创建一个新进程。系统调用来创建一个新进程。n系统调用格式:系统调用格式:pid=fork()nfork()返回值意义如下:返回值意义如下:n0:在子进程中,表示当前进程是子进程。:在子进程中,表示当前进程是子进程。n0:在父进程中,返回值为子进程的:在父进程中,返回值为子进程的id值(唯一标识号)。值(唯一标识号)。n-1:创建失败。:创建失败。n如果如果fork()调用成功,它向父进程返回子进程的调用成功,它向父进程返回子进程的pid,并向子进程返回并向子进程返回0,即,即fork()被调用了一次,但返回了被调用了一次,但返回了两次。此时两次。此时OS在内存中建立一个新进程,所建的新进在内存中建立一个新进程,所建的新进程是调用程是调用fork()父进程的副本,称为子进程。子进程继父进程的副本,称为子进程。子进程继承了父进程的许多特性,并具有与父进程完全相同的承了父进程的许多特性,并具有与父进程完全相同的用户级上下文。父进程与子进程并发执行。用户级上下文。父进程与子进程并发执行。创建进程创建进程n内核为内核为fork()fork()完成以下操作:完成以下操作:n(1 1)为新进程分配一进程表项和进程标识号)为新进程分配一进程表项和进程标识号n(2 2)检查同时运行的进程数目)检查同时运行的进程数目n(3 3)拷贝进程表项中的数据)拷贝进程表项中的数据n(4 4)子进程继承父进程的所有文件,对父进程当前目录)子进程继承父进程的所有文件,对父进程当前目录和所有已打开的文件表项中的引用计数加和所有已打开的文件表项中的引用计数加1 1。n(5 5)为子进程创建进程上、下文)为子进程创建进程上、下文n(6 6)子进程执行)子进程执行n虽然父进程与子进程程序完全相同,但每个进程都虽然父进程与子进程程序完全相同,但每个进程都有自己的程序计数器有自己的程序计数器PC(注意子进程的注意子进程的PC开始位置开始位置),然后根据,然后根据pid变量保存的变量保存的fork()返回值的不同,执行返回值的不同,执行了不同的分支语句。了不同的分支语句。创建进程创建进程n一个一个具体具体使用使用fork创建进程的实例。创建进程的实例。#includeintforkvar=0;intmain()intpid;pid=fork();/系统调用,创建进程系统调用,创建进程if(pid0)/创建不成功,出错创建不成功,出错printf(“Forkfailed.”);exit(1);/系统调用系统调用elseif(pid=0)/子进程执行子进程执行printf(“Imthechildprocess!n”);forkvar=1;else/父进程执行父进程执行wait();/系统调用,等待子进程完成系统调用,等待子进程完成printf(“Imtheparentprocess!n”);forkvar+;printf(“parent,forkvar=%d”,forkvar);exit(0);创建进程创建进程#includemain()inti;if(fork()=0)/*子进程程序子进程程序*/for(i=1;i10;i+)printf(“BBBn”);else/*父进程程序父进程程序*/for(i=1;i);printf();fgets(command,256,stdin);fgets(command,256,stdin);commandstrlen(command)-1=0;commandstrlen(command)-1=0;if(fork()=0)/*if(fork()=0)/*子进程执行此命令子进程执行此命令*/*/execlp(command,command);execlp(command,command);/*/*如果如果execexec函数返回,表明没有正常执行命令,打印错误信息函数返回,表明没有正常执行命令,打印错误信息*/*/perror(command);perror(command);exit(errorno);exit(errorno);else/*else/*父进程,父进程,等待子进程结束,并打印子进程的返回值等待子进程结束,并打印子进程的返回值*/*/wait(&rtn);wait(&rtn);printf(child process return%dn,.rtn);printf(child process return%dn,.rtn);/*/*此程序从终端读入命令并执行之,完成后,父进程继续等待从终端读入命令。此程序从终端读入命令并执行之,完成后,父进程继续等待从终端读入命令。*/*/2.3.3等待进程等待进程n父进程可用系统调用wait()等待它的一个子进程的结束,wait()的参数指定了父进程等待的子进程。nwait的函数原型是:n#include/*提供类型pid_t的定义*/n#includenpid_twait(int*status)n进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞,直到有一个出现为止。n如果参数status的值不是NULL,wait就会把子进程退出时的状态取出并存入其中,这是一个整数值。2.3.3等待进程等待进程nwaitpid系统调用在系统调用在Linux函数库中的原型是:函数库中的原型是:n#include/*提供类型提供类型pid_t的定义的定义*/n#includenpid_twaitpid(pid_tpid,int*status,intoptions)n从从本本质质上上讲讲,系系统统调调用用waitpid和和wait的的作作用用是是完完全全相相同同的的,但但waitpid多多出出了了两两个个可可由由用用户户控控制制的的参参数数pid和和options,从从而而为为编编程程提提供供了了另另一一种种更更灵灵活活的的方方式式。waitpid等等待待指指定定的的子子进进程直到子进程返回。程直到子进程返回。npid0,等待指定的进程,等待指定的进程(pid);npid=0,等待任何一个组,等待任何一个组ID和调用者的组和调用者的组ID相同的进程;相同的进程;npid=-1时等同于时等同于wait调用;调用;npid-1时等待任何一个组时等待任何一个组ID等于等于pid绝对值的进程。绝对值的进程。nstatus和和wait的的意意义义一一样样。options可可以以决决定定父父进进程程的的状状态态,可可以取两个值。以取两个值。nWNOHANG:当没有子进程存在时父进程立即返回。:当没有子进程存在时父进程立即返回。nWUNTRACED:当当子子进进程程结结束束时时waitpid返返回回,但但是是子子进进程程的的退退出出状状态不可得到。态不可得到。2.3.3等待进程等待进程n下面是一个利用下面是一个利用waitpid函数的实例。函数的实例。#include#include#include#include#include#include#include#include#include#include int main(void)int main(void)pid_t childpid;pid_t childpid;int status;int status;childpid=fork();childpid=fork();if(-1=childpid)if(-1=childpid)perror(fork();perror(fork();exit(EXIT_FAILURE);exit(EXIT_FAILURE);else if(0=childpid)else if(0=childpid)puts(In child process);puts(In child process);sleep(3);/sleep(3);/让子进程睡眠让子进程睡眠3 3秒秒 printf(tchild pid=%dn,getpid();printf(tchild pid=%dn,getpid();printf(tchild ppid=%dn,getppid();printf(tchild ppid=%dn,getppid();exit(EXIT_SUCCESS);exit(EXIT_SUCCESS);else else waitpid(childpid,&status,0);waitpid(childpid,&status,0);puts(In parent process);puts(In parent process);printf(tparent pid=%dn,getpid();printf(tparent pid=%dn,getpid();printf(tparent ppid=%dn,getppid();printf(tparent ppid=%dn,getppid();printf(tchild process exited with status%d n,status);printf(tchild process exited with status%d n,status);exit(EXIT_SUCCESS);exit(EXIT_SUCCESS);2.3.3等待进程等待进程n编译后运行,结果如下:In child processIn child processchild pid=4469child pid=4469child ppid=4468child ppid=4468In parent processIn parent processparent pid=4468parent pid=4468parent ppid=4379parent ppid=4379child process exited with status 0child process exited with status 0如如果果将将上上面面“waitpid(waitpid(childpid,childpid,&status,&status,0 0););”行行注注释释掉掉,程程序序执执行效果如下:行效果如下:In child processIn child processIn parent processIn parent processparent pid=4481parent pid=4481parent ppid=4379parent ppid=4379child process exited with status 1331234400child process exited with status 1331234400child pid=4482child pid=4482child ppid=1child ppid=1从运行结果中可以看出,子进程还没有退出,父进程已经退出了。从运行结果中可以看出,子进程还没有退出,父进程已经退出了。2.3.4终止进程终止进程n当需要一个进程结束或进程希望终止自己时,当需要一个进程结束或进程希望终止自己时,可通过系统调用可通过系统调用exit()来实现。在版内核中,来实现。在版内核中,exit是第是第1号调用,其在号调用,其在Linux函数库中的原函数库中的原型是:型是:n#includenvoidexit(intstatus);n无论在程序中的什么位置,只要执行到无论在程序中的什么位置,只要执行到exit系统调用,进程就会停止剩下的所有操作,系统调用,进程就会停止剩下的所有操作,清除包括清除包括PCB在内的各种数据结构,并终止在内的各种数据结构,并终止本进程的运行。本进程的运行。2.3.4终止进程终止进程n请看下面的程序:请看下面的程序:#includemain()printf(Thisistheexampleofexit!n);exit(0);printf(neverbeRUN!n);编译后运行:编译后运行:$gccexit_example.c-oexit_example$./exit_exampleThisistheexampleofexit!可以看到,程序并没有打印后面的可以看到,程序并没有打印后面的neverbeRUN,因为在此之前,在,因为在此之前,在执行到执行到exit(0)时,进程就已经终止了。时,进程就已经终止了。2.4Linux进程调度进程调度n在在Linux中,进程不能被抢占。只要能够运中,进程不能被抢占。只要能够运行它们就不能被停止。当进程必须等待某行它们就不能被停止。当进程必须等待某个系统事件时,它才决定释放出个系统事件时,它才决定释放出CPU。n进程常因为执行系统调用而需要等待。由进程常因为执行系统调用而需要等待。由于处于等待状态的进程还可能占用于处于等待状态的进程还可能占用CPU时时间,所以间,所以Linux采用了预加载调度策略。在采用了预加载调度策略。在此策略中,每个进程只允许运行很短的时此策略中,每个进程只允许运行很短的时间:间:200毫秒,当这个时间用完之后,系统毫秒,当这个时间用完之后,系统将选择另一个进程来运行,原来的进程必将选择另一个进程