2009-12-26 20 views
6

我正在编写一个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仍然存在。我该如何去杀死这个过程?

回答

8

我能够成功地通过杀死我的exec()ed进程,从而终止进程组,如问题In perl, killing child and its children when child was 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。

+0

我认为“kill 9, - $ PID”应该是“kill -9,$ PID”。我使用了系统(“kill -9 $ PID”)。 – Boolean 2013-06-03 19:34:15

+0

我认为“kill -9 $ PID”应该进一步是“kill -9 $ pid”,但是,否定pid表示杀死一个进程组,而不仅仅是一个进程。从kill文档中可以看出:“与shell不同的是,如果SIGNAL为负数,它将杀死进程组而不是进程(在System V上,负的进程号也会终止进程组,但这不是可移植的)” – simpleuser 2014-04-03 17:59:54

2

你的代码适用于我,经过一些小的修改 - 我假设你自己将代码变成一个通用示例。

所以这让我有两个想法:

  1. 当你创建的示例代码您删除的问题 - 尝试创建实际运行一个小样本(我不得不改变“program_of_interest”和$ num_secs_to_timeout以真正的价值来测试它)。确保样本具有相同的问题。
  2. 这与你正在运行的program_of_interest有关 - 据我所知,你不能掩盖杀死9,但也许有一些事情正在发生。你有没有尝试用一个非常简单的脚本来测试你的代码。 (1){print“hi \ n”;睡1; }
  3. 别的东西

好运...

+0

看起来你是对的:我实际上管道东西放到program_of_interest:系统( “回声blahblah | program_of_interest”)。 sh正在执行并正确处理,但不是program_of_interest .. – 2009-12-26 18:33:11

1

的唯一途径SIGKILL可以忽略是,如果过程是停留在一个系统调用是不间断。如果状态为D,则检查挂起进程的状态(使用ps aux),那么进程无法被终止。

您可能还想通过输出某些东西来检查该函数是否正在调用。

2

上面的代码(strictrude27)没有开箱即用,因为 - $ PID是用大写拼写的。 (BTW:还有还有: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 
    } 
}