C语言 waitpid()如何收获多个子节点?



在CSAPP书第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 childn");
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 %dn", (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 inputn");
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()调用,但能够收获多个子节点(注意在输出中多个子节点被处理程序收获)?根据这个答案:为什么waitpid在一个信号处理程序需要循环?waitpid()只能收获一个孩子。

谢谢!

更新:这是无关的,但处理程序以以下方式纠正(也取自CSAPP书籍):

void handler2(int sig) 
{
int olderrno = errno;
while (waitpid(-1, NULL, 0) > 0) {
Sio_puts("Handler reaped childn");
}
if (errno != ECHILD)
Sio_error("waitpid error");
Sleep(1);
errno = olderrno;
}

在我的linux计算机上运行此代码。

每次接收到分配给它的信号(本例中为SIGCHLD)时,您指定的信号处理程序都会运行。虽然waitpid在每次接收信号时只执行一次,但处理程序仍然会执行多次,因为每次子进程终止时都会调用它。

子n终止(SIGCHLD),处理程序启动并使用waitpid来"捕获";刚刚退出的孩子。

子n+1终止,其行为与子n相同。每个子都是如此。

不需要循环,因为它只在第一次需要时被调用。

编辑:正如下面指出的,为什么书后来用预期的循环纠正它的原因是,如果多个子线程同时发送它们的终止信号,处理程序可能最终只得到其中一个。

信号(7):

标准信号不排队。如果有多个实例当标准信号被阻塞时,就会产生标准信号只有一个信号实例被标记为挂起(和当信号被解除阻塞时,将只发送一次)。

循环waitpid确保获取所有退出的子节点,而不是像现在这样只获取其中一个。

为什么循环解决多个信号的问题?

想象一下:你目前在处理程序中,处理你收到的SIGCHLD信号,当你这样做的时候,你从其他孩子那里收到了更多的信号,这些孩子在此期间已经终止了。这些信号不能排队。通过不断循环waitpid,您可以确保即使处理程序本身无法处理正在发送的多个信号,waitpid仍然会在不断运行时拾取它们,而不是仅在处理程序激活时运行,这取决于信号是否已合并,能否按预期工作。

waitpid仍然正确退出,一旦没有更多的孩子收割。重要的是要理解,循环只是在那里捕获当你已经在信号处理程序中时发送的信号,而不是在正常的代码执行期间,因为在这种情况下,信号处理程序将像正常一样处理它。

如果你仍然有疑问,试着阅读这两个对你问题的回答。

  • 如何确保' waitpid(-1, &stat, WNOHANG) '收集所有子进程
  • 为什么waitpid在一个信号处理程序需要循环?(前两段)

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

最新更新