我有这段代码:
#!/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行之间有竞争条件吗?
答案 0 :(得分: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代码,则设置$ @()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
答案 1 :(得分:1)
您对alarm
的使用会引发潜在的竞争条件。
正常的解决方案是在alarm 0;
阻止后添加eval
,因此如果第一个alarm 0
未执行,您仍然可以关闭闹钟。
或者你可以在CPAN上使用Time::Out包来包装你的代码,它会更好更安全。
答案 2 :(得分:0)
你在运行什么操作系统?什么版本的perl?
使用perl 5.12.4在Mac OS X 10.8.3上运行正常。
如果您在Windows上使用perl,您会发现信号与POSIX和类似POSIX的操作系统的工作方式不同,您可能需要使用4参数替代版本的select()。