当我管道输出时,为什么我的分叉程序的输出不同?

时间:2011-01-20 17:00:59

标签: c linux bash pipe fork

我在fork上看了一些简单的代码,并决定自己尝试一下。我编译然后从Emacs内部运行它,并获得了一个不同的输出到通过在Bash中运行它产生的输出。

#include <unistd.h>
#include <stdio.h>

int main() {
  if (fork() != 0) {
    printf("%d: X\n", getpid());
  }

  if (fork() != 0) {
    printf("%d: Y\n", getpid());
  }

  printf("%d: Z\n", getpid());
}

我使用gcc编译它,然后从Emacs内部运行a.out,并将其传送到catgrep .,并得到了它。

  

2055:X
  2055:Y
  2055:Z
  2055:X
  2058:Z
  2057:Y
  2057:Z
  2059:Z

这是不对的。我只是从Bash运行它(我预期)

  2084:X
  2084:Y
  2084:Z
  2085:Y
  2085:Z
  2087:Z
  2086:Z

编辑 - 错过了一些换行符

发生了什么事?

4 个答案:

答案 0 :(得分:11)

不同进程编写输出的顺序完全不可预测。所以唯一令人惊讶的是,有时“X”打印声明有时会发生两次。

我相信这是因为有时在第二个fork(),包含“X”的输出行在输出缓冲区中,需要刷新。所以这两个过程最终打印出来。由于getpid()已被调用并转换为字符串,因此它们将显示相同的pid。

我能够重现多条“X”行,但如果我在第二行fflush(stdout);之前添加fork(),我总是只能看到一条“X”线,总共只有7条线。

答案 1 :(得分:8)

我想我知道发生了什么事。当输出是tty而不是管道或文件时,stdio缓冲将是不同的。子进程继承父缓冲区。当他们被冲洗时,你可以获得双倍输出。

如果你添加

fflush(stdout);

每次printf()来电后,你会看到我的意思。

有趣的是,当标准输出是tty设备时,它会有所不同。可能是库知道这意味着什么,并在每次换行后刷新,或类似的东西。

答案 2 :(得分:6)

所以我想你会想知道为什么你会得到不止一个“X”?

这是因为缓冲输出被刷新两次。

当您管道程序的输出时,stdio库会识别出您的输出不是终端,并且它会切换到块缓冲而不是行缓冲。因此,当进程分叉时,还没有任何输出,因此现在父进程和子进程都有待处理的输出。

答案 3 :(得分:3)

如果您在分叉之前完全使用过stdout,那么必须fflush(stdout)之前调用fork()(同样适用于任何其他输出FILE你用的)。如果不这样做会导致未定义的行为。当您连接到终端时,您看到的效果来自stdout 行缓冲,但当它连接到管道时完全缓冲。这不是必需的,但是标准(POSIX)推荐。