我在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,并将其传送到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
编辑 - 错过了一些换行符
发生了什么事?
答案 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)推荐。