为什么要使用 non-blocking waitpid 而不是阻塞等待?

Why to use non-blocking waitpid instead of blocking wait?

我正在阅读 https://www.amazon.com/Unix-Network-Programming-Sockets-Networking/dp/0131411551,作者在那里处理调用 waitpid 而不是 wait 的处理程序中的 sigchld

In Figure 5.7, we cannot call wait in a loop, because there is no way to prevent wait from blocking if there are running children that have not yet terminated.

处理程序如下:

void sig_chld(int signo)
{
    pid_t pid;
    int stat;

    // while ((pid = wait(&stat)) > 0)
    while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
    {
        printf("child %d terminated\n", pid);
    }
}

问题是,即使我使用 wait 的阻塞版本(如注释掉的那样),child 无论如何都会终止(这是我想要的,以便没有僵尸), 那么为什么还要考虑它是 blocking 方式还是 non-blocking?

我假设它是 non-blocking 方式(即使用 waitpid),那么我可以多次调用处理程序吗? (当some childs被终止,而other仍然是运行)。但我仍然可以在该处理程序中阻塞并等待 all child 终止。因此,多次调用处理程序或仅调用一次处理程序之间没有区别。还是有其他原因 non-blocking 并多次调用处理程序?

while 循环条件将 运行 比需要等待的僵尸进程 child 多一次。因此,如果您使用 wait() 而不是 waitpid()WNOHANG 标志,如果您还有另一个 运行ning child - 作为 wait() 只有 returns 早,如果根本没有 child 进程,则会出现 ECHLD 错误。一个健壮的通用处理程序将使用 waitpid() 来避免这种情况。

想象这样一种情况,其中 parent 进程启动多个 children 来做各种事情,并定期向他们发送有关做什么的指令。当第一个退出时,在 SIGCHLD 处理程序的循环中使用 wait() 将导致它永远阻塞,而其他 child 进程则等待更多它们永远不会的指令收到。

或者说,一个 inetd 服务器侦听网络连接并派生一个新进程来处理每个连接。有些服务可以很快完成,有些可以 运行 数小时或数天。如果它使用信号处理程序来捕获退出的 children,那么在 long-lived 退出之前,如果您在循环中使用 wait() 一旦该处理程序是由 short-lived 服务进程退出触发。

我花了一秒钟才明白问题出在哪里,所以让我把它说清楚。正如其他人所指出的,wait(2) 可能会阻塞。如果指定了 WNOHANG 选项,waitpid(2) 将不会阻塞。

你说 first 调用 wait(2) 不应该阻塞是正确的,因为只有在 child 退出时才会调用信号处理程序.但是,当调用信号处理程序时,可能有几个 children 可能已经退出。信号是异步传递的,并且可以合并。因此,如果两个 children 几乎同时退出,则操作系统可能只向 parent 进程发送一个信号。因此,必须使用循环来迭代检查是否有多个 child 退出。

现在我们已经确定循环对于检查多个 children 是否已经退出是必要的,很明显我们不能使用 wait(2) 因为它会在第二个阻塞如果有 child 还没有退出,则进行迭代。

TL;DR 循环是必要的,因此使用 waitpid(2) 是必要的。