7

いくつかの入力を書き込み、それらの入力を外部プログラムに送信する Perl スクリプトを作成しています。このプログラムがハングする可能性はわずかですがゼロではないため、タイムアウトさせたいと考えています。

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

現時点では、$num_secs_to_timeout の後、program_of_interest は引き続き存続します。次のように、匿名サブルーチンでそれを殺そうとしました$SIG{ALRM}:

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

しかし、これは何もしません。program_of_interest はまだ存続しています。このプロセスを強制終了するにはどうすればよいですか?

4

4 に答える 4

8

質問In perl, kill child and its children when child is created using open に示されているように、プロセス グループを強制終了することで、 exec()ed プロセスを正常に強制終了できました。コードを次のように変更しました。

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

タイムアウト後、program_of_interest は正常に強制終了されます。

于 2009-12-26T18:34:08.103 に答える
3

-$PID は大文字で綴られているため、上記のコード (厳密にはrude27による) はそのままでは機能しませんでした。(ところで: http://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.htmlもあります)

テストの例を次に示します。

#!/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
  }
}
于 2011-03-03T10:01:16.503 に答える
2

うーん、あなたのコードは、いくつかの小さな変更を加えた後、私のために機能します-これは、コードを一般的な例にするために自分で行った変更だと思います。

したがって、2つのアイデアが残ります。

  1. サンプルコードを作成したときに問題を取り除きました。実際に実行される小さなサンプルを作成してみてください(テストするには、「program_of_interest」と$ num_secs_to_timeoutを実際の値に変更する必要がありました)。サンプルに同じ問題があることを確認してください。
  2. これは、実行しているprogram_of_interestと関係があります。私が知る限り、キル9をマスクすることはできませんが、何かが起こっている可能性があります。本当に単純なスクリプトでコードをテストしてみましたか。テスト用に1つ作成しましたが、(1){print "hi \ n"; 睡眠1; }
  3. 他の何か

幸運を...

于 2009-12-26T14:26:57.743 に答える
1

SIGKILL を無視できる唯一の方法は、プロセスが中断できないシステム コールでスタックしている場合です。ハングしたプロセスの状態を ( でps aux) 確認します。状態が D の場合、プロセスを強制終了できません。

関数から何かを出力して、関数が呼び出されていることを確認することもできます。

于 2009-12-26T14:50:46.603 に答える