【发布时间】:2010-09-25 07:30:51
【问题描述】:
请考虑以下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);
}
}
在上面的例子中有一个竞争条件。 "/* child stuff */" 可能在 "/* parent stuff */" 开始之前完成,这可能导致孩子的 pid 在退出后被添加到孩子列表中,并且永远不会被删除。当应用程序关闭时,父母将无休止地等待已经完成的孩子完成。
我能想到的一个解决方案是有两个列表:started_children 和 finished_children。我现在添加到started_children 的位置与我现在添加到children 的位置相同。但在信号处理程序中,不是从children 中删除,而是添加 到finished_children。当应用关闭时,父母可以简单地等到started_children 和finished_children 之间的差异为零。
我能想到的另一个可能的解决方案是使用共享内存,例如分享家长的孩子名单,让孩子.add和.remove自己?但我对这方面了解不多。
编辑:另一个可能的解决方案是首先想到的,就是在/* child stuff */ 的开头添加一个sleep(1),但这对我来说很有趣,这就是我忽略它的原因。我什至不确定它是否 100% 修复。
那么,您将如何纠正这种竞争条件?如果对此有完善的推荐模式,请告诉我!
谢谢。
【问题讨论】:
-
只是我还是那个信号处理程序不是异步安全的?当新的 SIGCHLD 在中间中断时,children.remove() 怎么可能实现不爆炸?