从视觉上来看,For循环中fork()会发生什么
我一直在尝试理解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声明。 我编写并运行了代码,实际上有8个hi语句。 但是我真的不明白。 所以我画了下图:
图更新,以反映评论:)
观察:
- 父进程(main)必须迭代循环3次。 然后printf被调用
- 在父循环的每次迭代中,调用fork()
- 在每次fork()调用之后,i都会增加,所以每个孩子在增加之前都会从i开始一个for循环
- 在每个for循环结束时,打印“hi”
这是我的问题:
- 我的图是否正确?
- 为什么输出中有两个
i=0
实例? - 在fork()之后,
i
将什么值传递给每个孩子? 如果i
继续使用相同的值,那么“分叉”何时停止? -
2^n - 1
是否会成为计算分叉子女数量的一种方法? 那么这里n=3
,这意味着2^3 - 1 = 8 - 1 = 7
孩子,这是否正确?
以下是如何理解它,从for
循环开始。
-
循环开始在父母,
i == 0
-
父
fork()
,创build子项1。 -
你现在有两个进程。 都打印
i=0
。 -
循环在两个进程中重新启动,现在
i == 1
。 -
父母和孩子1
fork()
,创build孩子2和3。 -
你现在有四个进程。 所有四个打印
i=1
。 -
循环在所有四个进程中重新启动,现在
i == 2
。 -
父母和孩子1到3都是
fork()
,创build孩子4到7。 -
你现在有八个进程。 全部八个打印
i=2
。 -
循环重新启动所有八个进程,现在
i == 3
。 -
循环在所有八个进程终止,因为
i < 3
不再是真实的。 -
所有八个进程打印
hi
。 -
所有八个进程终止。
所以你打印两次,一次打印四次, 2
打印八次,打印八次。
- 是的,这是正确的。 (见下文)
- 不,
i++
是在调用fork
之后执行的,因为这是for
循环的工作方式。 - 如果一切顺利,是的。 但是,请记住
fork
可能会失败。
第二个小解释:
for (i = 0;i < 3; i++) { fork(); }
类似于:
i = 0; while (i < 3) { fork(); i++; }
所以i
在分叉进程(父母和孩子)是增量前的值。 然而, fork()
之后立即执行增量,所以在我看来,图可以被视为正确的。
要一一解答你的问题:
我的图是否正确?
是的,基本上。 这也是一个非常好的图表。
也就是说,如果将i=0
等标签解释为指全循环迭代,那么这是正确的。 然而,图表没有显示的是,在每个fork()
之后, fork()
调用之后的当前循环迭代的部分也由分叉subprocess执行。
为什么输出中有两个
i=0
实例?
因为在fork()
printf()
后面有printf()
,所以它由父进程和刚分叉的subprocess执行。 如果在fork()
printf()
之前移动printf()
,则只会由父级执行(因为subprocess尚不存在)。
在
fork()
之后,i
将什么值传递给每个孩子? 如果i
继续使用相同的值,那么“分叉”何时停止?
i
的值没有被fork()
改变,所以subprocess和父进程有相同的值。
关于fork()
一点是它被调用一次,但是它会返回两次,一次在父进程中,一次在新克隆的subprocess中。
更简单的例子,请考虑以下代码:
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()
创build的subprocess是其父进程的一个(几乎)精确的克隆,因此,从它自己的angular度来看,它“记住”它的父进程,inheritance所有父进程的状态(包括所有variables值,堆栈和正在执行的指令)。 唯一的直接差别(除了系统元数据,如getpid()
返回的进程ID)是fork()
的返回值,在subprocess中它将为零,但非零(实际上是subprocess的ID )在父母。
2^n - 1
是否会成为计算分叉子女数量的一种方法? 那么这里n=3
,意思是2^3 - 1 = 8 - 1 = 7
孩子,这是否正确?
执行fork()
每个进程都会变成两个进程(除非在不正常的错误情况下fork()
可能会失败)。 如果父母和孩子继续执行相同的代码(即,他们不检查fork()
的返回值或他们自己的进程ID,并基于它分支到不同的代码path),那么每个后续的fork将会使数字加倍的过程。 所以,是的,三叉之后,最终会有2 3 = 8个进程。