【问题标题】:Perl bidirectional pipe IPC, how to avoid output bufferingPerl双向管道IPC,如何避免输出缓冲
【发布时间】:2012-09-20 12:49:25
【问题描述】:

我正在尝试与交互式进程进行通信。我希望我的 perl 脚本成为用户和进程之间的“模块人”。该过程将文本放入标准输出,提示用户输入命令,将更多文本放入标准输出,提示用户输入命令,......提供原始图形:

 User <----STDOUT---- interface.pl <-----STDOUT--- Process
 User -----STDIN----> interface.pl ------STDIN---> Process
 User <----STDOUT---- interface.pl <-----STDOUT--- Process
 User -----STDIN----> interface.pl ------STDIN---> Process
 User <----STDOUT---- interface.pl <-----STDOUT--- Process
 User -----STDIN----> interface.pl ------STDIN---> Process

以下模拟了我正在尝试做的事情:

    #!/usr/bin/perl

    use strict;
    use warnings;

    use FileHandle;
    use IPC::Open2;
    my  $pid = open2( \*READER, \*WRITER, "cat -n" );
    WRITER->autoflush(); # default here, actually
    my $got = "";
    my $input = " ";

    while ($input ne "") {
            chomp($input = <STDIN>);
            print WRITER "$input \n";
            $got = <READER>;
            print $got;
    }

由于输出缓冲上述示例不起作用。无论输入什么文本,或者按了多少输入,程序都在那里。解决方法是发出:

    my  $pid = open2( \*READER, \*WRITER, "cat -un" );

注意“cat -un”而不是“cat -n”。 -u 关闭 cat 上的输出缓冲。关闭输出缓冲时,此方法有效。我试图与最有可能的缓冲区输出交互的过程,因为我面临与“cat -n”相同的问题。不幸的是,我无法关闭正在与之通信的进程的输出缓冲,那么我该如何处理这个问题?

UPDATE1(使用 ptty):

    #!/usr/bin/perl

    use strict;
    use warnings;

    use IO::Pty;
    use IPC::Open2;

    my $reader = new IO::Pty;
    my $writer = new IO::Pty;

    my  $pid = open2( $reader, $writer, "cat -n" );
    my $got = "";
    my $input = " ";

    $writer->autoflush(1);

    while ($input ne "") {
            chomp($input = <STDIN>);
            $writer->print("$input \n");
            $got = $reader->getline;
            print $got;
    }

~

【问题讨论】:

  • open2 总是打开它自己的管道,你不能给它 ptys。有关使用 IO::Pty::Easy 的简单示例,请参阅 perlmonks.org/?node_id=835953

标签: perl ipc pipe output-buffering tty


【解决方案1】:

缓冲分为三种:

  1. 块缓冲:输出被放置在一个固定大小的缓冲区中。缓冲区满时会被刷新。您会看到输出以块的形式出现。
  2. 行缓冲:输出被放置在一个固定大小的缓冲区中。当一个换行符被添加到缓冲区并且它变满时,缓冲区会被刷新。
  3. 无缓冲:输出直接传递给操作系统。

在 Perl 中,缓冲的工作方式如下:

  • 默认情况下缓冲文件句柄。一个例外:默认情况下不缓冲 STDERR。
  • 使用块缓冲。一个例外:当且仅当 STDOUT 连接到终端时,它才会被行缓冲。
  • 从 STDIN 读取会刷新 STDOUT 的缓冲区。
  • 直到最近,Perl 才使用 4KB 缓冲区。现在,默认值为 8KB,但可以在构建 Perl 时更改。

前两个在所有应用程序中都是令人惊讶的标准。这意味着:

  • User -------&gt; interface.pl

    用户是一个人。他没有说缓冲,尽管它是一个非常缓慢的数据源。 好的

  • interface.pl ----&gt; Process

    interface.pl 的输出是块缓冲的。 不好

    通过将以下内容添加到interface.pl 来修复:

    use IO::Handle qw( );
    WRITER->autoflush(1);
    
  • Process ----&gt; interface.pl

    进程的输出是块缓冲的。 不好

    通过将以下内容添加到Process 来修复:

    use IO::Handle qw( );
    STDOUT->autoflush(1);
    

    现在,您可能会告诉我您无法更改 Process。如果是这样,那么您有三个选择:

    • 使用工具提供的命令行或配置选项来更改其缓冲行为。我不知道有什么工具可以提供这样的选项。
    • 通过使用pseudo tty 而不是管道来欺骗孩子使用行缓冲而不是块缓冲。
    • 退出。

  • interface.pl -------&gt; User

    interface.pl 的输出是行缓冲的。 好的(对吗?)

【讨论】:

  • 感谢您非常详细的解释。我一直在谷歌搜索,看到很多解释讨论使用 ptty。 ptty 上的示例甚至 cPan 文档都非常模棱两可。我还没有采纳你的建议(还),但在阅读本文之前,我将我的代码“转换”为使用 ptty,但它仍然无法正常工作。你能告诉我我是否“做对了”,或者使用我上面的代码为我提供一个例子。
  • 我将研究 IO::Pty::Easy。只是想知道块缓冲有什么问题?为什么进程只是坐在那里而不是发送块?我不在乎它是逐行返回还是作为一个块返回。您是否暗示管道本质上是块缓冲的?如果是这样,为什么 "awk '{print $2}' | df -h | grep "SOMETHING" 起作用?为什么这些不会因为缓冲而挂起?
  • 唯一的问题是你说你不想要它。 // 他们不是坐在那里不发送,而是坐在那里等待输入。 // 不,缓冲的是程序。 // df 不会挂起等待输入,因为 df 不接受任何输入。与你所说的相反,grep 确实挂起等待输入(因为df 的输出肯定是块缓冲的,但df 完成得非常快,所以grep 只挂了一眨眼。跨度>
  • 那为什么原来的程序不起作用呢?如果 Process 坐在那里等待输入,并且 interface.pl 通过 stdout 发送它,则不应处理将其作为 STDIN,做它的事情,并将输出(作为块、行或其他方式)吐出 stdOUT for interface.pl接收它的标准输入?
  • 进程坐在那里等待输入,因为 interface.pl 目前没有发送任何内容。 STDIN 没有什么可取的。如果有,它会接受的。 interface.pl 最终会将 4K 或 8K 放入管道中,并且 Process 将在它发生后立即开始读取。
猜你喜欢
  • 2014-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-19
  • 2021-01-29
  • 1970-01-01
相关资源
最近更新 更多