所以我的程序生成了一些子进程来响应某些事件,我正在做类似这样的事情来跟踪并在程序退出时杀死它们(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
}
}
所以思路如上。然而,那里有一个明显的竞争条件,因为给定的 child 可以在父亲有机会运行并将其 pid 记录在 %children 哈希中之前开始和终止。所以父亲最终可能会认为给定的 pid 属于一个活跃的 child ,即使这个 child 已经终止。
有没有办法以安全的方式完成我想要完成的事情?
编辑:为了更好地跟踪 child ,可以按如下方式扩展代码(但是它也会遇到完全相同的竞争条件,所以这就是我一开始没有完整编写它的原因):
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
}
}
即使在这种情况下,%children 的内容也可能无法反射(reflect)实际活跃的 child 。
编辑 2: 我找到了 this question ,这完全是同一个问题。我喜欢那里建议的解决方案。
最佳答案
在 UNIX 上,这不是竞争条件。这是处理 fork()
的标准方法。当子进程退出时,其状态变为“已终止”;它变成了 zombie .它在进程表中仍然有一个条目,直到父进程调用其中一个 wait
函数。只有在那之后,死进程才真正被移除。
即使父级将自己设置为忽略 SIGCHLD,它仍然不符合竞争条件的条件; parent 只会有一个不再有效的 PID。在这种情况下,wait()
将返回 ECHILD。 但是设置 SIGCHLD 会释放子进程的 PID,可能会导致父进程试图杀死一个不是子进程的进程。
在没有 fork
调用的 Windows 上,它是通过在 perl 进程中创建一个线程来模拟的。参见 perlfork .我对 Windows 的了解还不足以判断这是否会导致竞争条件,但我怀疑不会。
关于linux - 如何跟踪子进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25192995/