将打印多less个XD?

当我和朋友一起玩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级完整二叉树中的节点数。 但是,当我们删除printf中的“\ n”时,我们又得到了另一个完全不同的结果:

xdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxdxd

它给了我64个XD(我的朋友在他的机器上有56个XD)。 这似乎是一个有点“稳定”的结果,因为我可以运行多次,它给了我相同的结果(和我的朋友一样)。

我试图把printf(“xd”)改成perror(“xd”),它给了我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个XDs。
我敢打赌,这与输出缓冲有关,但我不能解释这一点。
我有两个天真的猜测,一个被认为是经典的并发问题,但我很快就否认了fork()创build另一个进程而不是线程,每个subprocess实际上持有不同的副本。 (如果我错了,请纠正我)
另一个天真的猜测是,当多个进程写入同一个stdout时,紧接在缓冲区中的内容已经显示在屏幕上之前,但是在从缓冲区中清除之前,如果另一个进程向缓冲区写入内容,它将停止清除它/考虑整个缓冲区是有效的。
这两个很可能是错误的,因为我对Linux的实现知之甚less,有谁能帮我解释一下吗?

您所遵循的行为是因为缓冲模式。

由于最后没有换行符,输出以线路缓冲模式(或全缓冲模式)运行,所以您看到的输出是不同的

你必须使用fflush(0); 在分叉之前清空所有的I / O缓冲区

您可以通过标准C函数setvbuf()_IOFBF (完全缓冲), _IOLBF (行缓冲)和_IONBF (无缓冲)模式来控制缓冲模式。

标准输出是线性缓冲的,所以在第一个版本中,所有的printf将被立即打印:

 i=0: 1 xd i=1: 2 xd i=2: 4 xd i=3: 8 xd in sum: 15 xd 

另一方面没有\n的标准输出缓冲区将复制每个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将被打印。