fork()分支数目超出预期?

188

考虑以下代码:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
    int i;
    for(i = 0; i < 2; i++)
    {
        fork();
        printf(".");
    }
    return 0;
}

这个程序输出了8个点。这怎么可能呢?难道不应该是6个点吗?


请查看此帖子:https://dev59.com/IWgv5IYBdhLWcg3wBMOg#10909090 - tuxuday
14
http://ideone.com/B9HXL - Antonio Pérez
3个回答

248
fork()原语常常让人感到困惑。 在您了解它之前,应在纸上跟踪每个操作并记录进程数量。 不要忘记fork()会创建当前进程的一个近乎完全相同的副本。 对于大多数目的而言,最显着的区别在于fork()的返回值在父进程和子进程之间不同。(由于此代码忽略返回值,因此这没有影响。)
所以,一开始只有一个进程。 它创建第二个进程,两个进程都打印一个点并循环。 在第二次迭代时,每个进程都创建另一个副本,因此有四个进程打印一个点,然后退出。 因此,我们可以轻松地解释为什么有六个点,就像您预期的那样。
但是,printf()真正做的是缓冲其输出。 因此,当只有两个进程时的第一个点在写入时不会出现。 这些点保留在缓冲区中,并在fork()时复制。 直到该进程即将退出时,缓冲的点才会出现。 四个进程打印一个缓冲的点,再加上新的点,总共有8个点。
如果你想避免这种行为,请在printf()之后调用fflush(stdout);

13
谢谢,我不知道缓冲区会在 fork() 中产生副本。这解释了一个奇怪的行为。 - Nikolay Kovalenko
2
这不应该是10个点而不是8个吗?因为第四代子进程继承了缓冲的点,添加了自己的点,然后在退出时刷新,他们将打印总共8个点,但是前两个一代进程仍然有一个点被缓冲,并在退出时刷新,从而总共给出10个点。 - psusi
12
其中一个第二代进程实际上是第一代进程。fork()函数并不会创建两个进程然后退出,它只会创建一个新的进程。 - Izkata

70

你的输出流中有未提交的缓冲区。stdout是行缓冲的,并且该缓冲区会随着进程的其余部分一起复制。当程序终止时,这个未提交的缓冲区将被写入两次(每个进程写入一次)。双方都使用。

printf("a\n");

并且

printf("a "); fflush(stdout);

在您的第一个示例中,您创建了四个进程,每个进程的输出流缓冲区中都有两个点。当每个流终止时,它会刷新其缓冲区,生成八个点。

不要展示这个问题。


2

当 i=0 时

进程 1:缓冲文本= 1 个点

进程 2(由进程 1 创建):缓冲文本= 1 个点

当 i=1 时

进程 3(由进程 1 创建):从进程 1 继承 1 个缓冲点并自己打印 1 个点。进程 3 总共打印 2 个点。

进程 4(由进程 2 创建):从进程 2 继承 1 个缓冲点并自己打印 1 个点。进程 4 总共打印 2 个点。

进程 1:打印 2 个点(i=0 时有一个缓冲点和 i=1 时的另一个点)

进程 2:打印 2 个点(i=0 时有一个缓冲点和 i=1 时的另一个点)

最终输出:8 个点。 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接