linux waitpid函数详解
函数的作用
waitpid()
函数实际上用于等待子进程的状态变化,并为父进程提供子进程的终止状态信息。当一个子进程终止时,操作系统会向父进程发送 SIGCHLD 信号。在处理这个信号的过程中,父进程通常会使用 waitpid()
函数来获取子进程的状态信息,以便执行相应的操作(如回收资源)。
waitpid()
函数可以等待子进程的以下几种状态变化:
- 子进程正常终止:子进程执行完毕或调用
exit()
函数。 - 子进程被信号终止:子进程因接收到一个信号(如 SIGKILL)而被终止。
- 子进程被暂停:当使用
WUNTRACED
选项时,waitpid()
函数还可以返回因接收到 SIGSTOP 而暂停的子进程的状态。 - 子进程恢复执行:当使用
WCONTINUED
选项时,waitpid()
函数还可以返回因接收到 SIGCONT 而恢复执行的子进程的状态。
waitpid()
函数的功能不仅限于等待 SIGCHLD 信号。它实际上为父进程提供了一种在子进程状态变化时收集信息和执行操作的机制。这有助于避免僵尸进程的产生,并允许父进程对子进程的状态变化做出响应。
函数原型
waitpid()
函数是一个用于等待子进程状态变化的系统调用。它可以让父进程在子进程结束时收到通知,从而避免僵尸进程的产生。waitpid()
函数的原型如下:
#include <sys/types.h> #include <sys/wait.h> pid_t waitpid(pid_t pid, int *status, int options);
参数说明:
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
选项本身并不能保证处理所有子进程。然而,在信号处理函数(如 catch_child()
)中使用一个循环可以确保处理所有已终止的子进程。
当您在信号处理函数中使用如下循环时:
pid_t wpid; int status; while ((wpid = waitpid(-1, &status, WNOHANG)) > 0) { if (WIFEXITED(status)) { printf("-----------catch child id=%d, status=%d\n", wpid, WEXITSTATUS(status)); } }
waitpid()
函数会立即返回,而不会等待子进程终止。循环会检查是否有子进程已经终止,如果有,waitpid()
函数将返回已终止子进程的 PID。当没有更多子进程终止时,waitpid()
会返回 0,并退出循环。
这个循环会在有子进程终止时执行。只要 waitpid()
函数返回的值大于 0,意味着有子进程已经终止,循环将继续执行。当没有更多子进程终止时,waitpid()
会返回 0,并退出循环。
虽然 WNOHANG
选项并不知道有多少个子进程,但这个循环确保了处理所有已终止的子进程。只要有子进程终止,循环将继续执行,直到处理所有子进程。这种方法能够确保在一次信号处理函数调用中处理所有已终止的子进程,避免僵尸进程的产生。
这样一来,即使多个 SIGCHLD 信号被合并,您仍然可以通过这个循环找出所有已终止的子进程,并执行相应的操作。这种方法能够确保在一次信号处理函数调用中处理所有已终止的子进程,避免僵尸进程的产生。
SIGCHID信号
SIGCHLD 信号是一个由操作系统发送给父进程的信号,用于通知父进程子进程状态的变化。当子进程终止、被暂停或恢复时,操作系统会向父进程发送 SIGCHLD 信号。这使得父进程能够得知子进程的状态变化并采取适当的措施,如回收资源,避免僵尸进程的产生。
以下是 SIGCHLD 信号的一些主要用途:
- 子进程终止:当子进程正常终止(例如执行完成或调用
exit()
函数)或异常终止(例如收到一个致命信号)时,操作系统会向父进程发送 SIGCHLD 信号。在这种情况下,父进程可以通过调用wait()
或waitpid()
函数来回收子进程的资源,以及获取子进程的终止状态。 - 子进程被暂停:当子进程因收到 SIGSTOP 信号而被暂停时,操作系统会向父进程发送 SIGCHLD 信号。父进程可以通过调用
waitpid()
函数并使用WUNTRACED
选项来检测子进程的暂停状态。 - 子进程恢复:当子进程因收到 SIGCONT 信号而恢复执行时,操作系统会向父进程发送 SIGCHLD 信号。父进程可以通过调用
waitpid()
函数并使用WCONTINUED
选项来检测子进程的恢复状态。
默认情况下,当进程收到 SIGCHLD 信号时,信号会被忽略。但是,您可以为 SIGCHLD 信号定义一个信号处理函数来处理子进程的状态变化。这样,在子进程终止或状态发生变化时,信号处理函数会被自动调用,您可以在其中执行相应的操作。
总之,SIGCHLD 信号是一种通知机制,用于告知父进程其子进程状态的变化。这使得父进程能够采取适当的措施,如回收资源和避免僵尸进程的产生。