我有一个perl脚本,它运行一系列用于回归测试的批处理脚本。我想在批处理脚本上实现超时。我目前有以下代码。
my $pid = open CMD, "$cmd 2>&1 |";
eval {
# setup the alarm
local $SIG{ALRM} = sub { die "alarm\n" };
# alarm on the timeout
alarm $MAX_TIMEOUT;
log_output("setting alarm to $MAX_TIMEOUT\n");
# run our exe
while( <CMD> ) {
$$out_ref .= $_;
}
$timeRemaining = alarm 0;
};
if ($@) {
#catch the alarm, kill the executable
}
问题是无论我将最大超时设置为什么,警报都不会被触发。我尝试过使用Perl :: Unsafe :: Signals,但没有帮助。
如果我希望能够捕获其输出,这是执行批处理脚本的最佳方法吗?是否有另一种方法可以做同样的事情,允许我使用警报,或者除了警报之外还有另一种方法可以使程序超时吗?
我已经构建了一个测试脚本来确认警报是否适用于我的perl和Windows版本,但是当我运行这样的命令时它不起作用。
我在Windows 7 x64上使用activeperl 5.10.1运行它。
答案 0 :(得分:4)
很难判断alarm
什么时候有效,系统调用何时会被SIGALRM
中断,相同的代码在不同的操作系统上的行为方式如何等等。< / p>
如果你的工作超时,你想要杀死你已经开始的子流程。这是穷人警报的一个很好的用例:
my $pid = open CMD, "$cmd 2>&1 |";
my $time = $MAX_TIMEOUT;
my $poor_mans_alarm = "sleep 1,kill(0,$pid)||exit for 1..$time;kill -9,$pid";
if (fork() == 0) {
exec($^X, "-e", $poor_mans_alarm);
die "Poor man's alarm failed to start"; # shouldn't get here
}
# on Windows, instead of fork+exec, you can say
# system 1, qq[$^X -e "$poor_mans_alarm"]
...
穷人的警报在一个单独的过程中运行。每秒,它会检查标识符为$pid
的进程是否仍然存在。如果进程未处于活动状态,则退出警报进程。如果进程在$time
秒后仍处于活动状态,它会向进程发送一个终止信号(我使用9来使其无法访问,而使用-9来取出整个子进程树,你的需求可能会有所不同)。
(exec
实际上可能没有必要。我使用它是因为我也使用这个成语来监视可能比启动它们的Perl脚本更长的进程。因为这不是这个问题的情况,您可以跳过exec
来电并说
if (fork() == 0) {
for (1..$time) { sleep 1; kill(0,$pid) || exit }
kill -9, $pid;
exit;
}
代替。)