【问题标题】:Perl STDIN without buffering or line bufferingPerl STDIN 没有缓冲或行缓冲
【发布时间】:2014-06-23 12:17:07
【问题描述】:

我有一个 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。我需要使用selectpoll,因为我不想锤击CPU。

请注意:我说的是标准输入,而不是标准输出。 $|++ 不是解决方案。

[编辑] 根据 cmets 和其他答案更新我的问题以澄清。

正在写入 STDOUT(在管道的另一端)的程序在每次写入后都会进行行缓冲和刷新。每次写入都包含一个换行符,因此实际上缓冲对于第一个程序的 STDOUT 来说不是问题。

为了验证这是真的,我编写了一个小型 C 程序,该程序从禁用了 STDIN 缓冲的同一程序中读取管道输入(带有 _IONBF 的 setvbuf)。输入立即出现在测试程序的 STDIN 中。可悲的是,第一个程序的输出似乎不是问题。 [/EDIT]

感谢您的任何见解!

PS。我已经做了相当多的谷歌搜索。 This link 是我找到的最接近答案的答案,但它肯定不能满足我的所有需求。

【问题讨论】:

  • 如果您将示例代码更改为以通常的方式逐行读取 STDIN,则您的示例代码不会“影响 CPU”。 CPU 使用率仅在您进行非阻塞读取时才会成为一个问题,以便程序在等待数据时可以执行其他操作。也许你真正的程序正在做一些更复杂的事情。
  • Re“我不想无限调用 sysread 而不阻塞”,为什么不调用 sysread 阻塞呢? sysread 将始终在数据到达后立即返回。 (不会解决你的问题,但它会让你的程序变得更简单。)
  • 我不能使用阻塞,因为处理的其他任务未包含在粘贴的代码中(子收获等)。这让我可以选择无限循环 sysread(没有阻塞)(如果我没记错的话)将锁定 CPU。

标签: perl io stdin buffering


【解决方案1】:

假设管道缓冲区中有两条短线。

IO::Poll 通知您有要读取的数据,您可以使用readline 继续(间接)读取这些数据。

从文件句柄中一次读取一个字符是非常低效的。因此,readline(又名<>)一次从文件句柄中读取一个数据块。这两行最终在一个缓冲区中,并返回两行中的第一行。

然后您等待 IO::Poll 通知您有更多数据。它不知道 Perl 的缓冲区;它只知道管道是空的。因此,它会阻塞。

This post 演示了这个问题。它使用 IO::Select,但原理(和解决方案)是相同的。

【讨论】:

  • 太棒了!我能够对其进行调整并解决了问题!
【解决方案2】:

您实际上是在谈论 其他程序的 STDOUT。解决方案是其他程序中的$|=1;(或等效项)。

如果你不能,你也许可以说服其他程序使用行缓冲而不是块缓冲,方法是将其 STDOUT 连接到伪 tty 而不是管道(例如,像 Expect.pm 那样)。

unix 程序expect 有一个名为unbuffer 的工具,它正是这样做的。 (它是 Ubuntu 上 expect-dev 软件包的一部分。)只需在命令名称前加上 unbuffer

【讨论】:

  • 是的,您无法控制其他程序如何处理缓冲。你必须从源头上解决这个问题。
  • @tadman,一些程序为您提供了禁用缓冲的命令行选项,但这种情况非常罕见。
  • 谢天谢地,执行输出的程序是行缓冲的。它在每行输出后进行显式刷新。写入的每一行都有一个换行符。我相信缓冲的是 STDIN。
  • 另外,我确信 STDOUT 已从主程序中刷新,因为我用 C 编写了一个测试程序,在 STDIN 上完全禁用了行缓冲并立即接收到完整数据。
  • 可能是 IO::Poll 要求您使用 sysreadreason IO::Select 相同。
猜你喜欢
  • 2011-11-28
  • 2021-12-01
  • 1970-01-01
  • 2012-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多