分类: 系统运维
2012-03-29 13:20:46
当一个进程终止时,不管是正常还是异常地,内核通过向父进程发送SIGCHLD来通知父进程。因为子进程的终止是一个异步事件--它可以在父进程运行时的 任何时间发生--所以这个信号是从内核发送给父进程的异步通知。父进程可以选择忽略这个信号,或它提供一个当信号发生时被调用的函数:一个信号处理器。这 个信号的默认行为是被忽略。我们在第10章讲述这些选项。至于现在,我们需要知道一个调用wait或waitpid的进程会:
1、阻塞,如果它所有的子进程都还在运行;
2、如果一个子进程终止并等待它的终止状态被获取,则随着子进程的终止状态立即返回;
3、如果它没有任何子进程,则立即返回一个错误。
如果进程是因为收到SIGCHLD信号而正调用wait,那么我们预期wait会立即返回。但是如果我们在任何随机的时间点时调用它,那么它会阻塞。
这两个函数的区别如下:
1、wait函数可以阻塞调用者,直到一个子进程终止,而waitpid有选项可以避免它阻塞;
2、waitpid函数不等待最先终止的子进程;它有许多选项来控制进程等待哪个进程。
如果一个子进程已经终止并成为一个僵尸,那么wait会用子进程的状态立即返回。否则,它会阻塞调用者,直到子进程终止。如果调用者阻塞并有多个子进程,那么wait当某个进程终止时返回。我们总是可以知道哪个子进程终止了,因为这个函数返回这个进程ID。
对于两个函数,参数statloc是一个整型指针。如果参数不是一个空指针的话,那么终止的进程的终止状态由这个参数指向的地址存储。如果我们不关心终止状态,我们可以简单地传为一个空指针作为参数。
传统地,这两个函数返回的整型状态由实现来定义。(对于正常返回)其中的某些位来指明退出状态,(对于一个异常返回)另一些位指明信号号,一个位来指明是
否有核心文件产生,等等。POSIX.1规定了这个终止状态要用各种定义在
我们将在9.8节讨论工作控制时讨论一个进程如何可以被停止。
下面的代码展示了各种终止状态:
FreeBSD 5.2.1、Linux 2.4.22、Mac OS X 10.3,和Solaris 9都支持WCOREDUMP宏。
下面是程序运行的结果:
normal termination, exit status = 7
abnormal termination, signal number = 6
abnormal termination, signal number = 8
不幸的是,没有一个可移植的方法来把从WTERMISG得到的信号号映射到可描述的名字。(10.21节有一种方法。)我们并须查看
正如我们已经提过的,如果我们有不只一个子进程,wait在任何一个子进程终止时返回。如果我们想等一个指定进程终止是会发生什么呢(假定我们知道我们要
等待的进程ID)?在UNIX系统的早期版本,我们必须调用wait并把返回的进程ID和我们感兴趣的那个进行比较。如果终止的进程不是我们想要的,那我
们必须保存进程ID和终止状态,然后再次调用wait。我们需要持续这样做,直到需要的进程被终止。下一次我们想等一个特定的进程时,我们需要遍历已经终
止的进程的列表来看我们是否已经等待过它,如果没有,则再次调用wait。我们需要的是一个等待特定进程的函数。这种功能(和更多功能)由POISX.1
的waitpid函数提供。
waitpid的pid参数的解释取决于它的值:
pid = -1:等待任何一个子进程。这种用法等同于wait。
pid >0:等待进程ID为pid的子进程。
pid == 0:等待任何进程组ID和调用进程相同的子进程(我们在9.4节讨论进程组ID)
pid < -1:等待任何进程组ID等于pid的绝对值的子进程。
waitpid函数返回终止的子进程的ID,并把子进程的终止状存储到statloc指向的内存地址里。对于wait,唯一的真实的错误是调用进程没有子 进程。(其它错误返回也是可能的,万一函数调用被一个信号中断。我们将在第十章讨论这个。)然而对于waitpid,还有可能当指定的进程或进程组不存 在,可不是调用进程的子进程时会得到错误。
options参数让我们更多地控制waitpid的操作。这个参数是0或者由下表里的常量的与或值组成:
常量 | 描述 |
WCONTINUED | 如果实现支持工作控制,那么返回任何由pid指定的、在停止后又继续的、但其状态还没有被报告的子进程的状态。(POISX.1的XSI扩展。) |
WNOHANG | 如果pid指定的子进程不是立即可用的,waitpid函数不会阻塞。这种情况下,返回值为0. |
WUNTRACED | 如果实现支持工作控制,那么返回任何由pid指定的、其状态在停止后还未被报告的子进程的状态。WIFSTOPPED宏决定了返回值是否对应于一个停止的子进程。 |
Solaris支持一个补充的,但不是标准的选项常量,WNOWAIT,它让系统保存这个进程,它的终止状态由waitpid返回,以便它可以被再次等待。
waitpid函数提供了三个不被wait函数提供的特性:
1、waitpid函数让我们等待一个特定的进程,而wait函数返回任何终止的子进程的状态。我们将在讨论popen函数里再回到这个特性。
2、waitpid函数提供了一个wait的非阻塞版本。有时我们想得到子进程的状态,而不想阻塞。
3、waitpid函数用WUNTRACED和WCONTINUED选项提供了对工作控制的支持。
下面的代码避免僵尸程序:
我们在第二个子进程里调用sleep,保证第一个子进程在打印父进程ID之前退出。在fork之后,父进程或子进程可以继续执行,我们不会知道两个会先继
续执行。如果我们不让第二个子进程睡眠,而且它在fork后比它的父进程更早继续执行,那么它打印的父进程ID就会是它的父进程,而不是进程ID 1。
运行结果:
$ ./a.out
$ second child, parent pid = 1
注意shell当原始进程终止时打印它的命令提示符,它发生在第二个子进程打印它父进程ID之前。