1、第15章 进程控制,Linux系统是多任务操作系统,可同时进行多个程序完成多项工作。进程是处于活动状态的程序,在操作系统的管理下,所有进程共享计算机中的硬件资源。进程作为系统运行时的基本逻辑成员,不仅作为独立个体运行在系统上,而且还将相互竞争系统资源。了解进程的本质对于理解、描述和设计系统软件有着极为重要的意义,了解进程的活动状态也有利于设计复杂的程序。,15.1 进程的基本概念,在讨论进程的基本概念之前,我们首先介绍两种查看Linux系统中进程信息的方法。,15.1.1 进程状态和状态转换,进程在生存周期中呈现出各种状态及状态的转换,这些信息反映了进程的获取系统资源的情况。Linux系统的进
2、程状态模型见表15.1所示。,15.1.1 进程状态和状态转换,1子进程被Linux内核调入CPU执行的过程,15.1.1 进程状态和状态转换,2子进程进入睡眠状态,15.1.1 进程状态和状态转换,3子进程结束,15.1.2 进程控制,在Linux系统中,用户创建子进程的惟一方法就是使用fork系统调用。fork系统调用的流程如图15.5所示。,15.1.3 进程调度,Linux系统进程调度包括两个概念,分别是调度时机和调度算法。调度时机指进程何时被调度上CPU执行。例如,转变为睡眠状态的进程将获得较高的优先级,一但所需要的资源被释放,该进程可以立即被调度上CPU执行。被抢占的进程也将获得一
3、个较高的优先级,抢占其CPU时钟周期的进程一旦转为用户状态,被抢占的进程立即转为内核状态。调度算法所关心的内容就是如何为进程分配优先级。,15.2 进程基本操作,本节将通过介绍关于进程操作的系统调用函数来讲解进程的基本操作方法,其中包括fork调用、exec调用、exit调用、wait调用和sleep调用,相关函数被定义在系统调用库“unistd.h”中。通过本节,我们将了解如何产生子进程,进程如何改变它的执行映像,父子进程的同步等操作。由此也了解到一些并行程序的基本概念与如何设计简单的并行程序。,15.2.1 fork系统调用,fork系统调用有两个函数,分别是fork()函数和vfork(
4、)函数。fork系统调用可创建一个子进程,该调用的一般形式是: pid_t fork(void); pid_t vfork(void);,15.2.2 exec系统调用,系统调用exec以新进程替代原有进程,但是PID保持不变。因此可以认为,exec系统调用实际上没有创建新进程,只是替换了原有进程上下文的内容。,15.2.3 exit系统调用,系统调用exit的功能是终止发出调用的进程,它包含两个函数,分别是_exit()函数和exit函数。它们的一般形式如下: void _exit(int status); void exit(int status); 系统调用_exit()立即终止发出调用
5、的进程。所有属于该进程的文件描述符都关闭。如果该进程拥有子进程,那么父子进程关系被转到init进程上。被结束的进程将收到来自子进程的僵死信号SIGCHLD。如果被结束的进程在控制台或终端上运行,shell程序将收到SIGHUP信号。,15.2.4 wait系统调用,系统调用wait用于父进程与子进程同步。父进程调用后,将进入睡眠状态,直到子进程结束或者父进程在被其他进程终止。使用wait系统调用需要包含头文件“sys/types.h”和“sys/wait.h”。,15.2.5 sleep函数调用,系统调用sleep用来使进程主动进入睡眠状态,该函数的一般形式是: sleep(秒数); 执行该系
6、统调用后,进程将进入睡眠状态,直到指定的秒数已到。正常情况下,该调用的返回值为0,若是因为被信号所唤醒,则返回值为原始秒数减去已睡眠秒数的差。,15.3 进程的特殊操作,上一节介绍了有关进程的一些基本操作,如进程的产生、进程的终止、进程执行映像的改变、等待子进程终止等。本节要介绍一些有关进程的特殊操作。有了这些操作,就使得进程的编程更加完善,能编制更为实用的程序。主要的内容有得到关于进程的各种ID、对进程的设置用户ID、改变进程的工作目录、根交换和改变进程的优先级等操作。,15. 3.1 获得进程ID,获得运行进程的GID可使用getgid()函数,获得运行进程的EGID可使用getegid(
7、)函数。标识GID与EGID的不同是由于执行文件设置set-gid位引起的。 注意:GID和PGID的区别是,一般执行该进程的用户的组ID就是该进程的GID,如果该执行文件设置了set_gid位,则文件所群组ID就是该进程的GID。一个进程在shell下执行,shell程序就将该进程的PID作为该进程组PGID,从该进程派生的子进程都拥有父进程所属进程组PGID,除非父进程将子进程的PGID设置成与该子进程的PID一样。,15.3.2 setuid和setgid系统调用,设置进程的UID可使用setuid()函数,设置进程的GID可使用setgid()函数。 setuid()函数可修改发出调用
8、进程的UID,参数uid为创建进程的用户信息。如果以普通用户的UID作为参数执行该调用,Linux内核将直接设置进程UID为参数uid信息。如果以根用户的UID作为参数,为了保障系统的安全性,Linux内核将以进程表和u区中用户真实的标识号来设置进程UID。setuid()函数执行成功时,返回值为0,否则返回-1。 setgid()函数可修改发出调用进程的GID,与前者不同,该调用不会检验用户的真实身份。参数gid为进程的新GID信息。执行成功时,返回值为0,否则返回-1。,15.3.3 setpgrp和setpgid系统调用,系统调用setpgrp()和setpgid()都是用来设置进程PG
9、ID,它们的一般形式为: int setpgrp(void); int setpgid(pid_t pid, pid_t pgid); 其中,setpgrp()函数直接将进程的PGID设为与PID相同的数值,setpgid()以其中参数修改PGID。参数pid为指定进程的PID,值为0时修改发出调用进程的PGID。参数pgid为指定的PGID信息,值为0时,修改所有PID与参数pid相等的进程,将这些进程的PGID值设为参数pgid的值。若以普通用户权限发出此调用,而PGID原本为根用户组所有,那么只有在指定进程与调用进程的EUID相同时,或者指定进程为调用进程的子进程时才有效。,15.3.4
10、 chdir系统调用,在文件操作部分曾介绍过chdir()系统调用,该调用对于进程控制有不同的意义。chdir()函数将进程的当前工作目录改为由参数指定的目录。该调用的一般形式如下: int chdir(const char *path); 参数path为指定目录的路径,发出该调用的进程必须具备该目录的执行权限。调用成功时返回值为0,否则返回-1,并设置相应的错误代码。,15.3.5 chroot系统调用,系统调用chroot又被称为根交换操作,作用通常是在一个Linux系统上虚拟另一个Linux系统,根交换后,所有的命令操作都被重新定向。该调用的一般形式如下: int chroot(cons
11、t char *path); 参数path为新的根目录路径,执行后,进程将以该目录作为根目录,并且使进程不能访问该目录以外的内容。该操作不改变当前工作目录,如果当前工作目录在指定目录以外,则无法访问其中内容。根交换操作只能由根用户发出,调用成功时返回值为0,错误时返回-1,并设置相应的错误代码。,15.3.6 nice系统调用,系统调用nice()用来改变进程的优先级。该调用的一般形式如下: int nice(int inc); 参数inc为调用nice()函数的进程优先级数值的增量。优先级数值越低的值,被调度上CPU运行的机会越大;优先级数值越高,被调度上CPU运行的机会越低。但是,只有根用户能为inc参数设置负值,使进程优先级提高,普通用户设置的正值会降低优先级。调用成功时,返回值为0,否则返回-1。,15.4 小结,本章介绍了进程的基本概念和基本操作方法,讲解了与进程操作有关的Linux系统调用函数。进程操作涉及到许多操作系统和Linux内核方面的知识,读者不能理解时,请查阅操作系统相关的理论书籍。进程控制对于发布大型软件非常有用,将程序实现不同功能的代码分开编译为可执行文件,实现分而治之的思想。用一个主控制程序调用其他的程序,每个可执行文件的大小都有限,不用一次将所有程序调入内存。通过网络对软件升级时,每次也只用从网络传输被改动的可执行文件,保证了升级过程的平稳过渡。,