Как я могу приостановить разветвленный процесс, который может зависнуть? - PullRequest
7 голосов
/ 26 декабря 2009

Я пишу 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 все еще сохраняется. Как мне убить этот процесс?

Ответы [ 4 ]

8 голосов
/ 26 декабря 2009

Мне удалось успешно убить мой процесс exec (), убив , убив группу процессов , как показано в ответе на вопрос В 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);
}

После тайм-аута program_of_interest успешно убит.

3 голосов
/ 03 марта 2011

Вышеприведенный код (строго от 27) не работал "из коробки", потому что - $ 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
  }
}
2 голосов
/ 26 декабря 2009

Хммм Ваш код работает для меня , после некоторых незначительных изменений - которые, как я полагаю, являются изменениями, внесенными вами, чтобы превратить код в общий пример.

Так что у меня остаются две идеи:

  1. Вы удалили проблему, когда создали пример кода - попробуйте создать небольшой пример, который действительно работает (мне пришлось изменить 'program_of_interest' и $ num_secs_to_timeout на реальные значения, чтобы протестировать его). Убедитесь, что образец имеет ту же проблему.
  2. Это как-то связано с запущенной программой - насколько я знаю, вы не можете замаскировать убийство 9, но, возможно, что-то происходит. Вы пробовали тестировать свой код с помощью действительно простого скрипта. Я создал один для моего тестирования, который идет пока (1) {print "hi \ n"; сон 1; }
  3. Что-то еще .

Удачи ...

1 голос
/ 26 декабря 2009

SIGKILL можно игнорировать только в том случае, если процесс застрял в системном вызове, который не прерывается. Проверьте состояние зависшего процесса (с помощью ps aux), если состояние D, то процесс не может быть остановлен.

Возможно, вы также захотите проверить, что функция вызывается путем вывода чего-либо из нее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...