我正在学习C语言中的fork函数,并且正在学习过去的考试,并且遇到了一个非常有趣的问题。
请参阅以下代码:
int main(void) {
printf("Hello ");
fork();
printf("Hello ");
fork();
printf("Hello \n");
pause();
return 0;
}
结果输出
Hello Hello Hello
Hello Hello Hello
Hello Hello Hello
Hello Hello Hello
如果我将代码改为
fork();
fork();
printf("Hello ");
printf("Hello ");
printf("Hello \n");
输出结果仍然相同。 为什么fork函数的位置对输出本身没有影响。 fork在执行之前是否等待所有printf()完成?这应该解释输出,因为第一次printf()将打印出Hello的第一行,然后它将被分叉并且将打印另外1行Hello。然后第二个分叉将分叉前两行并生成另外两行Hello。
然而,问题只是稍微改变了代码,整个输出发生了巨大的变化。
printf("Hello \n");
fork();
printf("Hello ");
fork();
printf("Hello \n");
这导致输出
Hello
Hello Hello
Hello Hello
Hello Hello
Hello Hello
我不明白为什么只添加一个' \ n'将产生5行输出而不是4。这次,如果我像以前一样将fork函数的位置更改为顶部,输出也会改变。看来fork函数没有效果这次是printf的第一行。有人可以向我解释一下fork函数输出的确切内容吗?
答案 0 :(得分:4)
问题不在于顺序,这是无关紧要的,因为我们正在处理fork()
并且您没有关于两个同时线程的并发性的保证。
问题是程序根据程序的语义打印了一个不正确的Hello
。如果你考虑一下
printf
仅由父进程执行printf
由父进程和子进程执行printf
由父进程,子进程以及父母和第一个孩子的两个子女执行这会产生总共7个printf,而你得到12或11.问题在于,当你在{{fork()
内部调用某个内容时,输出是按行缓存的。 1}}缓冲然后发生奇怪的事情。如果您将代码更改为:
stdout
您将获得:
#include <unistd.h>
int main(void) {
printf("(1:%u)\n", getpid());
fork();
printf("(2:%u)\n", getpid());
fork();
printf("(3:%u)\n", getpid());
pause();
return 0;
}
这是正确的,因为现在我们在cuboid:Dev jack$ ./a.out
(1:4307)
(2:4307)
(3:4307)
(2:4308)
(3:4309)
(3:4308)
(3:4310)
的每次调用中强制刷新(\n
),并且pid对应于我们之前看到的内容:1,2,4次调用。< / p>
如果在代码中调用printf
之前强行刷新,例如:
fork()
您将获得:
int main(void) {
printf("1Hello ");
fflush(stdout);
fork();
printf("2Hello ");
fflush(stdout);
fork();
printf("3Hello \n");
pause();
return 0;
}
产生相同的正确结果,您可以看到父进程通过所有3行,第一个子进行第2和第3次,而最后两个子进程仅通过最后一个。
答案 1 :(得分:2)
您使用printf()
遇到了缓冲I / O的特殊副作用。通常,如果有换行符,printf()
实际上只会将文本打印到控制台,否则它将在内部缓冲该文本,等待下一个换行符。
在你的情况下发生的事情是前两个printf()
调用没有进入控制台而是保持缓冲状态。分叉程序时,内部缓冲区也是分叉的 - 意味着它的内容也被复制到新程序中。因此,当他们做最后的printf()
时,该程序的所有副本都有“你好”的三份副本。
如果你用fflush(0)
强制文本输出到控制台,你应该得到你期望的行为(有点):
int main(void) {
printf("Hello "); fflush(0);
fork();
printf("Hello "); fflush(0);
fork();
printf("Hello \n"); fflush(0);
pause();
return 0;
}
导致
Hello Hello Hello
Hello Hello
Hello
Hello
或
Hello Hello Hello Hello
Hello
Hello
Hello
你也可以得到其他组合......
您仍然在程序的不同副本之间进行竞赛,以查看谁先完成并打印相对于其他人的换行符。