因此,我的程序会生成许多子进程以响应某些事件,并且我正在做一些事情,以便在程序退出时跟踪并终止它们(Perl语法):
my %children = ();
# this will be called upon exit
sub kill_children {
kill 'INT' => keys %children;
exit;
}
# main code
while(1) {
...
my $child = fork();
if ($child > 0) {
$children{$child} = 1;
} elsif ($child == 0) {
# do child work ...
exit();
} else {
# handle the error
}
}
所以这个想法如上所述。然而,那里存在明显的竞争条件,因为给定的孩子可以在父亲有机会运行并在%子哈希中记录其pid之前开始和终止。因此,即使这个孩子已经终止,父亲也可能最终认为给定的pid属于活跃的孩子。
有没有办法做我想以安全的方式完成的事情?
编辑:为了更好地跟踪孩子,代码可以扩展如下(但是也会受到完全相同的竞争条件的影响,这就是为什么我没有在第一次完全写入它地方):
my %children = ();
sub reap {
my $child;
while (($child = waitpid(-1, WNOHANG)) > 0) {
#print "collecting dead child $child\n";
delete $children{$child};
}
}
$SIG{CHLD} = \&reap;
# this will be called upon exit
sub kill_children {
local $SIG{CHLD} = 'IGNORE';
kill 'INT' => keys %children;
exit;
}
# main code
while(1) {
...
my $child = fork();
if ($child > 0) {
$children{$child} = 1;
} elsif ($child == 0) {
# do child work ...
exit();
} else {
# handle the error
}
}
即使在这种情况下,%child的内容也可能无法反映实际活跃的孩子。
编辑2:我找到了this question,这正是同样的问题。我喜欢那里建议的解决方案。
答案 0 :(得分:2)
在UNIX上,它不是竞争条件。这是处理fork()
的标准方法。当子进程退出时,其状态将更改为"已终止&#34 ;;它变成zombie。它仍然在进程表中有一个条目,直到父进程调用wait
函数之一。只有在那之后才真正消除了死亡过程。
即使父母将自己设置为忽略SIGCHLD,它仍然不符合竞争条件;父母将只有一个无效的PID。在这种情况下,wait()
将返回ECHILD。 但设置SIGCHLD会释放孩子的PID,可能导致父母试图杀死不是孩子的进程。
在没有fork
调用的Windows上,通过在perl进程中创建线程来模拟它。见perlfork。关于Windows是否可能导致竞争状况,我对Windows不足以了解情况,但我怀疑不是。