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

我能够通过杀死进程组成功地杀死我的 exec() 进程,如问题In perl, kill child and its children when child is created using open的答案所示。我修改了我的代码如下:

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

上面的代码(通过 strictrude27)没有开箱即用,因为 -$PID 用大写字母拼写。(顺便说一句:还有: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

嗯,经过一些小的修改后,您的代码对我有用——我认为这是您自己进行的更改,以使代码成为通用示例。

所以这让我有两个想法:

  1. 您在创建示例代码时消除了问题- 尝试创建一个实际运行的小示例(我必须将 'program_of_interest' 和 $num_secs_to_timeout 更改为实际值来测试它)。确保样品有同样的问题。
  2. 这与您正在运行的 program_of_interest 有关- 据我所知,您无法掩盖杀戮 9,但也许正在发生一些事情。您是否尝试过使用非常简单的脚本来测试您的代码。我为我的测试创建了一个,而 (1) { print "hi\n"; 睡觉1;}
  3. 别的东西

祝你好运...

于 2009-12-26T14:26:57.743 回答
1

可以忽略 SIGKILL 的唯一方法是进程卡在不可中断的系统调用中。检查挂起进程的状态(带ps aux)如果状态为 D,则该进程无法被杀死。

您可能还想通过输出某些内容来检查该函数是否被调用。

于 2009-12-26T14:50:46.603 回答