Как я могу остановить разветвленный процесс, который может зависнуть?

StackOverflow https://stackoverflow.com/questions/1962985

  •  21-09-2019
  •  | 
  •  

Вопрос

Я пишу сценарий 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
  }
}

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

Итак, у меня остается две идеи:

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

Удачи...

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top