3

我有这个代码:

#!/usr/bin/perl
use strict;
use warnings;
my ($timeout, $size, $buffer) = (10, 10, undef);
eval {
    local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
    alarm $timeout;
    my $nread = sysread STDIN, $buffer, $size;
    # !!!! race condition !!!!!
    alarm 0;
    print "$nread: $buffer";
};
if ($@) {    
    warn $@;
}

这是对的吗?8号线和9号线之间可能存在竞争条件吗?

4

3 回答 3

2

让我们看看,发生了什么:

my ($timeout, $size, $buffer) = (10, 10, undef);
eval {
    #establish ALRM signal handler
    local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required

    #send alarm signal to program in 10 second
    alarm $timeout;

    #try yo read 10 bytes of data into $buffer
    my $nread = sysread STDIN, $buffer, $size;

    #cancel the previous timer without starting a new one 
    #if we returned from sysread. Yes, if 10 seconds pass
    #before the next function is executed, the script will
    #die even though the data was read
    alarm 0;

    #print number of bytes read (will be 10) and the string,
    #read from input
    print "$nread: $buffer";
};

如果要评估的字符串没有编译,或者在评估期间执行的 Perl 代码 die()d,则设置 $@。在这些情况下,$@ 的值是编译错误,或者是要死的参数:

if ($@) {    
    warn $@;
}

因此,如果我们在 10 秒内没有从 sysread 返回,这将打印出消息“alarm\n”。

在极不可能的情况下,当输入将在 10 秒过去之前收到并且我们将无法运行警报 0;,我建议使用以下代码:

my ($timeout, $size, $buffer) = (10, 10, undef);

#I define $nread before the signal handler as undef, so if it's defined
#it means, that sysread was executed and the value was assigned
my $nread = undef;
eval {
    local $SIG{ALRM} = sub {

        #if it's not defined - it means, that sysread wasn't executed
        unless(defined($nread))
        {
            die "alarm\n";
        }
    };
    alarm $timeout;
    $nread = sysread STDIN, $buffer, $size;
    alarm 0;
    print "$nread: $buffer";
};

不幸的是,当赋值运算符没有被执行时,它并没有让我们摆脱这种情况。

链接:

http://perldoc.perl.org/functions/alarm.html

http://perldoc.perl.org/perlvar.html

http://perldoc.perl.org/functions/sysread.html

于 2013-07-23T08:49:16.367 回答
1

您的使用alarm引入了潜在的竞争条件。

正常的解决方案是alarm 0;在你的eval块之后添加,所以如果第一个alarm 0没有执行,你仍然可以关闭警报。

或者你可以使用CPAN 上的Time::Out包来包装你的代码,它更好更安全。

于 2013-07-23T09:04:51.340 回答
0

你在什么操作系统上运行这个?什么版本的perl?

在带有 perl 5.12.4 的 Mac OS X 10.8.3 上工作正常。

如果您在 Windows 上使用 perl,您会发现信号在 POSIX 和类似 POSIX 的操作系统上的工作方式不同,您可能需要改用 select() 的 4 参数版本。

于 2013-07-23T08:46:42.573 回答