Domanda

Sto scrivendo uno script Perl che scriverà alcuni ingressi e inviare tali ingressi ad un programma esterno. C'è un piccolo, ma non zero possibilità che questo programma si bloccherà, e voglio tempo out:

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

Allo stato attuale, dopo $ num_secs_to_timeout, program_of_interest persiste. Ho cercato di ucciderlo nella subroutine anonimo per $SIG{ALRM} come segue:

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

, ma questo non fa nulla. program_of_interest è ancora persiste. Come posso fare per uccidere questo processo?

È stato utile?

Soluzione

Sono stato in grado di uccidere con successo il mio exec) processo di cura (da uccidendo il gruppo di processi , come mostrato come la risposta alla domanda in perl, uccidendo bambino ed i suoi figli quando il bambino è stata creata usando aperto . Ho modificato il mio codice come segue:

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

Dopo il timeout, program_of_interest viene ucciso con successo.

Altri suggerimenti

Il codice di cui sopra (da strictlyrude27) non ha funzionato out of the box, perché - $ PID è scritto in maiuscolo. (A proposito: c'è anche: http://www.gnu.org /software/coreutils/manual/html_node/timeout-invocation.html )

Ecco un esempio con il 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
  }
}

Hmmm il codice funziona per me , dopo alcune piccole modifiche -. Che presumo sono le modifiche apportate da soli per rendere il codice in un esempio generico

In modo che mi lascia con due idee:

  1. è stato rimosso il problema quando è stato creato il codice di esempio - provare a creare un piccolo campione che in realtà funziona (ho dovuto cambiare 'program_of_interest' e $ num_secs_to_timeout ai valori reali di provarlo). Assicurarsi che il campione ha lo stesso problema.
  2. E 'qualcosa a che fare con la program_of_interest si sta eseguendo - per quanto ne so, non è possibile mascherare un kill 9, ma forse c'è qualcosa da fare. Hai provato a testare il codice con uno script molto semplice. Ho creato uno per il mio test che va while (1) {print "hi \ n"; sonno 1; }
  3. Qualcos'altro .

In bocca al lupo ...

L'unico modo SIGKILL può essere ignorato è che se il processo è bloccato in una chiamata di sistema che è continuità. Controllare lo stato del processo bloccato (con ps aux) se lo stato è D, allora il processo non può essere ucciso.

Si potrebbe anche voler controllare che la funzione è chiamata tramite emissione di qualcosa da esso.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top