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

Squ*_*dly 8 c linux bash fork pipe

我在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());
}
Run Code Online (Sandbox Code Playgroud)

我用gcc编译它,然后从Emacs内部运行a.out cat,并将它管道输送到grep .,然后得到它.

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

编辑 - 错过了一些换行符

这是怎么回事?

asc*_*ler 11

不同进程写入其输出的顺序完全不可预测.所以唯一令人惊讶的是,有时"X"打印语句有时会发生两次.

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

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


Poi*_*nty 8

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

如果你添加

fflush(stdout);
Run Code Online (Sandbox Code Playgroud)

每次printf()通话结束后,你会明白我的意思.

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


Dig*_*oss 6

所以我想你会想知道为什么你会得到一个以上的"X"?

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

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