Визуально, что происходит с 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
детей, что правильно?
Ответы
Ответ 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 процессов в целом.