这种非分叉的 Perl 超时方法是否安全?



我有一个函数可以调用其他几个函数,例如

sub do_work {
    send_mail();
    send_soap_envelope();
    send_rpc();
}

被调用的函数可能会挂起,所以我想在超时后停止它们。我想避免分叉,因为它在我的上下文中很昂贵(例如,每次分叉后都需要重新创建数据库句柄)。我想出了以下方案:

sub timeout {
    my ($code) = @_;
    eval {
        alarm 2;
        local $SIG{ALRM} = sub { die 'timeout' };
        &$code;
        alarm 0;
    };
    # handling of $@ eq 'timeout' removed for brevity
}
sub do_work {
    timeout &send_mail;
    timeout &send_soap_envelope;
    timeout &send_rpc;
};

timeout()函数(在此示例中硬编码为 2 秒超时)使用 eval 块作为使用 die 中止有效负载函数执行的方法。

这在我的测试场景中工作正常,但是我对如果die在Perl解释器不处于"安全状态"时中断有效负载功能会发生什么感到不安,例如,当它正在处理XS子例程时。我的直觉对吗?

从 5.8.1 开始,Perl 使用了"安全信号处理"。它不会向系统提供信号处理程序,而是提供安全的信号处理程序。此安全信号处理程序只是记录信号已接收并返回。在执行 Perl 操作码之间,解释器检查是否接收到任何信号,如果有,则调用信号处理程序。

这意味着信号不会在长操作过程中得到处理,例如长 XS 调用或长正则表达式匹配。信号会中断大多数系统调用,因此,如果您处于阻塞系统调用的中间(例如 sleepread等)

alarm(2);
my $s = time;
$SIG{ALRM} = sub {
   my $e = time;
   print $e-$s, "n";  # 6, not 2.
};
('a' x 25) =~ /a*a*a*a*a*a*a*a*a*(?:b|c)/;

* — 为了加快程序速度,现在检查的频率会降低一些,但您不应该注意到差异。

它不太安全,因为它在安装SIGALRM处理程序之前调用alarm()。交换local $SIG{ALRM}alarm线,应该会有很大的改进。

好的,现在我看到perldoc -f alarm提到了我的确切用例:

如果要使用"

警报"使系统调用超时,则需要使用"eval"/"die"对。

(后面是示例代码。

相关内容

  • 没有找到相关文章

最新更新