视觉上For循环中fork()会发生什么

luc*_*old 63 c 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());
}
Run Code Online (Sandbox Code Playgroud)

这是输出:

[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
Run Code Online (Sandbox Code Playgroud)

我是一个非常有视觉的人,所以我真正理解事物的唯一方法就是通过图表.我的导师说会有8个hi语句.我编写并运行了代码,确实有8个hi语句.但我真的不明白.所以我画了下面的图:

在此输入图像描述

更新图表以反映评论:)

观察:

  1. 父进程(main)必须迭代循环3次.然后调用printf
  2. 在父for循环的每次迭代中,调用fork()
  3. 在每次fork()调用之后,i递增,因此每个子函数在递增之前从i开始for循环
  4. 在每个for循环结束时,打印"hi"

这是我的问题:

  • 我的图表是否正确?
  • 为什么输出中有两个实例i=0
  • i在fork()之后,每个孩子有什么价值?如果相同的值i被转移,那么"分叉"何时停止?
  • 是否总是2^n - 1这样可以计算分叉的子女数量?所以,这里n=3,这意味着2^3 - 1 = 8 - 1 = 7孩子,这是正确的吗?

Cro*_*man 38

for循环开始,这是如何理解它的.

  1. 循环从父级开始, i == 0

  2. 父母fork(),创造孩子1.

  3. 您现在有两个进程.两个都打印i=0.

  4. 现在循环在两个进程中重新启动i == 1.

  5. 父母和孩子1 fork(),创造孩子2和3.

  6. 您现在有四个流程.全部四个打印i=1.

  7. 现在循环在所有四个进程中重新启动i == 2.

  8. 父母和孩子1到3全部fork(),创造4到7岁的孩子.

  9. 你现在有八个进程.所有八个打印i=2.

  10. 现在循环在所有八个进程中重新启动i == 3.

  11. 循环终止于所有八个进程,i < 3不再如此.

  12. 打印所有八个流程hi.

  13. 所有八个进程终止.

所以你0打印两次,1打印四次,2打印8次,hi打印8次.

  • 是的,`fork()`只是复制过程,它们都以同样的方式继续前进.*他们两个刚刚从`fork()`调用*返回,并且在他们下次遇到对`fork()`的调用之前不再进行另一次调用.唯一的区别是`fork()`在子节点中返回0,在父节点中返回其他内容,但就每个进程而言,它们都只是从`fork()`返回,然后继续前进. (4认同)
  • 太棒了,所以我的图表是正确的。然而,这解释了为什么有两个 i=0 的实例(因为我总是打印 i 的当前值)。即使 i=0 被结转,下一个 fork 仅在 i 递增时执行!谢谢! (2认同)

Yu *_*Hao 12

  1. 是的,这是对的.(见下文)
  2. 不,在调用之后i++执行,因为这是循环的工作方式.forkfor
  3. 如果一切顺利,是的.但是,请记住fork可能会失败.

关于第二个的一点解释:

for (i = 0;i < 3; i++)
{
   fork();
}
Run Code Online (Sandbox Code Playgroud)

类似于:

i = 0;
while (i < 3)
{
    fork();
    i++;
}
Run Code Online (Sandbox Code Playgroud)

因此i在分叉进程(父对象和子进程)中是增量前的值.但是,增量会立即执行fork(),所以在我看来,图表可以被视为正确.


Ilm*_*nen 5

一一回答您的问题:

我的图表正确吗?

是的,本质上。这也是一个非常好的图表。

也就是说,如果您将i=0etc. 标签解释为指代完整循环迭代,则是正确的。然而,图中没有显示的是,在 eachfork()之后,fork()调用之后的当前循环迭代部分也由分叉的子进程执行。

为什么i=0输出中有两个实例?

因为你有printf()after 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");
Run Code Online (Sandbox Code Playgroud)

创建的子进程fork()是其父进程的(几乎)完全克隆,因此,从它自己的角度来看,它“记得”是它的父进程,继承了父进程的所有状态(包括所有变量值、调用堆栈和正在执行的指令)。唯一的直接区别(除了系统元数据,例如由 返回的进程 ID 之外getpid())是 的返回值fork(),它在子进程中为零,但在父进程中非零(实际上是子进程的 ID)。

2^n - 1计算分叉的孩子数量的方法总是如此吗?那么,在这里n=3,这意味着2^3 - 1 = 8 - 1 = 7孩子,哪个是正确的?

每个执行 a 的进程都会fork()变成两个进程(除非在异常错误情况下fork()可能会失败)。如果父子fork()进程继续执行相同的代码(即不检查返回值,或者自己的进程ID,并根据它分支到不同的代码路径),那么随后的每个fork都会使进程数增加一倍。所以,是的,在三个 fork 之后,您最终将总共有 2³ = 8 个进程。