简体   繁体   English

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

[英]Visually what happens to fork() in a For Loop

I have been trying to understand fork() behavior. 我一直在试图理解fork()行为。 This time in a for-loop . 这次是for-loop Observe the following code: 请注意以下代码:

#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());
}

Here is the output: 这是输出:

[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

I am a very visual person, and so the only way for me to truly understand things is by diagramming. 我是一个非常有视觉的人,所以我真正理解事物的唯一方法就是通过图表。 My instructor said there would be 8 hi statements. 我的导师说会有8个hi语句。 I wrote and ran the code, and indeed there were 8 hi statements. 我编写并运行了代码,确实有8个hi语句。 But I really didn't understand it. 但我真的不明白。 So I drew the following diagram: 所以我画了下面的图:

在此输入图像描述

Diagram updated to reflect comments :) 图更新以反映评论:)

Observations: 观察:

  1. Parent process (main) must iterate the loop 3 times. 父进程(main)必须迭代循环3次。 Then printf is called 然后调用printf
  2. On each iteration of parent for-loop a fork() is called 在父for循环的每次迭代中,调用fork()
  3. After each fork() call, i is incremented, and so every child starts a for-loop from i before it is incremented 在每次fork()调用之后,i递增,因此每个子函数在递增之前从i开始for循环
  4. At the end of each for-loop, "hi" is printed 在每个for循环结束时,打印“hi”

Here are my questions: 这是我的问题:

  • Is my diagram correct? 我的图表是否正确?
  • Why are there two instances of i=0 in the output? 为什么输出中有两个 i=0实例?
  • What value of i is carried over to each child after the fork()? 在fork()之后, i每个孩子的价值是多少? If the same value of i is carried over, then when does the "forking" stop? 如果i值相同,那么“分叉”何时停止?
  • Is it always the case that 2^n - 1 would be a way to count the number of children that are forked? 总是这样的情况, 2^n - 1将是一种计算分叉儿童数量的方法吗? So, here n=3 , which means 2^3 - 1 = 8 - 1 = 7 children, which is correct? 那么,这里n=3 ,这意味着2^3 - 1 = 8 - 1 = 7孩子,这是正确的吗?

Here's how to understand it, starting at the for loop. for循环开始,这是如何理解它的。

  1. Loop starts in parent, i == 0 循环从父开始, i == 0

  2. Parent fork() s, creating child 1. fork() ,创建子1。

  3. You now have two processes. 您现在有两个进程。 Both print i=0 . 两者都打印i=0

  4. Loop restarts in both processes, now i == 1 . 循环在两个进程中重新启动,现在i == 1

  5. Parent and child 1 fork() , creating children 2 and 3. 父和子1 fork() ,创建子级2和3。

  6. You now have four processes. 您现在有四个流程。 All four print i=1 . 全部四个打印i=1

  7. Loop restarts in all four processes, now i == 2 . 循环在所有四个进程中重新启动,现在i == 2

  8. Parent and children 1 through 3 all fork() , creating children 4 through 7. 父母和孩子1到3都是fork() ,创建4到7岁的孩子。

  9. You now have eight processes. 你现在有八个进程。 All eight print i=2 . 所有八个打印i=2

  10. Loop restarts in all eight processes, now i == 3 . 循环在所有八个进程中重新启动,现在i == 3

  11. Loop terminates in all eight processes, as i < 3 is no longer true. 循环在所有八个进程中终止,因为i < 3不再为真。

  12. All eight processes print hi . 所有八个过程打印hi

  13. All eight processes terminate. 所有八个进程终止。

So you get 0 printed two times, 1 printed four times, 2 printed 8 times, and hi printed 8 times. 所以,你得到0印刷两次, 1印刷四次, 2印刷8次, hi印刷8次。

  1. Yes, it's correct. 是的,这是对的。 (see below) (见下文)
  2. No, i++ is executed after the call of fork , because that's the way the for loop works. 不,在调用fork 之后执行i++ ,因为这就是for循环的工作方式。
  3. If all goes successfully, yes. 如果一切顺利,是的。 However, remember that fork may fail. 但是,请记住fork可能会失败。

A little explanation on the second one: 关于第二个的一点解释:

for (i = 0;i < 3; i++)
{
   fork();
}

is similar to: 类似于:

i = 0;
while (i < 3)
{
    fork();
    i++;
}

So i in the forked processes(both parent and child) is the value before increment. 所以i在分叉进程(父对象和子进程)中都是增量前的值。 However, the increment is executed immediately after fork() , so in my opinion, the diagram could be treat as correct. 但是,在fork()之后立即执行增量,所以在我看来,图表可以被视为正确。

To answer your questions one by one: 逐一回答您的问题:

Is my diagram correct? 我的图表是否正确?

Yes, essentially. 是的,基本上。 It's a very nice diagram, too. 这也是一个非常好的图表。

That is to say, it's correct if you interpret the i=0 etc. labels as referring to full loop iterations. 也就是说,如果将i=0等标签解释为指向完整循环迭代,则是正确的。 What the diagram doesn't show, however, is that, after each fork() , the part of the current loop iteration after the fork() call is also executed by the forked child process. 然而,该图显示的是,在每个fork()之后, fork()调用之后的当前循环迭代的部分也由分叉的子进程执行。

Why are there two instances of i=0 in the output? 为什么输出中有两个i=0实例?

Because you have the printf() after the fork() , so it's executed by both the parent process and the just forked child process. 因为fork() printf()之后有printf() ,所以它由父进程和刚分叉的子进程执行。 If you move the printf() before the fork() , it will only be executed by the parent (since the child process doesn't exist yet). 如果在fork() printf()之前移动printf() ,它将仅由父项执行(因为子进程尚不存在)。

What value of i is carried over to each child after the fork() ? fork()之后, i每个孩子的价值是多少? If the same value of i is carried over, then when does the "forking" stop? 如果i值相同,那么“分叉”何时停止?

The value of i is not changed by fork() , so the child process sees the same value as its parent. fork()不会更改i的值,因此子进程看到的值与其父进程相同。

The thing to remember about fork() is that it's called once, but it returns twice — once in the parent process, and once in the newly cloned child process. 关于fork()的要记住的是它被调用一次,但它返回两次 - 一次在父进程中,一次在新克隆的子进程中。

For a simpler example, consider the following code: 有关更简单的示例,请考虑以下代码:

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");

The child process created by fork() is an (almost) exact clone of its parent, and so, from its own viewpoint, it "remembers" being its parent, inheriting all of the parent process's state (including all variable values, the call stack and the instruction being executed). fork()创建的子进程是其父级的(几乎)精确克隆,因此,从它自己的角度来看,它“记住”作为其父级,继承所有父进程的状态(包括所有变量值,调用)堆栈和正在执行的指令)。 The only immediate difference (other than system metadata such as the process ID returned by getpid() ) is the return value of fork() , which will be zero in the child process but non-zero (actually, the ID of the child process) in the parent. 唯一的直接差异(除了系统元数据,例如getpid()返回的进程ID)是fork()的返回值,它在子进程中为零但非零(实际上是子进程的ID) )在父母。

Is it always the case that 2^n - 1 would be a way to count the number of children that are forked? 总是这样的情况, 2^n - 1将是一种计算分叉儿童数量的方法吗? So, here n=3 , which means 2^3 - 1 = 8 - 1 = 7 children, which is correct? 那么,这里n=3 ,这意味着2^3 - 1 = 8 - 1 = 7孩子,这是正确的吗?

Every process that executes a fork() turns into two processes (except under unusual error conditions, where fork() might fail). 执行fork()每个进程都会变成两个进程(除非在异常错误条件下, fork()可能会失败)。 If the parent and child keep executing the same code (ie they don't check the return value of fork() , or their own process ID, and branch to different code paths based on it), then each subsequent fork will double the number of processes. 如果父和子继续执行相同的代码(即他们不检查fork()的返回值,或者他们自己的进程ID,并根据它分支到不同的代码路径),那么每个后续的fork将使数字加倍进程 So, yes, after three forks, you will end up with 2³ = 8 processes in total. 所以,是的,在三把叉之后,你最终会得到2³= 8个进程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM