在此示例中,取自 CSAPP 书籍第 8 章:\#include \'csapp.h\'/* 警告:此代码有缺陷!\*/void handler1(int sig){int olderrno = errno; if ((waitpid(-1, NULL, 0)) < ...
这是来自 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 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
......
使用的 if 块 waitpid()
用于生成 waitpid()
无法收获所有子进程的错误。 While I understand that waitpid()
is to be put in a while()
loop to ensure reaping all children, what I don't understand is that why only one waitpid()
call is made, yet was able to reap more than one children(Note in the output more than one child is reaped by handler)? 根据这个答案: 为什么信号处理程序中的 waitpid 需要循环? waitpid()
只能收获一个子进程。
谢谢!
更新: 这无关紧要,但处理程序通过以下方式进行更正(也取自 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 计算机上运行此代码。
您指定的信号处理程序每次 SIGCHLD
收到您分配给它的信号(在本例中)时都会运行。虽然 waitpid
每次收到信号时只执行一次,但处理程序仍会执行多次,因为每次子进程终止时都会调用它。
子进程 n 终止( SIGCHLD
),处理程序立即采取行动并用来 waitpid
“收割”刚刚退出的子进程。
子进程 n+1 终止,其行为与子进程 n 相同。每个子进程都会重复此过程。
没有必要循环它,因为它仅在需要时才会被调用。
编辑:正如下面指出的那样,书中后来用预期的循环来纠正它的原因是,如果多个子级同时发送终止信号,处理程序最终可能只会得到其中一个。
信号(7) :
标准信号不排队。如果在信号被阻塞时生成了多个标准信号实例,则只有一个信号实例被标记为待处理(并且该信号在解除阻塞时只会被传送一次)。
循环 waitpid
确保收获所有退出的子进程,而不是像现在这样只收获其中一个。
为什么循环可以解决多信号的问题?
想象一下:您当前位于处理程序内部,处理 SIGCHLD
收到的信号,在执行此操作的同时,您会收到来自在此期间已终止的其他子进程的更多信号。这些信号无法排队。通过不断循环 waitpid
,您可以确保即使处理程序本身无法处理发送的多个信号, waitpid
仍会在不断运行时拾取它们,而不是仅在处理程序激活时运行,这取决于信号是否已合并。
waitpid
一旦没有更多子节点需要收割,循环仍然会正确退出。重要的是要理解,循环只是为了捕获当您已经处于信号处理程序中时发送的信号,而不是在正常代码执行期间发送的信号,因为在这种情况下,信号处理程序将照常处理它。
如果您仍有疑问,请尝试阅读这两个问题的答案。
第一个使用诸如的标志 WNOHANG
,但 waitpid
如果没有子进程准备好被收割,这只会立即返回而不是等待。