Linux waitpid函数分析
函数的作用
waitpid()函数实际上用于等待子进程的状态变化,并为父进程提供子进程的终止状态信息。
当一个子进程终止时,操作系统会向父进程发送 SIGCHLD 信号。在处理这个信号的过程中,
父进程通常会使用 waitpid()函数来获取子进程的状态信息,以便执行相应的操作(如回收资源)。
waitpid() 函数可以等待子进程的以下几种状态变化:
- 子进程正常终止:子进程执行完毕或调用 exit() 函数。
- 子进程被信号终止:子进程因接收到一个信号(如 SIGKILL)而被终止。
- 子进程被暂停:当使用 WUNTRACED 选项时,waitpid() 函数还可以返回因接收到 SIGSTOP 而暂停的子进程的状态。
- 子进程恢复执行:当使用 WCONTINUED 选项时,waitpid() 函数还可以返回因接收到 SIGCONT 而恢复执行的子进程的状态。
waitpid() 函数的功能不仅限于等待 SIGCHLD信号。它实际上为父进程提供了一种在子进程状态变化时收集信息和执行操作的机制。
这有助于避免僵尸进程的产生,并允许父进程对子进程的状态变化做出响应。
参数说明
pid:指定要等待的子进程的进程 ID。有以下几种取值:
- pid > 0:等待指定 PID 的子进程。
- pid == -1:等待任何子进程。此时,waitpid() 的行为类似于 wait() 函数。
- pid == 0:等待同一进程组中的任何子进程。
- pid < -1:等待指定进程组 ID 的绝对值的任何子进程。
status:一个指向整数的指针,用于存储子进程的终止状态。
您可以使用一些宏(如 WIFEXITED、WEXITSTATUS 等)来分析这个状态值。
options:提供额外的选项来控制waitpid()函数的行为。主要有以下几种选项:
- WNOHANG:使 waitpid() 函数在没有子进程终止时立即返回,而不会阻塞。
- WUNTRACED:除了已终止的子进程外,还返回因接收到 SIGSTOP 而停止的子进程的状态。
- WCONTINUED:返回因接收到 SIGCONT 而继续运行的子进程的状态。
返回值
如果成功,waitpid() 函数返回一个子进程的进程 ID。
如果使用了 WNOHANG 选项且没有子进程已经终止,waitpid() 函数返回 0。
如果发生错误,waitpid() 函数返回 -1,并设置 errno。
示例
在信号处理函数中使用 waitpid() 函数和 WNOHANG 选项的一个典型场景是
处理子进程终止时发送的 SIGCHLD信号。
通过在信号处理函数中使用循环,您可以确保在一次信号处理函数调用中处理所有已终止的子进程。
这种方法在多个 SIGCHLD 信号被合并的情况下尤其有效,能避免僵尸进程的产生。
WNOHANG 参数的作用
如果两个子进程几乎同时终止,它们各自发送的 SIGCHLD 信号可能会被合并为一个信号,导致信号处理函数只被调用一次。
然而,通过在信号处理函数中使用 waitpid() 函数并设置 WNOHANG 选项,仍然可以处理这种情况。
特殊场景
WNOHANG 选项本身并不能保证处理所有子进程。
while((mypid=waitpid(-1, NULL, WNOHANG)) > 0) { // 业务处理 }
信号处理函数如上实现仍出现僵尸进程原因如下
Unix/Linux 的信号机制中,相同信号不会排队
(如果短时间内有 100 个子进程退出,内核只会向父进程发送 1 次 SIGCHLD 信号,而不是 100 次)
上述处理函数能清理信号触发时已退出的子进程,但如果处理函数执行过程中,又有新的子进程退出,
这些新退出的子进程不会触发新的 SIGCHLD(因为信号还在处理中,内核不会重复发送),直到处理函数执行完
而大规模子进程退出时,处理函数执行和子进程退出几乎同时发生,
必然有部分子进程退出在信号处理窗口外成为僵尸进程。
浙公网安备 33010602011771号