如何使可能挂起的分叉进程超时?

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,则进程不能被杀死。

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

【讨论】:

以上是关于如何使可能挂起的分叉进程超时?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用独立的 stdout、stderr 和 stdin 分叉一个新进程?

如何在分叉进程中处理套接字连接

如何在python中杀死一个分叉的孩子及其jackd子进程

C: 多个分叉

分叉和传递套接字c ++

如何在分叉进程上使用 gradle 任务启动 Spring Boot 应用程序?