Как я могу остановить разветвленный процесс, который может зависнуть?
Вопрос
Я пишу сценарий Perl, который будет записывать некоторые входные данные и отправлять их во внешнюю программу.Существует небольшая, но ненулевая вероятность того, что эта программа зависнет, и я хочу установить время ее ожидания:
my $pid = fork;
if ($pid > 0){
eval{
local $SIG{ALRM} = sub { die "TIMEOUT!"};
alarm $num_secs_to_timeout;
waitpid($pid, 0);
alarm 0;
};
}
elsif ($pid == 0){
exec('echo blahblah | program_of_interest');
exit(0);
}
В нынешнем виде, после $num_secs_to_timeout, program_of_interest все еще сохраняется.Я пытался убить его в анонимной подпрограмме для $SIG{ALRM}
следующее:
local $SIG{ALRM} = sub{kill 9, $pid; die "TIMEOUT!"}
но это ничего не дает.program_of_interest все еще сохраняется.Как мне убить этот процесс?
Решение
Мне удалось успешно завершить процесс exec()ed с помощью убийство группы процессов, как показано в ответе на вопрос В Perl убийство дочернего элемента и его дочерних элементов, когда дочерний элемент был создан с использованием открытого.Я изменил свой код следующим образом:
my $pid = fork;
if ($pid > 0){
eval{
local $SIG{ALRM} = sub {kill 9, -$PID; die "TIMEOUT!"};
alarm $num_secs_to_timeout;
waitpid($pid, 0);
alarm 0;
};
}
elsif ($pid == 0){
setpgrp(0,0);
exec('echo blahblah | program_of_interest');
exit(0);
}
По истечении времени ожидания программа_of_interest успешно уничтожается.
Другие советы
Приведенный выше код (автор: strictrude27) не работал сразу, поскольку -$PID пишется заглавными буквами.(КСТАТИ:есть также: http://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html)
Вот пример с тестом:
#!/usr/bin/perl
use strict;
use warnings;
use File::Basename;
my $prg = basename $0;
my $num_secs_sleep = 2;
my $num_secs_to_timeout = 1;
my $orig_program = "sleep $num_secs_sleep; echo \"Look ma, survived!\"";
my $program = $orig_program;
my $expect = "";
if (@ARGV){
if($ARGV[0] eq "test"){
test();
exit 0;
} elsif (@ARGV == 1) {
$num_secs_to_timeout = $ARGV[0];
} elsif (@ARGV == 2) {
$program = $ARGV[0];
$num_secs_to_timeout = $ARGV[1];
} else {
die "Usage: $prg [ \"test\" | [program] seconds ] "
}
}
if($orig_program eq $program) {
if(@ARGV < 2) {
$expect = $num_secs_to_timeout > $num_secs_sleep ?
"(we expected to survive.)" : "(we expected to TIME OUT!)";
}
print STDERR "sleeping: $num_secs_sleep seconds$/";
}
print STDERR <<END;
timeout after: $num_secs_to_timeout seconds,
running program: '$program'
END
if($orig_program eq $program) {
print STDERR "$expect$/";
}
exit Timed::timed($program, $num_secs_to_timeout);
sub test {
eval "use Test::More qw(no_plan);";
my $stdout;
close STDOUT;
open STDOUT, '>', \$stdout or die "Can't open STDOUT: $!";
Timed::timed("sleep 1", 3);
is($stdout, undef);
Timed::timed("sleep 2", 1);
is($stdout, "TIME OUT!$/");
}
################################################################################
package Timed;
use strict;
use warnings;
sub timed {
my $retval;
my ($program, $num_secs_to_timeout) = @_;
my $pid = fork;
if ($pid > 0){ # parent process
eval{
local $SIG{ALRM} =
sub {kill 9, -$pid; print STDOUT "TIME OUT!$/"; $retval = 124;};
alarm $num_secs_to_timeout;
waitpid($pid, 0);
alarm 0;
};
return defined($retval) ? $retval : $?>>8;
}
elsif ($pid == 0){ # child process
setpgrp(0,0);
exec($program);
} else { # forking not successful
}
}
Хм твой код работает для меня, после некоторых незначительных модификаций, которые, как я предполагаю, являются изменениями, внесенными вами с целью превратить код в общий пример.
Итак, у меня остается две идеи:
- Вы устранили проблему, когда создали пример кода. - попробуйте создать небольшой образец, который действительно работает (мне пришлось изменить «program_of_interest» и $num_secs_to_timeout на реальные значения, чтобы проверить это).Убедитесь, что образец имеет ту же проблему.
- Это как-то связано с программой_of_interest, которую вы используете - насколько я знаю, убийство 9 замаскировать нельзя, но, возможно, что-то происходит.Пробовали ли вы протестировать свой код с помощью действительно простого сценария.Я создал один для своего тестирования, который работает while (1) { print "hi ";сон 1;}
- Что-то другое.
Удачи...
Единственный способ игнорировать SIGKILL — это если процесс застрял в системном вызове, который не может быть прерван.Проверьте состояние зависшего процесса (с помощью ps aux
) если состояние D, то процесс невозможно завершить.
Вы также можете проверить, вызывается ли функция, выведя из нее что-нибудь.