2009-12-26 7 views
6

Ich schreibe ein Perl-Skript, das einige Eingaben schreibt und diese Eingaben an ein externes Programm sendet. Es gibt eine kleine, aber nicht Null Chance, dass dieses Programm hängen, und ich möchte es eine Zeitüberschreitung:Wie kann ich einen gegabelten Prozess abbrechen, der hängen könnte?

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

Wie es jetzt aussieht, nach $ num_secs_to_timeout, program_of_interest immer noch besteht. Ich versuchte es in dem anonymen Unterprogramm für $SIG{ALRM} zu töten, wie folgt:

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

aber nicht alles tun. program_of_interest besteht noch immer. Wie gehe ich vor, diesen Prozess zu beenden?

Antwort

8

Ich konnte erfolgreich meine exec() ed-Prozess von töten die Prozessgruppe töten, wie als Antwort auf Frage In perl, killing child and its children when child was created using open gezeigt. Ich habe meinen Code wie folgt geändert:

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

Nach dem Timeout wird program_of_interest erfolgreich beendet.

+0

Ich denke, "kill 9, - $ PID" sollte "kill -9, $ PID" sein. Ich habe stattdessen das System ("kill -9 $ PID") verwendet. – Boolean

+0

Ich denke, "kill -9 $ PID" sollte weiter "kill -9 $ pid" sein, aber ja, die negative pid zeigt an, eine Prozessgruppe statt nur einen Prozess zu töten. Aus der Kill-Dokumentation: "Anders als in der Shell, wenn SIGNAL negativ ist, werden Prozessgruppen anstatt Prozesse gelöscht. (Auf System V wird eine negative PROCESS-Nummer auch Prozessgruppen löschen, aber das ist nicht portierbar.)" – simpleuser

2

Hmmm Ihr Code funktioniert für mich, nach ein paar kleinen Änderungen - die ich vermute, sind Änderungen von Ihnen vorgenommen, um den Code in ein generisches Beispiel zu machen.

Damit ich mit zwei Ideen läßt:

  1. Sie entfernt, um das Problem, wenn Sie den Beispielcode erstellt - versuchen, eine kleine Probe zu schaffen, die tatsächlich läuft (ich hatte ‚program_of_interest‘ und $ num_secs_to_timeout zu ändern zu realen Werten, um es zu testen). Stellen Sie sicher, dass das Beispiel das gleiche Problem aufweist.
  2. Es hat etwas mit dem Programm_von_Interesse zu tun, - soweit ich weiß, können Sie eine Kill 9 nicht maskieren, aber vielleicht ist etwas los. Haben Sie versucht, Ihren Code mit einem wirklich einfachen Skript zu testen? Ich habe einen für meine Tests erstellt, der geht während (1) {print "hi \ n"; Schlaf 1; }
  3. Etwas anderes.

Viel Glück ...

+0

Sieht aus wie Sie richtig waren: Ich verrohrt tatsächlich etwas in program_of_interest: system ("echo blahblah | program_of_interest"). sh wurde exec'd und ordnungsgemäß getötet, aber nicht program_of_interest .. –

1

Der einzige Weg, SIGKILL ignoriert werden kann, wenn das Verfahren in einem Systemaufruf stecken, die unterbrechungsfrei ist. Überprüfen Sie den Status des angehaltenen Prozesses (mit ps aux), wenn der Status D ist, dann kann der Prozess nicht beendet werden.

Sie können auch überprüfen, ob die Funktion aufgerufen wird, indem Sie etwas von ihr ausgeben.

2

Der obige Code (von strictlyrude27) funktionierte nicht aus der Box, weil - $ PID in Großbuchstaben geschrieben ist. (BTW: Es gibt auch: http://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html)

Hier ist ein Beispiel mit Test:

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