我有一个产生一组孩子的脚本。家长必须等待每个孩子完成。
我的脚本执行类似于以下perl脚本:
#! /usr/bin/perl
use strict;
use warnings;
print "I am the only process.\n";
my @children_pids;
for my $count (1..10){
my $child_pid = fork();
if ($child_pid) { # If I have a child PID, then I must be the parent
push @children_pids, $child_pid;
}
else { # I am the child
my $wait_time = int(rand(30));
sleep $wait_time;
my $localtime = localtime;
print "Child: Some child exited at $localtime\n";
exit 0; # Exit the child
}
}
foreach my $child (@children_pids) {
print "Parent: Waiting on $child\n";
waitpid($child, 0);
my $localtime = localtime;
print "Parent: Child $child was reaped - $localtime.\n";
}
print "All done.\n";
与我上面提供的代码类似,每个孩子可能需要不同的时间才能完成。
问题是当我尝试通过循环子PID时收获子节点,在最后foreach
块中,父节点按照创建它们的顺序等待子节点。
显然,孩子们没有按照他们产生的顺序完成,所以我为孩子们留下了一堆僵尸进程,这些进程很快就会完成。
在我的实际代码中,这些孩子可能会在彼此之前完成几天,并且漂浮在周围的僵尸进程的数量可以增加到数百个。
我有更好的方法来收获一组孩子吗?
答案 0 :(得分:12)
如果您的父进程不需要知道其子进程的完成状态,那么您只需设置
即可$SIG{CHLD} = 'IGNORE';
会在所有孩子完成后自动收割。
如果做需要通知孩子完成,那么需要设置信号处理程序以获得所有可能的过程
use POSIX ();
$SIG{CHLD} = sub {
while () {
my $child = waitpid -1, POSIX::WNOHANG;
last if $child <= 0;
my $localtime = localtime;
print "Parent: Child $child was reaped - $localtime.\n";
}
};
答案 1 :(得分:6)
使用“-1”表示pid,或使用wait()函数以等待任何子进程。收到的pid会被退回,因此您可以根据需要在列表中进行检查。如果这是不可接受的,那么定期使用POSIX :: WNOHANG()作为第二个参数,对列表中的每个pid进行waitpid。
答案 2 :(得分:5)
Borodin's answer非常适合儿童终止时的异步收割。
如果您的问题和代码向我建议,您正在寻找同步(阻止)按照终止的顺序收集所有未完成的孩子,父母可以这样做:
use feature qw(say);
...
# Block until all children are finished
while (1) {
my $child = waitpid(-1, 0);
last if $child == -1; # No more outstanding children
say "Parent: Child $child was reaped - ", scalar localtime, ".";
}
say "All done."
答案 3 :(得分:1)
永远不要使用这样的循环来等待孩子:
while (1) {
my $child = waitpid(-1, POSIX::WNOHANG);
last if $child == -1;
print "Parent: Child $child was reaped\n";
}
在等待子进程死亡时,父进程将消耗100%的cpu - 特别是当它们可以运行很长时间时。至少加一个睡眠(坏主意 - 当他们快死时,父母正在等待)。
始终对TERM / INT / ppid使用阻塞等待+计数以获得良好性!:
my $loop = 1;
$SIG{CHLD} = 'DEFAULT'; # turn off auto reaper
$SIG{INT} = $SIG{TERM} = sub {$loop = 0; kill -15 => @children_pids};
while ($loop && getppid() != 1) {
my $child = waitpid(-1, 0);
last if $child == -1;
print "Parent: Child $child was reaped\n";
}
当父进程也必须执行其他操作时,阻塞等待它当然不可能 - 例如getppid()调用;-)。为此,您可以使用socketpair()并将其放在执行阻塞调用的select()中。即使是循环检查也可以从中受益。