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

C 为什么我的分叉程序的输出在管道输出时不同?,c,linux,bash,pipe,fork,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());

我正在研究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());
}
#包括
#包括
int main(){
如果(fork()!=0){
printf(“%d:X\n”,getpid());
}
如果(fork()!=0){
printf(“%d:Y\n”,getpid());
}
printf(“%d:Z\n”,getpid());
}
我用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

编辑-遗漏了一些新行


发生了什么事?

我想我知道发生了什么事。当输出为tty时,stdio缓冲区与管道或文件时不同。子进程继承父缓冲区。当它们被刷新时,可以获得双倍的输出

如果你加上

fflush(stdout);
每次调用
printf()
之后,您就会明白我的意思


有趣的是,当标准输出是tty设备时,情况就不同了。可能是库知道这意味着什么,并在每次换行或类似的情况下进行刷新。

不同进程写入其输出的顺序完全不可预测。因此,唯一令人惊讶的是,有时“X”print语句有时会出现两次

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


我能够复制多个“X”行,但是如果我添加
fflush(stdout)
就在第二个
fork()
之前,我总是只看到一个“X”行,而且总是总共7行。

所以我想你可能想知道为什么会得到多个“X”

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


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

如果在分叉之前使用了
stdout
,则必须
fork()
之前调用
fflush(stdout)
(同样,对于您使用的任何其他输出
文件
)。否则将导致未定义的行为。您看到的效果来自于
stdout
在连接到终端时被行缓冲,但在连接到管道时被完全缓冲。这不是必需的,但标准(POSIX)建议这样做。

为什么要将结果通过管道传输到任何东西(特别是“cat”,它实际上什么都做不了)?-哦,等等,是管道引入了奇怪。。。嗯…我可以复制这个,所以它不是宇宙射线或任何东西。有趣的注意,前一个输出中缺少一个PID(2056);这可能是
cat
的PID。我运行了几次,其中大部分都没有缺少PID。有一刻,我认为这里使用“forking”是为了替代另一个词。的确!放置
fflush(stdout)printf
后,code>解决它。输出缓冲区的内容在fork时被复制,因此当缓冲区被刷新时,相同的缓冲输出会被打印两次。这是正确的<默认情况下,当写入交互终端时,code>stdout是行缓冲的(因此在每次
\n
之后刷新),但在写入管道时完全缓冲。单个
fflush(stdout)fork()之前的code>也将起作用。