Pregunta

Estoy escribiendo un script en Perl que va a escribir algunas entradas y enviar las entradas a un programa externo.Hay una pequeña, pero no cero posibilidad de que este programa se bloquea, y quiero tiempo fuera:

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

Tal como está ahora, después de $num_secs_to_timeout, program_of_interest aún persiste.Traté de matar en la subrutina anónima $SIG{ALRM} de la siguiente manera:

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

pero este no hace nada.program_of_interest es todavía persiste.¿Cómo ir sobre el asesinato de este proceso?

¿Fue útil?

Solución

Yo era capaz de matar con éxito mi exec()ed proceso por matar el proceso del grupo, tal como se muestra la respuesta a la pregunta En perl, matando niño y de sus hijos cuando niño fue creado usando abierto.He modificado mi código como sigue:

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

Después de tiempo de espera, program_of_interest correctamente asesinados.

Otros consejos

El código anterior (por strictlyrude27) no funcionaba fuera de la caja, porque - $ PID se escribe en mayúsculas. (Por cierto: también hay: http://www.gnu.org /software/coreutils/manual/html_node/timeout-invocation.html )

Este es un ejemplo con la prueba:

#!/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 su código funciona para mí , después de algunas modificaciones de menor importancia -. Que supongo que son los cambios hechos por ti mismo para hacer el código en un ejemplo genérico

Así que me deja con dos ideas:

  1. Ha retirado el problema al crear el código de ejemplo - intente crear una pequeña muestra de que realmente ejecuta (que tenía que cambiar 'program_of_interest' y $ num_secs_to_timeout a los valores reales para probarlo). Asegúrese de que la muestra tiene el mismo problema.
  2. Es algo que ver con el que se está ejecutando program_of_interest - por lo que yo sé, no se puede enmascarar una matanza 9, pero tal vez hay algo que hacer. ¿Usted ha intentado probar su código con un guión muy simple. He creado una para mis pruebas que va while (1) {print "Hola \ n"; sueño 1; }
  3. Otra cosa .

Buena suerte ...

La única manera SIGKILL puede ser ignorado es que si el proceso se ha quedado atascado en una llamada al sistema que es ininterrumpida. Compruebe el estado del proceso bloqueado (con ps aux) si el estado es D, entonces el proceso no puede ser matado.

Es posible que también desee comprobar que la función está siendo llamado por la salida de algo de él.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top