请考虑以下fork()/ SIGCHLD伪代码.
// main program excerpt
for (;;) {
if ( is_time_to_make_babies ) {
pid = fork();
if (pid == -1) {
/* fail */
} else if (pid == 0) {
/* child stuff */
print "child started"
exit
} else {
/* parent stuff */
print "parent forked new child ", pid
children.add(pid);
}
}
}
// SIGCHLD handler
sigchld_handler(signo) {
while ( (pid = wait(status, WNOHANG)) > 0 ) {
print "parent caught SIGCHLD from ", pid
children.remove(pid);
}
}
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,有一个竞争条件." /* child stuff */"可以在" /* parent stuff */"开始之前完成,这可能导致孩子的pid在退出后被添加到子列表中,并且永远不会被删除.当应用程序关闭的时候,父母将无休止地等待已经完成的孩子完成.
我能想到的一个解决方案就是有两个列表:started_children和finished_children.我将添加到started_children我现在添加到的相同位置children.但在信号处理程序中,而不是从children我添加到删除finished_children.当应用程序关闭时,父级可以简单地等到started_children和之间的差异finished_children为零.
我能想到的另一个可能的解决方案是使用共享内存,例如分享父母的孩子列表并让孩子.add和.remove他们自己?但我对此并不太了解.
编辑:另一个可能的解决方案,这是我想到的第一件事,就是简单地添加一个sleep(1)开头/* child stuff */但对我来说闻起来很有趣,这就是为什么我把它排除在外.我甚至不确定它是100%修复.
那么,你如何纠正这种竞争条件?如果有一个完善的推荐模式,请告诉我!
谢谢.
qrd*_*rdl 15
最简单的解决方案是在处理pid之后阻止SIGCHLD信号fork(),sigprocmask()并在父代码中解除阻塞.
如果孩子死亡,在解锁信号后将调用SIGCHLD的信号处理程序.这是一个关键的部分概念 - 在您的情况下,关键部分在之前fork()和之后开始children.add().