我正在运行以下代码,我在Mac上用c编写。
#include <unistd.h>
#include <stdio.h>
int main(int argc, const char * argv[]) {
int i = 0;
printf("Starting pid:%d\n", getpid());
while(i++<3){
int ret = fork();
printf("hello(%d)%d, %d ", i, getpid(), ret);
if(!ret){
printf("\n");
return 0;}
}
}
我想到的目标是总共有6张照片 - 来自原始父母的3张(打印在“Starting pid”中的pid),以及3个衍生儿童中的每一个。相反,我收到的是:
Starting pid:7998
hello(1)8009, 0
hello(1)7998, 8009 hello(2)8010, 0
hello(1)7998, 8009 hello(2)7998, 8010 hello(3)7998, 8011 hello(1)7998, 8009 hello(2)7998, 8010 hello(3)8011, 0
请注意,原始父级(7998)打印6次(当i == 1时为3次,i == 2时为2次,i == 3时为1次)。
为什么会这样?为什么我没有从父母那里收到3份照片(一次当i == 1,i == 2,i == 3)。如果我们从父母的角度来看它 - 有一个while循环,我们应该只打3次内部的print语句。
也许是fork是异步还是什么?我错过了什么?
答案 0 :(得分:7)
当您fork
进程时,父级中的内存在子级中重复。这包括stdout
缓冲区。
从父级循环的第二次迭代开始,缓冲区中有hello(1)7998, 8009
。当第二个孩子分叉时,这个文本仍然在那个孩子的缓冲区中。然后第二个孩子打印hello(2)8010, 0
,然后打印换行符。换行刷新孩子的缓冲区,所以第二个孩子打印出来:
hello(1)7998, 8009 hello(2)8010, 0
在fork之后,父进程打印hello(2)7998, 8010
,它被添加到输出缓冲区。然后在第三次迭代中,第三个子节点继承它并打印:
hello(1)7998, 8009 hello(2)7998, 8010 hello(3)8011, 0
父母然后打印hello(3)7998, 8011
,再次添加到缓冲区。然后父节点退出循环并退出,之后刷新缓冲区并输出父节点:
hello(1)7998, 8009 hello(2)7998, 8010 hello(3)7998, 8011
要纠正此问题,父进程需要在分叉之前清除输出缓冲区。
这可以通过在致电fflush(stdout)
后致电printf
,或者在\n
的末尾添加新行(printf
)来完成。
编辑:
作为一种更通用的解决方案,调用fflush(NULL)
将刷新所有打开的输出流。
答案 1 :(得分:3)
我认为您所看到的只是在stdout
上复制了我将最终写入fork
缓冲区的进程本地内容,因此然后,在fork之前排队的内容将由多个进程独立输出。
分叉正在按照你的想法行事,衡量工具是有缺陷的。
我修改了你的代码以添加一个flush:
int ret = fork();
printf("hello(%d)%d, %d ", i, getpid(), ret);
fflush(stdout);
这可确保printf
的结果不会位于进程本地缓冲区中。它直接写入stdout
然后清空本地缓冲区。然后输出成为:
Starting pid:41731
hello(1)41731, 41732 hello(2)41731, 41733 hello(1)41732, 0
hello(3)41731, 41734 hello(2)41733, 0
hello(3)41734, 0
即。预期的6个电话。
(我在Mac上使用CodeRunner来测试它;它还显示了表面上没有flush
的九个结果,因此我确信我复制了初始条件。)
答案 2 :(得分:0)
Fork将进程分成两部分。在每个子进程中。在3个叉子之后你最终得到8个进程(除非你做了有条件的事情)