题
请考虑以下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
自己?但我不知道太多关于这一点。
编辑:另一种可能的解决方案,这是浮现在脑海的第一件事,就是简单地在sleep(1)
开始添加/* child stuff */
但有趣的气味对我来说,这就是为什么我离开它。我也甚至不知道这是一个100%的修正。
那么,你将如何纠正这种竞争条件?如果有一个行之有效的模式推荐此,请让我知道!
感谢。
解决方案
简单的解决办法是用fork()
sigprocmask()
之前阻止SIGCHLD信号和在父代码解锁已处理的PID之后。
如果孩子死了,对于SIGCHLD信号处理程序将被解除你的信号后调用。这是一个关键部分的概念 - 。在你的情况临界段fork()
之前开始和children.add()
之后结束
其他提示
如果您无法使用关键的片段,也许一个简单的计数器可以做这个工作。 +1时添加,-1时捞出,没有母校哪一个先发生,你最终可以得到零时,一切都过去了。
在除了现有的“孩子”添加新的数据结构“早期死亡”。这将让孩子们的内容干净。
// 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
if (!earlyDeaths.contains(pid)) {
children.add(pid);
} else {
earlyDeaths.remove(pid);
}
}
}
}
// SIGCHLD handler
sigchld_handler(signo) {
while ( (pid = wait(status, WNOHANG)) > 0 ) {
print "parent caught SIGCHLD from ", pid
if (children.contains(pid)) {
children.remove(pid);
} else {
earlyDeaths.add(pid);
}
}
}
编辑:这可以,如果你的过程是单线程被简化 - earlyDeaths不必是一个容器,它只是为保持一个PID
也许乐观算法?尝试children.remove(PID),和如果失败,移动与寿命。
或者检查pid是在孩子试图将其删除?