在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电脑上运行这段代码。
1条答案
按热度按时间7lrncoxx1#
每次接收到指定给它的信号(本例中为
SIGCHLD
)时,您指定的信号处理程序就会运行。虽然每次接收信号时waitpid
只执行一次,但处理程序仍然会多次执行它,因为每次子进程终止时都会调用它。Child n终止(
SIGCHLD
),处理程序立即开始执行,并使用waitpid
“收割”刚刚退出的child。子节点n+1终止,它的行为与子节点n相同。对于存在的每个子节点,都将如此。
没有必要循环它,因为它只在需要时才被调用。
编辑:正如下面所指出的,本书后来用预期的循环来纠正它的原因是,如果多个子进程同时发送它们的终止信号,处理程序可能最终只得到其中一个。
signal(7):
标准信号不排队。如果在阻塞某个标准信号时生成了该信号的多个示例,则只有一个信号示例被标记为待定(并且该信号在解除阻塞时只传送一次)。
循环
waitpid
可以确保获得所有退出的子进程,而不是像现在这样只获得其中一个。为什么循环可以解决多信号问题?
想象一下:你现在正在处理一个你收到的
SIGCHLD
信号,在你处理这个信号的同时,你收到了更多的来自其他子进程的信号,这些信号在这段时间里已经终止了。这些信号不能排队。通过不断地循环waitpid
,你可以确保即使处理程序本身不能处理发送的多个信号,waitpid
仍然会在不断运行时拾取它们,而不是仅在处理程序激活时才运行,处理程序是否能按预期工作取决于信号是否已合并。一旦没有更多的子对象要获取,
waitpid
仍然会正确退出。需要了解的是,循环的作用只是捕获当您已经在信号处理程序中时发送的信号,而不是在正常代码执行期间发送的信号,因为在这种情况下,信号处理程序会正常处理它。如果你仍然有疑问,试着阅读这两个答案。
waitpid(-1, &stat, WNOHANG)
collect all children processes第一种方法使用
WNOHANG
等标志,但如果没有准备好获取的子进程,这只能使waitpid
立即返回而不是等待。