当我和我的一个朋友一起玩fork()时,我遇到了这个奇怪的问题。
非常简单的POC代码喜欢:
int main(int argc, char** argv)
{
int i = 0;
for(i=0; i<4; i++) {
printf("xd\n");
fork();
}
return 0;
}
我得到了一个漂亮的输出:
xd
xd
xd
xd
xd
xd
xd
xd
xd
xd
xd
xd
xd
xd
xd
xd已被打印15次,这是我所期望的 - 4级完整二叉树中的节点数。
但是,当我们删除&#34; \ n&#34;在printf中,我们得到了另一个完全不同的结果:
xdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxd
它给了我64个XD(我的朋友在他的机器上有56个XD)。这似乎有点'稳定'#34;结果,因为我可以多次运行它,它给我相同的结果(与我的朋友相同)。
我尝试将printf(&#34; xd&#34;)更改为perror(&#34; xd&#34;),它给了我15个输出:
xd: Success
xd: Success
xd: Success
xd: Success
xd: Success
xd: Success
xd: Success
xd: Success
xd: Success
xd: Success
xd: Success
xd: Success
xd: Success
xd: Success
xd: Success
我试过了
for(i=0; i<4; i++) {
printf("xd");
fflush(stdout);
fork();
}
这将在一行中给我15个XD。
我敢打赌这与输出缓冲区有关,但我无法解释这一点。
我有两个天真的猜测,一个是将其视为经典并发问题,但我很快就否认它,因为fork()创建另一个进程而不是线程,每个子进程实际上持有i的不同副本。 (如果我错了,请纠正我)
另一个天真的猜测是,当多个进程写入同一个stdout时,就在缓冲区中的内容显示在屏幕上但之前已从缓冲区清除之前,如果另一个进程将某些内容写入缓冲区,它将停止清理它/将整个缓冲区视为有效。
这两个都很可能是错误的,因为我对Linux实现知之甚少,有人可以帮我解释一下吗?
答案 0 :(得分:2)
您关注的行为是因为缓冲模式。
因为最后没有换行符,并且输出以行缓冲模式运行(或者 全缓冲模式)这就是为什么你看到的输出是不同的
在分叉之前必须使用fflush(0);
清空所有I / O缓冲区
您可以通过标准C函数setvbuf()
和_IOFBF
(完全缓冲),_IOLBF
(行缓冲)和_IONBF
(无缓冲)来控制缓冲模式模式。
答案 1 :(得分:1)
stdout是linebuffered,因此在第一个版本中,所有printf
将立即打印出来:
i=0: 1 xd
i=1: 2 xd
i=2: 4 xd
i=3: 8 xd
in sum: 15 xd
另一边没有\n
stdout缓冲区的将每fork()
复制一次,因此将xd计数加倍。
i=0: 1 xd, after fork 2 xd and 2 processes
i=1: 4 xd, after fork 8 xd and 4 processes
i=2: 12 xd, after fork 24 xd and 8 processes
i=3: 32 xd, after fork 64 xd and 16 processes
在主过程结束时,将打印所有64 xd。