2009-12-26 23 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 
    } 
}