当没有任何内容可读取时,perl can_read 返回句柄
在下面的代码中,我尝试执行简单的文件尾部操作。假设 foo.txt 完全是空的。我预计第一个 can_read() 会阻塞,但事实并非如此。 sysreadline() 中的 can_read 调用也不会阻塞或等待。相反,第一个 can_read 立即返回 foo.txt 的句柄,并且 sysreadline 中的第一个 can_read 执行相同的操作。然后 sysread 不会返回任何内容,因为没有任何内容可读取,这会导致 sysreadline 内繁忙等待。怎么会这样呢?我知道选择可能会由于信号或关闭的文件句柄而提前结束,但我在这里看不到任何机会。事实上,当文本确实出现在 foo.txt 中(带有换行符)时,它就会被打印出来。我不明白为什么当没有任何内容可读取时,代码不会像第一个 can_read 那样无限期地阻塞。除了浪费 cpu 之外,它还使得无法同时尾部多个文件,因为你总是会陷入第一个繁忙的等待。我觉得我一定忽略了一些简单的东西......
这是 perl 5.8.8
#!/usr/bin/perl -w
use strict;
use IO::Select;
use IO::Handle;
use Symbol qw(qualify_to_ref);
open my $inf, "<", "foo.txt" or die "hey! Can't open foo.txt!\n";
my $sel = IO::Select->new();
$sel->add($inf);
while(my @ready = $sel->can_read()){
foreach my $handle (@ready){
my $line = sysreadline($handle);
print $line;
}
}
##
## deal with buffering for select. from perl cookbook 7.23
sub sysreadline {
my($handle, $timeout) = @_;
$handle = qualify_to_ref($handle, caller( ));
my $infinitely_patient = (@_ == 1 || $timeout new( );
$selector->add($handle);
my $line = "";
SLEEP:
until (at_eol($line)) {
unless ($infinitely_patient) {
return $line if time( ) > ($start_time + $timeout);
}
# sleep only 1 second before checking again
next SLEEP unless $selector->can_read(1.0);
INPUT_READY:
while ($selector->can_read(0.0)) {
my $was_blocking = $handle->blocking(0);
CHAR: while (sysread($handle, my $nextbyte, 1)) {
$line .= $nextbyte;
##print "next: [$nextbyte]\n";
last CHAR if $nextbyte eq "\n";
}
$handle->blocking($was_blocking);
# if incomplete line, keep trying
next SLEEP unless at_eol($line);
last INPUT_READY;
}
}
return $line;
}
sub at_eol($) { $_[0] =~ /\n\z/ }
In the below code, I am attempting to perform a simple file tail operation. Assume foo.txt is completely empty. I would expect that the first can_read() would block but it does not. Nor do the can_read calls in sysreadline() block or wait either. Instead what happens is that the first can_read immediately returns the handle to foo.txt and the first can_read in sysreadline does the same. The sysread then returns nothing because there is nothing to read, which results in a busy wait inside sysreadline. How can this be? I know a select can end early due to a signal or closed file handle, but I don't see any opportunity for that here. In fact, when text does appear (with a newline) in foo.txt, it is printed. I don't see why the code doesn't block indefinitely as the first can_read when there is nothing to read. In addition to wasting cpu, it makes it impossible to tail multiple files at the same time because you will always get stuck in the first busy wait. I feel like I must be overlooking something simple here...
This is perl 5.8.8
#!/usr/bin/perl -w
use strict;
use IO::Select;
use IO::Handle;
use Symbol qw(qualify_to_ref);
open my $inf, "<", "foo.txt" or die "hey! Can't open foo.txt!\n";
my $sel = IO::Select->new();
$sel->add($inf);
while(my @ready = $sel->can_read()){
foreach my $handle (@ready){
my $line = sysreadline($handle);
print $line;
}
}
##
## deal with buffering for select. from perl cookbook 7.23
sub sysreadline {
my($handle, $timeout) = @_;
$handle = qualify_to_ref($handle, caller( ));
my $infinitely_patient = (@_ == 1 || $timeout new( );
$selector->add($handle);
my $line = "";
SLEEP:
until (at_eol($line)) {
unless ($infinitely_patient) {
return $line if time( ) > ($start_time + $timeout);
}
# sleep only 1 second before checking again
next SLEEP unless $selector->can_read(1.0);
INPUT_READY:
while ($selector->can_read(0.0)) {
my $was_blocking = $handle->blocking(0);
CHAR: while (sysread($handle, my $nextbyte, 1)) {
$line .= $nextbyte;
##print "next: [$nextbyte]\n";
last CHAR if $nextbyte eq "\n";
}
$handle->blocking($was_blocking);
# if incomplete line, keep trying
next SLEEP unless at_eol($line);
last INPUT_READY;
}
}
return $line;
}
sub at_eol($) { $_[0] =~ /\n\z/ }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
select
的返回意味着“读取不会阻塞(即永远等待某些外部事件发生)”,而不是“数据可用”。从磁盘读取文件永远不会阻塞,它在 EOF 处立即返回 0。所以你最好使用 File::Tail @TLP 建议的。
The return from
select
means "read will not block (i.e. wait forever for some external event to happen)", and not "data is available". Reading files from disk never blocks, it returns 0 immediately at EOF.So you're probably better off with File::Tail that @TLP suggests.