2009-12-26 16 views
6

Estoy escribiendo un script Perl que escribirá algunas entradas y las enviará a un programa externo. Hay una pequeña, pero no nula posibilidad de que este programa va a colgar, y quiero en cuando a cabo:¿Cómo puedo agotar el tiempo de espera de un proceso bifurcado que podría bloquearse?

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 matarlo en la subrutina anónima para $SIG{ALRM} de la siguiente manera:

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

pero esto no hace nada. program_of_interest aún persiste. ¿Cómo voy a matar a este proceso?

Respuesta

8

Pude matar con éxito mi proceso exec() ed por matando al grupo de proceso, como se muestra en la respuesta a la pregunta In perl, killing child and its children when child was created using open. Modifiqué mi código de la siguiente manera:

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 del tiempo de espera, program_of_interest se cancela satisfactoriamente.

+0

Creo que "kill 9, - $ PID" debería ser "kill -9, $ PID". Usé el sistema ("kill -9 $ PID") en su lugar. – Boolean

+0

Creo que "kill -9 $ PID" debería ser "kill -9 $ pid", pero sí, el pid negativo indica matar un grupo de procesos en lugar de solo un proceso. De la documentación de kill: "A diferencia de Shell, si SIGNAL es negativo, mata los grupos de procesos en lugar de los procesos. (En el Sistema V, un número de PROCESO negativo también eliminará los grupos de procesos, pero eso no es portátil)". – simpleuser

2

Hmmm su código funciona para mí, después de algunas modificaciones menores, que supongo que son cambios realizados por usted mismo para convertir el código en un ejemplo genérico.

Así que me deja con dos ideas:

  1. ha quitado el problema al crear el código de ejemplo - Trate de crear una pequeña muestra de que realmente ejecuta (que tenía que cambiar 'program_of_interest' y $ num_secs_to_timeout a valores reales para probarlo). Asegúrese de que la muestra tenga el mismo problema.
  2. Tiene algo que ver con el program_of_interest que está ejecutando - hasta donde yo sé, no se puede enmascarar un kill 9, pero tal vez esté pasando algo. ¿Has probado probar tu código con un script realmente simple? Creé uno para mis pruebas que va mientras (1) {imprime "hi \ n"; dormir 1; }
  3. Algo más.

Buena suerte ...

+0

Parece que tenías razón: De hecho, me toqué algo en program_of_interest: system ("echo blahblah | program_of_interest"). sh estaba siendo exec'd y correctamente asesinado, pero no program_of_interest .. –

1

La única manera SIGKILL puede ignorarse es 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 se puede eliminar.

Es posible que también desee comprobar que se está llamando a la función dando salida a algo de ella.

2

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

He aquí 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 
    } 
} 
Cuestiones relacionadas