信号处理程序不使用双eval

时间:2015-04-10 07:00:54

标签: perl

我有这段代码来超时长时间运行的进程(在这种情况下为sleep):

#!/usr/bin/env perl
use strict;
use warnings;

die "Usage: $0 SLEEP TIMEOUT\n" unless @ARGV == 2;
my ( $sleep, $timeout ) = @ARGV;

$|++;

eval {
    local $SIG{ALRM} = sub { die "TIMEOUT\n" };
    alarm $timeout;

    eval {
        # long-running process
        print "Going to sleep ... ";
        sleep $sleep;
        print "DONE\n";
    };  

    alarm 0;    # cancel timeout
};  

die $@ if $@;

当我将其作为./alarm 5 2运行时,我希望die说“TIMEOUT”。然而它以0退出并且什么也没说。当我删除内部eval块(不是块的内容,只是eval)时,它按预期工作。有人可以解释为什么会这样吗?感谢。

1 个答案:

答案 0 :(得分:1)

因为您在第一个eval块中捕获了错误,而第二个eval块没有异常并清除$@

eval {
    local $SIG{ALRM} = sub { die "TIMEOUT\n" };
    alarm $timeout;

    eval {
        # long-running process
        print "Going to sleep ... ";
A:      sleep $sleep;
        print "DONE\n";
    };  
B:

    alarm 0;    # cancel timeout
C:};  

die $@ if $@;

$sleep> $timeout,因此在A:您的计划会抛出SIGALRM。信号由本地信号处理程序捕获并调用die "TIMEOUT\n"。因此,Perl将$@设置为"TIMEOUT\n"并在B:处继续执行。然后,您的程序会将其转到C:,而不会出现任何其他错误。由于您的外部eval块已正常完成,因此Perl会清除$@,并且您的最终die语句不会执行。

要做你想做的事,你可以

  1. 请勿在外部区块
  2. 上使用eval
  3. 在外部块的末尾添加另一个die $@ if $@调用