Подтвердить что ты не робот

Визуально, что происходит с fork() в цикле For

Я пытаюсь понять поведение 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 привет. Но я действительно этого не понимал. Поэтому я нарисовал следующую диаграмму:

enter image description here

Диаграмма обновлена, чтобы отразить комментарии:)

Замечания:

  • Родительский процесс (основной) должен повторять цикл 3 раза. Затем printf вызывается
  • На каждой итерации родительского цикла fork() называется
  • После каждого вызова fork() я увеличивается, и поэтому каждый дочерний элемент запускает цикл for из цикла я до того, как он будет увеличен.
  • В конце каждого цикла for-loop печатается "hi"

Вот мои вопросы:

  • Является ли моя диаграмма правильной?
  • Почему на выходе есть два экземпляра i=0?
  • Какое значение i переносится на каждый дочерний элемент после fork()? Если переносится одно и то же значение i, тогда, когда останавливается "разветвление"?
  • Всегда ли 2^n - 1 способ подсчета числа детей, которые разветвляются? Итак, здесь n=3, что означает 2^3 - 1 = 8 - 1 = 7 детей, что правильно?
4b9b3361

Ответ 1

Вот как это понимать, начиная с цикла for.

  • Цикл начинается с родителя, i == 0

  • Родитель fork() s, создающий дочерний элемент.

  • Теперь у вас есть два процесса. Оба печатают i=0.

  • Цикл перезапускается в обоих процессах, теперь i == 1.

  • Родительский и дочерний 1 fork(), создающий детей 2 и 3.

  • Теперь у вас есть четыре процесса. Все четыре печати i=1.

  • Петля перезапускается во всех четырех процессах, теперь i == 2.

  • Родитель и дети с 1 по 3 все fork(), создавая детей с 4 по 7.

  • Теперь у вас есть восемь процессов. Все восемь печатных i=2.

  • Петля перезапускается во всех восьми процессах, теперь i == 3.

  • Loop завершается во всех восьми процессах, поскольку i < 3 больше не верен.

  • Все восемь процессов печатают hi.

  • Все восемь процессов завершаются.

Итак, вы печатаете 0 два раза, 1 печатается четыре раза, 2 напечатано 8 раз, а hi - 8 раз.

Ответ 2

  • Да, это правильно. (см. ниже).
  • Нет, i++ выполняется после вызова fork, так как работает цикл for.
  • Если все идет успешно, да. Однако помните, что fork может выйти из строя.

Небольшое объяснение второго:

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

похож на:

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

So i в разветвленных процессах (как родительский, так и дочерний) - это значение до приращения. Однако приращение выполняется сразу после fork(), поэтому, на мой взгляд, диаграмму можно рассматривать как правильную.

Ответ 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()), - это возвращаемое значение fork(), которое будет равно нулю в дочернем процессе, но отличное от нуля (на самом деле, идентификатор дочернего процесса ) в родительском.

Всегда ли так, что 2^n - 1 будет способ подсчета числа детей, которые разветвляются? Итак, здесь n=3, что означает 2^3 - 1 = 8 - 1 = 7 детей, что правильно?

Каждый процесс, выполняющий fork(), превращается в два процесса (за исключением необычных условий ошибки, где fork() может завершиться ошибкой). Если родитель и ребенок продолжают выполнять один и тот же код (т.е. Они не проверяют возвращаемое значение fork() или их собственный идентификатор процесса и ветвь на разные кодовые пути на его основе), то каждая последующая fork удваивает число процессов. Итак, да, после трех вилок вы получите 2 & sup3; = 8 процессов в целом.