Почему эта программа печатает "forked!" 4 раза?
#include <stdio.h>
#include <unistd.h>
int main(void) {
fork() && (fork() || fork());
printf("forked!\n");
return 0;
}
Почему эта программа печатает "forked!" 4 раза?
#include <stdio.h>
#include <unistd.h>
int main(void) {
fork() && (fork() || fork());
printf("forked!\n");
return 0;
}
Первый fork()
возвращает ненулевое значение в вызывающем процессе (вызовите его p0) и 0 в дочернем элементе (вызовите его p1).
В p1 выполняется короткая схема для &&
, и процесс вызывает printf
и завершается. В p0 процесс должен оценивать остальную часть выражения. Затем он вызывает fork()
снова, создавая таким образом новый дочерний процесс (p2).
В p0 fork()
возвращается ненулевое значение, и выполняется короткая схема для ||
, поэтому процесс вызывает printf
и завершается.
В p2, fork()
возвращает 0, так что остаток от || должен быть оценен, что является последним fork()
; что приводит к созданию дочернего элемента для p2 (назовите его p3).
P2 выполняет printf
и заканчивается.
P3 выполняет printf
и завершает.
4 printf
.
Он исходит от main()
и других трех от каждого fork()
.
Обратите внимание, что все три forks()
будут выполнены. Вы можете взглянуть на ref:
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
После успешного завершения fork() возвращает 0 в дочерний процесс и возвращает идентификатор процесса дочернего процесса в родительский процесс. Оба процесса продолжают выполняться из функции fork(). В противном случае -1 будет возвращен родительскому процессу, ни один дочерний процесс не будет создан, а errno должен быть установлен для указания ошибки.
Обратите внимание, что идентификатор процесса не может быть равен нулю, как указано здесь.
Итак, что на самом деле происходит?
Имеем:
fork() && (fork() || fork());
Итак, первый fork()
вернет родительскому элементу нулевой идентификатор процесса, а он вернет 0 в дочерний процесс. Это означает, что логическое выражение first fork будет оценено как true в родительском процессе, тогда как в дочернем процессе оно будет оценено как false и, из-за оценки коротких замыканий, он не будет вызывать оставшиеся два fork()
s.
Итак, теперь мы знаем, что получится как минимум два отпечатка (один из основного и один из 1-го fork()
).
Теперь 2-й fork()
в родительском процессе будет выполнен, и он возвращает ненулевое значение родительскому процессу и нулевой в дочернем процессе.
Итак, теперь родитель не будет продолжать выполнение до последнего fork()
(из-за короткого замыкания), в то время как дочерний процесс выполнит последнюю fork, так как первый операнд ||
равен 0.
Значит, мы получим еще две отпечатки.
В результате мы получаем всего четыре отпечатка.
Короткое замыкание
Здесь короткое замыкание в основном означает, что если первый операнд && равен нулю, то другой операнд (s) не оценивается /. По той же логике, если операнд || 1, то остальные операнды не нуждаются в оценке. Это происходит потому, что остальные операнды не могут изменить результат логического выражения, поэтому их не нужно выполнять, поэтому мы экономим время.
См. пример ниже.
Процесс
Помните, что родительский процесс создает процессы потомства, которые, в свою очередь, создают другие процессы и т.д. Это приводит к иерархии процессов (или дерева, которое можно сказать).
Имея это в виду, стоит взглянуть на эту аналогичную проблему, а также ответ.
Описательное изображение
Я тоже сделал эту цифру, которая может помочь, я думаю. Я предположил, что возвращаемый pid fork()
равен 3, 4 и 5 для каждого вызова.
Обратите внимание, что у некоторых fork()
есть красный X над ними, что означает, что они не выполняются из-за короткого замыкания оценки логического выражения.
fork()
наверху не будут выполняться, потому что первый операнд оператора &&
равен 0, поэтому целое выражение приведет к 0, поэтому никакая сущность в выполнении остальной части операнда ( s) of &&
.
fork()
внизу не будет выполняться, так как это второй операнд ||
, где его первый операнд - ненулевое число, поэтому результат выражения уже оценивается как true, no что второй операнд.
И на следующем рисунке вы можете увидеть иерархию процессов: на основе предыдущего рисунка.
Пример короткого замыкания
#include <stdio.h>
int main(void) {
if(printf("A printf() results in logic true\n"))
;//empty body
if(0 && printf("Short circuiting will not let me execute\n"))
;
else if(0 || printf("I have to be executed\n"))
;
else if(1 || printf("No need for me to get executed\n"))
;
else
printf("The answer wasn't nonsense after all!\n");
return 0;
}
Вывод:
A printf() results in logic true
I have to be executed
Для всех downvoters это связано с объединенным, но другим вопросом. Обвинение SO. Спасибо.
Вы можете разложить проблему на три строки, первая и последняя строки просто удваивают количество процессов.
fork() && fork() || fork();
Операторы короткозамкнуты, поэтому вы получаете:
fork()
/ \
0/ \>0
|| fork() && fork()
/\ / \
/ \ 0/ \>0
* * || fork() *
/ \
* *
Таким образом, это всего 4 * 5 = 20 процессов, каждый из которых печатает одну строку.
Примечание. Если по какой-либо причине fork() не работает (например, у вас есть ограничение на количество процессов), он возвращает -1, а затем вы можете получать разные результаты.
Выполнение fork() && (fork() || fork())
, что произойдет
Каждый fork
дает 2 процесса с соответственно значениями pid (parent) и 0 (child)
Первая вилка:
&& (fork() || fork())
||
part = > print forked
|| fork()
forked
forked
forked
Всего: 4 forked
Мне нравятся все ответы, которые уже были представлены. Возможно, если вы добавили еще несколько переменных в ваш оператор printf, вам было бы легче увидеть, что происходит.
#include<stdio.h>
#include<unistd.h>
int main(){
long child = fork() && (fork() || fork());
printf("forked! PID=%ld Child=%ld\n", getpid(), child);
return 0;
}
На моей машине он произвел этот вывод:
forked! PID=3694 Child = 0
forked! PID=3696 Child = 0
forked! PID=3693 Child = 1
forked! PID=3695 Child = 1
Этот код:
fork();
fork() && fork() || fork();
fork();
получает 20 процессов для себя и 20 раз Printf будет идти.
И для
fork() && fork() || fork();
printf будет в общей сложности 5 раз.