在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
立即返回,而不是等待,如果没有准备收获的子进程。