Визуально, что происходит с 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=%dn", getppid(), getpid(), i);
   }

   printf("[%d] [%d] hin", 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 привет заявления. Но я действительно не понимаю. Поэтому я нарисовал следующую диаграмму:

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

замечания:

  1. родительский процесс (основной) должен повторить цикл 3 раза. Тогда printf называется
  2. на каждой итерации родительского цикла for-loop вилка () называется
  3. после каждого вызова fork() i увеличивается, и поэтому каждый ребенок начинает цикл for-loop от i до того, как он будет увеличивается
  4. в конце каждого цикла for-loop печатается "hi"

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

  • моя схема правильная?
  • почему два экземпляров i=0 на выходе?
  • какое значение i переносится на каждого ребенка после вызова Fork()? Если же значение i переносится, то когда" разветвление " останавливается?
  • это всегда так, что 2^n - 1 был бы способ подсчитать количество детей, которые раздвоены? Так вот, здесь n=3, что означает 2^3 - 1 = 8 - 1 = 7 дети, что правильно?
3 53

3 ответа:

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

  1. цикл начинается в родительской, i == 0

  2. родитель fork()s, создание дочернего элемента 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 раз.

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

небольшое объяснение на втором:

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

аналогично:

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

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

чтобы ответить на ваши вопросы по одному:

моя схема правильная?

да, по сути. Это тоже очень хорошая схема.

то есть, это правильно, если вы трактуете i=0 etc. метки, относящиеся к итерациям полного цикла. Какая диаграмма не показать, однако, что, после каждого 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(), или свои собственные идентификатор процесса и ответвление на разные пути кода на его основе), то каждая последующая вилка будет удваивать количество процессов. Итак, Да, после трех вилок вы получите в общей сложности 23 = 8 процессов.