调用fork()后输出的额外数据

时间:2016-10-05 14:38:56

标签: c multiprocessing fork

我正在运行以下代码,我在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是异步还是什么?我错过了什么?

3 个答案:

答案 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个进程(除非你做了有条件的事情)