C语言 waitpid()如何获取多个子进程?

lf5gs5x2  于 2022-12-03  发布在  其他
关注(0)|答案(1)|浏览(158)

在CSAPP书chap.8的这个例子中:

\#include "csapp.h"

/* WARNING: This code is buggy! \*/

void handler1(int sig)
{
int olderrno = errno;

    if ((waitpid(-1, NULL, 0)) < 0)
        sio_error("waitpid error");
    Sio_puts("Handler reaped child\n");
    Sleep(1);
    errno = olderrno;

}

int main()
{
int i, n;
char buf[MAXBUF];

    if (signal(SIGCHLD, handler1) == SIG_ERR)
        unix_error("signal error");
    
    /* Parent creates children */
    for (i = 0; i < 3; i++) {
        if (Fork() == 0) {
            printf("Hello from child %d\n", (int)getpid());
            exit(0);
        }
    }
    
    /* Parent waits for terminal input and then processes it */
    if ((n = read(STDIN_FILENO, buf, sizeof(buf))) < 0)
        unix_error("read");
    
    printf("Parent processing input\n");
    while (1)
        ;
    
    exit(0);

}

它会产生下列输出:

......
Hello from child 14073
Hello from child 14074
Hello from child 14075
Handler reaped child
Handler reaped child //more than one child reaped
......

用于waitpid()的if块用于生成waitpid()无法获取所有子级的错误。**虽然我理解waitpid()将被放入while()循环中以确保获取所有子级,但我不理解的是****为什么只进行一个waitpid()调用,但却能够获取多个子项(请注意,在输出中,处理程序获取了多个子项)?**根据以下答案:Why does waitpid in a signal handler need to loop?waitpid()只能获取一个子进程。
谢谢你!

**update:**这是不相关的,但是处理程序以下面的方式进行了更正(也摘自CSAPP书):

void handler2(int sig) 
{
    int olderrno = errno;

    while (waitpid(-1, NULL, 0) > 0) {
        Sio_puts("Handler reaped child\n");
    }
    if (errno != ECHILD)
        Sio_error("waitpid error");
    Sleep(1);
    errno = olderrno;
}

在我的linux电脑上运行这段代码。

7lrncoxx

7lrncoxx1#

每次接收到指定给它的信号(本例中为SIGCHLD)时,您指定的信号处理程序就会运行。虽然每次接收信号时waitpid只执行一次,但处理程序仍然会多次执行它,因为每次子进程终止时都会调用它。
Child n终止(SIGCHLD),处理程序立即开始执行,并使用waitpid“收割”刚刚退出的child。
子节点n+1终止,它的行为与子节点n相同。对于存在的每个子节点,都将如此。
没有必要循环它,因为它只在需要时才被调用。
编辑:正如下面所指出的,本书后来用预期的循环来纠正它的原因是,如果多个子进程同时发送它们的终止信号,处理程序可能最终只得到其中一个。
signal(7)
标准信号不排队。如果在阻塞某个标准信号时生成了该信号的多个示例,则只有一个信号示例被标记为待定(并且该信号在解除阻塞时只传送一次)。
循环waitpid可以确保获得所有退出的子进程,而不是像现在这样只获得其中一个。

为什么循环可以解决多信号问题?

想象一下:你现在正在处理一个你收到的SIGCHLD信号,在你处理这个信号的同时,你收到了更多的来自其他子进程的信号,这些信号在这段时间里已经终止了。这些信号不能排队。通过不断地循环waitpid,你可以确保即使处理程序本身不能处理发送的多个信号,waitpid仍然会在不断运行时拾取它们,而不是仅在处理程序激活时才运行,处理程序是否能按预期工作取决于信号是否已合并。
一旦没有更多的子对象要获取,waitpid仍然会正确退出。需要了解的是,循环的作用只是捕获当您已经在信号处理程序中时发送的信号,而不是在正常代码执行期间发送的信号,因为在这种情况下,信号处理程序会正常处理它。
如果你仍然有疑问,试着阅读这两个答案。

第一种方法使用WNOHANG等标志,但如果没有准备好获取的子进程,这只能使waitpid立即返回而不是等待。

相关问题