我有一个Perl脚本,它接收来自另一个程序的输入。它使用8k(Ubuntu默认值)输入缓冲区进行缓冲,这会导致问题。我想使用行缓冲或完全禁用缓冲。它看起来不像是一个很好的方法。有什么建议吗?
use IO::Handle;
use IO::Poll qw[ POLLIN POLLHUP POLLERR ];
use Text::CSV;
my $stdin = new IO::Handle;
$stdin->fdopen(fileno(STDIN), 'r');
$stdin->setbuf(undef);
my $poll = IO::Poll->new() or die "cannot create IO::Poll object";
$poll->mask($stdin => POLLIN);
STDIN->blocking(0);
my $halt = 0;
for(;;) {
$poll->poll($config{poll_timout});
for my $handle ($poll->handles(POLLIN | POLLHUP | POLLERR)) {
next unless($handle eq $stdin);
if(eof) {
$halt = 1;
last;
}
my @row = $csv->getline($stdin);
# Do more stuff here
}
last if($halt);
}
轮询STDIN会引发一些问题,因为IO :: Poll使用缓冲和sysread这样的直接调用(并且它们不能混合)。我不想在没有阻塞的情况下无限地调用sysread。我需要使用select
或poll
,因为我不想锤击CPU。
请注意:我说的是STDIN,而不是STDOUT。 $ | ++不是解决方案。
[编辑] 根据评论和其他答案更新我的问题以澄清。
写入STDOUT的程序(在管道的另一端)是每次写入后进行行缓冲和刷新的程序。每个写入都包含一个换行符,因此实际上,缓冲不是第一个程序的STDOUT的问题。
为了验证这是真的,我写了一个小的C程序,它读取来自同一程序的管道输入,禁用STDIN缓冲(带有_IONBF的setvbuf)。输入立即出现在测试程序的STDIN中。遗憾的是,它似乎不是第一个程序输出的问题。 的 [/编辑]
感谢您的任何见解!
PS。我做了相当多的谷歌搜索。 This link是我找到答案的最接近的,但它肯定不能满足我的所有需求。
答案 0 :(得分:3)
您实际上是在谈论其他程序的STDOUT 。其他程序中的解决方案是$|=1;
(或等效的)。
如果你不能,你可以通过将其STDOUT连接到伪tty而不是管道(例如Expect.pm)来说服其他程序使用行缓冲而不是块缓冲。
unix程序expect
有一个名为unbuffer
的工具,它就是这样做的。 (它是Ubuntu上expect-dev
包的一部分。)只需在命令名前加上unbuffer
。
答案 1 :(得分:2)
假设管道缓冲区中有两条短线。
IO :: Poll通知您要读取的数据,您可以使用readline
继续(间接)读取数据。
从文件句柄一次读取一个字符是非常低效的。因此,readline
(又名<>
)一次从文件句柄中读取一个数据块。这两行最终在一个缓冲区中,并返回两行中的第一行。
然后等待IO :: Poll通知您有更多数据。它不了解Perl的缓冲区;它只知道管道是空的。因此,它会阻止。
This post演示了这个问题。它使用IO :: Select,但原理(和解决方案)是相同的。