我一直在努力了解fork()
行为。这次是for-loop
。请注意以下代码:
#include <stdio.h>
void main()
{
int i;
for (i=0;i<3;i++)
{
fork();
// This printf statement is for debugging purposes
// getppid(): gets the parent process-id
// getpid(): get child process-id
printf("[%d] [%d] i=%d\n", getppid(), getpid(), i);
}
printf("[%d] [%d] hi\n", getppid(), getpid());
}
以下是输出:
[6909][6936] i=0
[6909][6936] i=1
[6936][6938] i=1
[6909][6936] i=2
[6909][6936] hi
[6936][6938] i=2
[6936][6938] hi
[6938][6940] i=2
[6938][6940] hi
[1][6937] i=0
[1][6939] i=2
[1][6939] hi
[1][6937] i=1
[6937][6941] i=1
[1][6937] i=2
[1][6937] hi
[6937][6941] i=2
[6937][6941] hi
[6937][6942] i=2
[6937][6942] hi
[1][6943] i=2
[1][6943] hi
我是一个非常直观的人,所以我真正理解事物的唯一方法就是通过图表。我的导师说会有8个 hi 语句。我编写并运行了代码,确实有8个 hi 语句。但我真的不明白。所以我画了下面的图表:
图表已更新以反映评论:)
i=0
个实例? i
的值被转移给每个孩子?如果i
的值相同,那么“分叉”何时停止? 2^n - 1
是否可以计算分叉的子女数?那么,n=3
,这意味着2^3 - 1 = 8 - 1 = 7
个孩子,这是正确的吗?答案 0 :(得分:37)
以下是如何理解它,从for
循环开始。
循环从父i == 0
父fork()
个,创建子1。
您现在有两个流程。两者都打印i=0
。
循环在两个进程中重新启动,现在为i == 1
。
父母和子女1 fork()
,创建儿童2和3。
您现在有四个流程。全部四个打印i=1
。
循环在所有四个进程中重新启动,现在为i == 2
。
父母和孩子1到3全部fork()
,创建4到7岁的孩子。
您现在有八个流程。全部八个打印i=2
。
循环重启所有八个进程,现在i == 3
。
循环在所有八个进程中终止,因为i < 3
不再成立。
所有八个进程都打印hi
。
所有八个进程终止。
因此,您将0
打印两次,1
打印四次,2
打印8次,hi
打印8次。
答案 1 :(得分:12)
i++
在调用fork
之后执行,因为这是for
循环的工作方式。fork
可能会失败。关于第二个问题的一点解释:
for (i = 0;i < 3; i++)
{
fork();
}
类似于:
i = 0;
while (i < 3)
{
fork();
i++;
}
所以分叉进程中的i
(父和子)都是增量前的值。但是,增量会在fork()
之后立即执行,因此在我看来,图表可能会被视为正确。
答案 2 :(得分:3)
逐一回答您的问题:
我的图表是否正确?
是的,基本上。这也是一个非常好的图表。
也就是说,如果您将i=0
等标签解释为引用完整循环迭代,那么它是正确的。然而, 所显示的图表是,在每个fork()
之后,fork()
调用之后的当前循环迭代的部分也由分叉子项执行过程
为什么输出中有
i=0
的两个实例?
因为printf()
后面有fork()
,所以它由父进程和刚分叉的子进程执行。如果您在printf()
之前移动fork()
,它将仅由父项执行(因为子进程尚不存在)。
在
i
之后,fork()
对每个孩子的价值是多少?如果结转i
的相同值,那么“分叉”何时停止?
i
的值不会被fork()
更改,因此子进程看到的值与其父进程相同。
关于fork()
要记住的事情是它被调用一次,但它返回两次 - 一次在父进程中,一次在新克隆的子进程中。
有关更简单的示例,请考虑以下代码:
printf("This will be printed once.\n");
fork();
printf("This will be printed twice.\n");
fork();
printf("This will be printed four times.\n");
fork();
printf("This will be printed eight times.\n");
由fork()
创建的子进程是其父级的(几乎)精确克隆,因此,从它自己的角度来看,它“记住”作为其父级,继承所有父进程的状态(包括所有变量值,调用堆栈和正在执行的指令)。唯一的直接差异(除了系统元数据,例如getpid()
返回的进程ID)是fork()
的返回值,它在子进程中为零但非零(实际上是ID)父母的子进程)。
总是这样
2^n - 1
是计算分叉子女数量的方法吗?那么,n=3
,这意味着2^3 - 1 = 8 - 1 = 7
个孩子,这是正确的吗?
执行fork()
的每个进程都会变成两个进程(在异常错误情况下,fork()
可能会失败)。如果父和子继续执行相同的代码(即他们不检查fork()
的返回值,或者他们自己的进程ID,并根据它分支到不同的代码路径),那么每个后续的fork将加倍进程数。所以,是的,在三把叉之后,你最终会得到2³= 8个进程。