Жатва дочерних процессов из Perl
у меня есть сценарий, который порождает набор детей. Родитель должен дождаться окончания каждого из детей.
мой скрипт выполняет аналогично следующему скрипту 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 $localtimen";
exit 0; # Exit the child
}
}
foreach my $child (@children_pids) {
print "Parent: Waiting on $childn";
waitpid($child, 0);
my $localtime = localtime;
print "Parent: Child $child was reaped - $localtime.n";
}
print "All done.n";
подобно коду, который я предоставил выше, каждый ребенок может занять разное время, чтобы закончить.
проблема в том, когда я пытаюсь пожать детей, зацикливаясь на детях PIDs, в этом последнем foreach
блок, родитель ждет детей в том порядке, в котором они находятся создан.
очевидно, что дети не заканчивают в том порядке, в котором они порождены, и поэтому я остаюсь с кучей зомби-процессов для детей, которые заканчиваются рано.
в моем фактическом коде эти дети могут заканчивать дни раньше друг друга, и количество зомби-процессов, плавающих вокруг, может расти сотнями.
есть ли лучший способ для меня, чтобы пожинать набор детей?
4 ответов
Если ваш родительский процесс не должен знать о состоянии завершения своих детей, вы можете просто установить
$SIG{CHLD} = 'IGNORE';
который автоматически пожнет всех детей по мере их завершения.
Если вы do необходимо сообщить о завершении детей, затем обработчик сигнала должен быть установлен, чтобы собрать все возможные процессы
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 " для pid или используйте функцию wait (), чтобы дождаться любого дочернего процесса. Возвращенный PID возвращается, поэтому при необходимости вы можете проверить его по списку. Если это неприемлемо, то периодически waitpid для каждого pid в вашем списке с POSIX:: WNOHANG() в качестве второго аргумента.
Бородина отлично подходит для асинхронного жатвы детей по мере их завершения.
если, как ваш вопрос и код говорят мне, вы ищете синхронно (блокировка) жатва всех выдающихся детей в том порядке, в котором они заканчиваются, родитель может просто сделать это:
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."
никогда не используйте такой цикл, чтобы ждать детей:
while (1) {
my $child = waitpid(-1, POSIX::WNOHANG);
last if $child == -1;
print "Parent: Child $child was reaped\n";
}
родительский процесс будет потреблять 100% cpu, ожидая, пока дочерние процессы умрут-особенно когда они могут работать в течение длительного времени. По крайней мере, добавьте сон (плохая идея - когда они умирают быстро, родитель ждет).
всегда используйте блокировку wait + count for 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 (), который выполняет блокирующий вызов. Даже проверка цикла может извлечь из этого пользу.