Pergunta

Estou escrevendo um script Perl que escreverá algumas entradas e enviará essas entradas para um programa externo. Há uma chance pequena, mas diferente de zero, de que este programa pendure, e eu quero cronometrar:

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);
}

Como está agora, depois de $ num_secs_to_timeout, program_of_intest ainda persiste. Eu tentei matá -lo na sub -rotina anônima para $SIG{ALRM} do seguinte modo:

local $SIG{ALRM} = sub{kill 9, $pid; die "TIMEOUT!"}

Mas isso não faz nada. Program_of_intest ainda está persistente. Como faço para matar esse processo?

Foi útil?

Solução

Consegui matar com sucesso meu processo executivo () ed por matando o grupo de processos, como mostrado como a resposta para a pergunta Em Perl, matar o filho e seus filhos quando a criança foi criada usando o aberto. Eu modifiquei meu código da seguinte forma:

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);
}

Após o tempo limite, o programa_of_intest é morto com sucesso.

Outras dicas

O código acima (por Strictlyrude27) não funcionou fora da caixa, porque -$ pid está escrito em capitais. (BTW: Há também: http://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html)

Aqui está um exemplo com teste:

#!/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
  }
}

Hmmm Seu código funciona para mim, após algumas pequenas modificações - que eu suponho que sejam as alterações feitas por você para transformar o código em um exemplo genérico.

Então isso me deixa com duas idéias:

  1. Você removeu o problema quando criou o código de amostra - Tente criar uma pequena amostra que realmente seja executada (eu tive que alterar 'program_of_interest' e $ num_secs_to_timeout para valores reais para testá -lo). Verifique se a amostra tem o mesmo problema.
  2. Tem algo a ver com o programa_of_intest que você está executando - Até onde eu sei, você não pode mascarar um Kill 9, mas talvez haja algo acontecendo. Você já tentou testar seu código com um script realmente simples. Eu criei um para os meus testes que dão (1) {print "hi n"; sono 1; }
  3. Algo mais.

Boa sorte...

A única maneira de ignorar Sigkill é se o processo estiver preso em uma chamada do sistema que é ininterrupta. Verifique o estado do processo pendurado (com ps aux) Se o estado for D, o processo não poderá ser morto.

Você também pode querer verificar se a função está sendo chamada produzindo algo a partir dela.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top