如何使可能挂起的分叉进程超时?
Posted
技术标签:
【中文标题】如何使可能挂起的分叉进程超时?【英文标题】:How can I timeout a forked process that might hang? 【发布时间】:2009-12-26 09:17:43 【问题描述】:我正在编写一个 Perl 脚本,它将编写一些输入并将这些输入发送到外部程序。该程序挂起的可能性很小但非零,我想将其超时:
my $pid = fork;
if ($pid > 0)
eval
local $SIGALRM = 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 仍然存在。我试图在$SIGALRM
的匿名子例程中杀死它,如下所示:
local $SIGALRM = subkill 9, $pid; die "TIMEOUT!"
但这并没有做任何事情。 program_of_interest 仍然存在。我该如何杀死这个进程?
【问题讨论】:
【参考方案1】:我能够通过杀死进程组成功地杀死我的 exec()ed 进程,如问题 In perl, killing child and its children when child was created using open 的答案所示。我修改了我的代码如下:
my $pid = fork;
if ($pid > 0)
eval
local $SIGALRM = 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 被成功杀死。
【讨论】:
我认为“kill 9, -$PID”应该是“kill -9, $PID”。我用 system("kill -9 $PID") 代替。 我认为“kill -9 $PID”应该进一步是“kill -9 $pid”,但是是的,负的pid表示要杀死一个进程组而不仅仅是一个进程。来自 kill 文档:“与 shell 不同,如果 SIGNAL 为负数,它会杀死进程组而不是进程。(在 System V 上,负数 PROCESS 也会杀死进程组,但这不是可移植的。)”【参考方案2】:上面的代码(由 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 $SIGALRM =
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
【讨论】:
【参考方案3】:嗯您的代码适用于我,经过一些小的修改 - 我认为这是您自己进行的更改以使代码成为通用示例。
所以这让我有两个想法:
-
您在创建示例代码时解决了问题 - 尝试创建一个实际运行的小示例(我必须将 'program_of_interest' 和 $num_secs_to_timeout 更改为实际值来测试它)。确保样本有同样的问题。
这与您正在运行的 program_of_interest 有关 - 据我所知,您无法掩盖杀戮 9,但也许发生了一些事情。您是否尝试过使用非常简单的脚本来测试您的代码。我为我的测试创建了一个,而 (1) print "hi\n";睡觉1;
别的东西。
祝你好运……
【讨论】:
看起来你是对的:我实际上将一些东西通过管道传输到 program_of_interest:system("echo blahblah | program_of_interest")。 sh 被执行并被正确杀死,但不是 program_of_interest..【参考方案4】:可以忽略 SIGKILL 的唯一方法是进程卡在不可中断的系统调用中。检查挂起进程的状态(用ps aux
),如果状态为D,则进程不能被杀死。
您可能还想通过输出一些内容来检查该函数是否被调用。
【讨论】:
以上是关于如何使可能挂起的分叉进程超时?的主要内容,如果未能解决你的问题,请参考以下文章