为什么我的父进程在退出之前看不到子进程的输出?

时间:2010-02-26 17:48:29

标签: perl ipc

考虑以下脚本:

use IO::File;
$| = 1;
my ($handle, $pid) = myPipe();
if ($pid == 0) {
  print "$$";
  sleep 5;
  exit;
}

print "child: ".<$handle>."\n";

sub myPipe {
  my $handle = new IO::File();
  my $pid = open($handle, "-|");
  return ($handle, $pid);
}

在这种情况下,进程启动后5秒内不会出现“child:”消息。如果我从分叉的孩子中删除睡眠呼叫,则立即打印。为什么分叉的孩子必须退出管道以冲洗父母?

3 个答案:

答案 0 :(得分:12)

有两个问题。首先,子进程正在缓冲其输出;第二,父进程使用<>运算符,该运算符将阻塞直到完整的一行可用,或者直到文件结束。

因此,获得您期望的结果的一种方法是让子进程在写入后立即关闭其输出流:

if ($pid == 0) {
    print "$$";
    close STDOUT;
    sleep 5;
    exit;
}

另一种方法是在子进程的输出中添加换行符,然后刷新流:

if ($pid == 0) {
    print "$$\n";
    STDOUT->flush;  # "close STDOUT;" will work too, of course
    sleep 5;
    exit;
}

刷新是必要的,因为管道(通常)是无缓冲的,而不是通过连接到终端的流来进行行缓冲。

第三种方法是将子进程的输出流设置为autoflush:

if ($pid == 0) {
    $| = 1;
    print "$$\n";
    sleep 5;
    exit;
}

答案 1 :(得分:3)

在某些(大多数?)系统上,管道默认使用I / O缓冲。放一个

$handle->autoflush(1);
您的myPipe函数中的

语句。

但即使关闭缓冲,除了换行后,Perl仍然不会刷新。因此,您可能还希望子进程在输出中包含换行符。

<小时/>

更新:测试您的代码(Cygwin,perl 5.10.0,YMMV),我发现问题是子输出中缺少换行符,而不是当autoflush是否明确打开时管道已创建。

答案 2 :(得分:2)

在任何固定的时间表上都不会冲洗管道。您可以强制管道刷新的唯一两种方法是退出子进程(这是您现在正在执行的操作),或者显式调用flush。您可以通过执行以下任一操作使您的句柄在perl中刷新:

  • \n添加到子邮件的末尾,这将(通常)导致管道刷新
  • $|设置为1,这会导致当前选定的文件句柄自动刷新
  • 使用IO::Handle并致电$handle->flush
  • 使用IO::Handle并设置$handle->autoflush = 1