杀死一个吊死的孩子进程 [英] Kill a hung child process

查看:119
本文介绍了杀死一个吊死的孩子进程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的Perl脚本运行一个外部程序(采用单个命令行参数)并处理其输出.最初,我是这样做的:

My Perl script runs an external program (which takes a single command-line parameter) and processes its output. Originally, I was doing this:

my @result = `prog arg`;

但是,事实证明该程序有错误,在极少数情况下无法正常挂起.如果在一定时间后仍未退出该程序,该如何杀死该程序?该脚本必须在Windows和Linux中均能正常运行,据我了解,警报和拨叉在Windows中无法(或根本无法)正常工作.

However, turns out that the program is buggy and hangs unpredictably in rare cases. How can I kill the program if it hasn't exited after a certain amount of time? The script has to work both in Windows and in Linux, and it is my understanding that alarms and forks don't work well (or at all) in Windows.

我找到了一个名为 IPC :: Run 但我无法从其文档中找出如何正确使用它. :-(我尝试过这个:

I found a module called IPC::Run but I can't figure out how to use it properly from its documentation. :-( I tried this:

use strict;
use warnings;
use IPC::Run qw(run timeout);
my $in;
my $out;
my $err;
my @result;
my @cmd = qw(prog arg);
run \@cmd, \$in, \$out, \$err, timeout (10) or die "@cmd: $?";
push @result, $_ while (<$out>);
close $out;
print @result;

作为测试,我创建了一个程序,该程序仅睡眠60秒,将字符串输出到stdout并退出.当我尝试使用上面的代码运行它时,它会挂起60秒(而不是超时指定的10秒),然后中止并出现一个奇怪的错误:

As a test, I created a program that just sleeps 60 seconds, prints a string to stdout and exits. When I try to run it with the above code, it hangs for 60 seconds (instead of for 10 seconds, as specified in the timeout) and aborts with a bizarre error:

IPC::Run: timeout on timer #1 at C:/Bin/Unix/Perl/site/lib/IPC/Run.pm line 2956

然后我找到了另一个模块 Proc :: Reliable .从描述来看,它似乎恰好满足了我的要求.除非它不起作用!我试过了:

Then I found another module, Proc::Reliable. From the description, it seems to do precisely what I want. Except that it doesn't work! I tried this:

use strict;
use warnings;
use Proc::Reliable;

my $proc = Proc::Reliable->new ();
$proc->maxtime (10);
my $out = $proc->run ("prog arg");
print "$out\n";

它确实在10秒后中止了子进程.到目前为止,一切都很好.但是后来我修改了外部程序,并使它仅休眠了5秒钟.这意味着程序应在上述代码中指定的10秒超时之前完成,并且其stdout输出应捕获到变量$out中.但事实并非如此!上面的脚本没有输出任何内容.

It indeed aborts the child process after 10 seconds. So far, so good. But then I modified the external program and made it sleep for only 5 seconds. This means that the program should finish before the 10-second timeout specified in the above code and its stdout output should be captured into the variable $out. But it isn't! The above script doesn't output anything.

任何想法如何正确执行? (修复越野车的外部程序不是一种选择.)预先感谢.

Any ideas how to do it properly? (Fixing the buggy external program is not an option.) Thanks in advance.

推荐答案

尝试穷人的警报

my $pid;
if ($^O eq 'MSWin32') {
    $pid = system 1, "prog arg";    # Win32 only, run proc in background
} else {
    $pid = fork();
    if (defined($pid) && $pid == 0) {
        exec("proc arg");
    }
}

my $poor_mans_alarm = "sleep 1,kill(0,$pid)||exit for 1..$TIMEOUT;kill -9,$pid";
system($^X, "-e", $poor_mans_alarm);

穷人的警报在一个单独的过程中运行.每秒检查一次标识符为$ pid的进程是否仍然存在.如果该过程尚未运行,则警报过程将退出.如果该进程在$ time秒后仍处于活动状态,则会向该进程发送一个终止信号(我使用9使其不可陷阱,使用-9删除整个子进程树,您的需求可能会有所不同.kill 9,...也可移植)

The poor man's alarm runs in a separate process. Every second, it checks whether the process with identifier $pid is still alive. If the process isn't alive, the alarm process exits. If the process is still alive after $time seconds, it sends a kill signal to the process (I used 9 to make it untrappable and -9 to take out the whole subprocess tree, your needs may vary. kill 9,... is also portable).

编辑:如何用穷人的警报捕获过程的输出? 不带反引号-那么您将无法获取进程ID,并且如果进程超时并被杀死,您可能会丢失中间输出.替代方法是

How do you capture the output of the process with the poor man's alarm? Not with backticks -- then you can't get the process id and you may lose the intermediate output if the process times out and gets killed. The alternatives are

1)将输出发送到文件,完成该过程后读取文件

1) send output to a file, read the file when the process is done

$pid = system 1, "proc arg > some_file";
... start poor man's alarm, wait for program to finish ...
open my $fh, '<', 'some_file';
my @process_output = <$fh>;
...

2)使用Perl的open启动该过程

2) use Perl's open to start the process

$pid = open my $proc, '-|', 'proc arg';
if (fork() == 0) {
    # run poor man's alarm in a background process
    exec($^X, '-e', "sleep 1,kill 0,$pid||exit ...");
}
my @process_output = ();
while (<$proc>) {
   push @process_output, $_;
}

while循环将在进程自然或不自然地结束时结束.

The while loop will end when the process ends, either naturally or unnaturally.

这篇关于杀死一个吊死的孩子进程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆