一、前言

首先在了解waitpid()函数之前,我们需要先明确以下几个概念。

1.进程状态

        从程序员的角度,我们可以认为进程总是处于下面三种状态之一:

(1)运行。进程要么在CPU上执行,要么在等待被执行且最终会被内核调度。

(2)停止。进程的执行被挂起(suspended),且不会被调度。当收到SIGSTOP、SIGTSTP、SIGTTIN或者SIGTTOU信号时,进程就停止,并且保持停止直到他收到一个SIGCONT信号,在这个时刻,进程再次开始运行。

(3)终止。进程永远的停止了。进程会因为三种原因终止:第一、收到一个信号,信号的默认行为是终止进程。第二、从主程序返回。第三、调用exit函数

2.回收子进程

        当一个进程由于某种原因终止时,内核并不是立即把它从系统中清除 。相反,进程被保持在一种已终止的状态中,直到被它的父进程回收(reaped)。 当父进程回收已终止的子进程时,内核将子进程的退出状态传递给父进程,然后抛弃已终止的进程,从此时开始,该进程就不存在了 。一个终止了但还未被回收的进程称为僵死进程 (zombie)。如果一个进程在回收他的子进程之前就终止了,那他的子进程被称为孤儿进程。

        如果一个父进程终止了,内核会安排 init 进程成为它的孤儿进程的养父。init进程的 PID 为 1, 是在系统启动时由内核创建的 ,它不会终止 ,是所有进程的祖先 。如果父进程没有回收它的僵死子进程就终止了,那么内核会安排 init 进程去回收它们 。不过,长时间运行的程序,比如 shell 或者服务器,总是应该回收它们的僵死子进程 。即使僵死子进程没有运行,它们仍然消耗系统的内存资源。
        一个进程可以通过调用 waitpid 函数来等待它的子进程终止或者停止。

二、waitpid()函数详解        

pid_t waitpid (pid_t pid, int* statusp, int options);
返回 :如果成功,则为子进程的PID,如果options为WNOHANG,则返回0,如果发生其他错误,则返回-1。

        waitpid函数有点复杂 。默认情况下 (当 options=0 时 ),waitpid挂起调用进程的执行,直到它的等待集合 (wait set) 中的一个子进程终止。如果等待集合中的一个进程在刚调用的时刻就已经 终止了,那么 waitpid 就立即返回 。在这两种情况中,waitpid返回导致 waitpid 返回的已终止子进程的PID此时,已终止的子进程已经被回收,内核会从系统中删除掉它的所有痕迹。
1. 判定等待集合的成员
        等待集合的成员是由参数 pid 来确定的:

(1)如果Pid>0,那么等待集合就是一个单独的子进程 ,它的进程 ID 等于 pid。
(2)如果Pid=-1,那么等待集合就是由父进程所有的子进程组成的。
2. 修改默认行为
        可以通过将 options 设置为常量 WNOHANG、WUNTRACED 和 WCONTINUED
的各种组合来修改默认行为:
WNOHANG: 如果等待集合中的任何子进程都还没有终止,那么就立即返回(返回值为0)。 默认的行为是挂起调用进程,直到有子进程终止 。在等待子进程终止的同时,如果还想做些有用的工作,这个选项会有用。
WUNTRACED: 挂起调用进程的执行,直到等待集合中的一个进程变成已终止或者被停止 。返回的PID 为导致返回的已终止或被停止子进程的 PID。默认的行为是只返回已终止的子进程。当你想要检査已终止和被停止的子进程时,这个选项会有用。
WCONTINUED: 挂起调用进程的执行,直到等待集合中一个正在运行的进程终止或等待集合中一 个被停止的进程收到 SIGCONT 信号重新开始执行。
        可以用或运算把这些选项组合起来 。例如:WNOHANG | WUNTRACED: 立即返回,如果等待集合中的子进程都没有被停止或终止,则返回值为0: 如果有一个停止或终止,则返回值为该子进程的 PID。
3. 检查已回收子进程的退出状态
        如果statusp参数是非空的,那么waitpid就会在 status 中放上关于导致返回的子进程的状态信息, status是statusp指向的值。 wait.h头文件定义了解释status参数的几个宏:
WIFEXITED(status):如果子进程通过调用 exit 或者一个返回 (return) 正常终止,就返回真。
WEXITSTATUS(status):返回一个正常终止的子进程的退出状态。只有在WIFEXITED()返回为真时,才会定义这个状态。
WIFSIGNALED(status):如果子进程是因为一个未被捕获的信号终止的,那么就返回真。
WTERMSIG(status):返回导致子进程终止的信号的编号 。只有在 WIFSIGNALED()返回为真时,才定义这个状态。
WIFSTOPPED(status):如果引起返回的子进程当前是停止的,那么就返回真。
WSTOPSIG(status):返回引起子进程停止的信号的编号。只有在 WIFSTOPPED()返回为真时,才定义这个状态。
WIFCONTINUED(status):如果子进程收到SIGCONT信号重新启动,则返回真。
4. 错误条件
        如果调用进程没有子进程,那么waitpid返回-1, 并且设置 errno为ECHILD。 如果waitpid函数被一个信号中断,那么它返回-1,并设置errno为EINTR。

        注意,程序不会按照特定的顺序回收子进程。子进程回收的顺序是这台特定的计算机系统的属性 。在另一个系统上,甚至在同一个系统上再执行一次,两个子进程都可能以相反的顺序被回收。这是非确定性行为的一个示例,这种非确定性行为使得对并发进行推理非常困难。两种可能的 结果都同样是正确的,作为一个程序员,我们绝不可以假设总是会出现某一个结果,无论多么不可能出现另一个结果 。唯一正确的假设是每一个可能的结果都同样可能出现。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐