Почему эта программа печатает " раздвоенный!- 4 раза?
Почему эта программа печатает " раздвоенный!- 4 раза?
#include <stdio.h>
#include <unistd.h>
int main(void) {
fork() && (fork() || fork());
printf("forked!n");
return 0;
}
6 ответов:
Первый
fork()
возвращает ненулевое значение в вызывающем процессе (назовем его p0) и 0 в дочернем (назовем его p1).В p1 берется короткое замыкание для
&&
, и процесс вызываетprintf
и завершается. В p0 процесс должен вычислить остаток выражения. Затем он снова вызываетfork()
, создавая таким образом новый дочерний процесс (p2).В p0
fork()
возвращает ненулевое значение, и короткое замыкание для||
берется, поэтому процесс вызываетprintf
и прекращает.В p2,
fork()
возвращает 0, поэтому остаток || должен быть вычислен, который является последнимfork()
; это приводит к созданию дочернего элемента для p2 (назовем его p3).P2 затем выполняет
printf
и завершает работу.P3 затем выполняет
printf
и завершает работу.4
printf
s затем выполняются.
Один исходит от
Обратите внимание, что все триmain()
, а остальные три-от каждогоfork()
.forks()
будут выполнены. Возможно, вы захотите взглянуть на ref :Обратите внимание, что идентификатор процесса не может быть равен нулю, как указано здесь.ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
После успешного завершения, fork () возвращает 0 дочернему процессу и возвращает идентификатор процесса дочернего процесса родительскому процессу. Оба процесса должны продолжать выполняться из функции fork (). В противном случае -1 должен быть возвращен родительскому процессу, дочерний процесс не должен быть создан, и errno должен быть установлен для указания ошибки.
Так что же происходит на самом деле?
У нас есть:
Таким образом, первыйfork() && (fork() || fork());
fork()
вернет родительскому процессу его ненулевой идентификатор процесса, в то время как он вернет 0 дочернему процессу. Это означает, что первая вилка логического выражения будет вычислена следующим образом: значение true в Родительском процессе, в то время как в дочернем процессе оно будет оценено как false и, из-за короткого замыкания, оно не вызовет оставшиеся дваfork()
s. Итак, теперь мы знаем, что собираемся получить по крайней мере два отпечатка (один из main и один из 1-гоfork()
).Теперь, 2-й
fork()
в Родительском процессе будет выполнен, он делает это и возвращает ненулевое значение в родительский процесс и нулевое значение в дочерний процесс.Итак, теперь родительский процесс не будет продолжать выполнение до последнего
Это значит, что мы получим еще два отпечатка. В результате мы получаем в общей сложности четыре отпечатка.fork()
(из-за короткого замыкания), в то время как дочерний процесс будет выполнять последнюю вилку, так как первый операнд||
равен 0.
Короткое замыкание
Здеськороткое замыкание в основном означает, что если первый операнд && равен нулю, то другие операнды/не вычисляются. На та же логика, если операнд a / / равен 1, то остальные операнды не нуждаются в вычислении. Это происходит потому, что остальные операнды не могут изменить результат логического выражения, поэтому их не нужно выполнять, таким образом мы экономим время.
См. пример ниже.
Процесс
Помните, что родительский процесс создает дочерние процессы, которые, в свою очередь, создают другие процессы и так далее. Это приводит к иерархии процессов (или дереву, которое можно было бы сказать). Имея это в виду, стоит взглянуть на этуаналогичную проблему , а такжеэтот ответ.
Описательный образ
Я сделал и эту цифру, которая может помочь, я думаю. Я предположил, что возвращаемые pidfork()
равны 3, 4 и 5 для каждого вызова.Обратите внимание, что некоторые
fork()
s имеют красный крест над ними, что означает, что они не выполняются из-за короткого замыкания оценки логики выражение.S в верхней части не будут выполняться, потому что первый операнд оператора
&&
равен 0, таким образом, все выражение приведет к 0, поэтому нет смысла выполнять остальные операнды&&
.
fork()
внизу не будет выполняться, так как это второй операнд||
, где его первый операнд является ненулевым числом, таким образом, результат выражения уже оценивается как true, независимо от того, какой второй операнд является.И на следующем рисунке вы можете увидеть иерархию процессов: исходя из предыдущего рисунка.
Пример короткого замыкания
#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
для всех даунвотеров это из Объединенного, но другого вопроса. Виноват так. Спасибо.
Вы можете разложить задачу на три строки, причем первая и последняя строки просто удваивают число процессов.fork() && fork() || fork();
Операторы закорачиваются, так что вот что получается:
fork() / \ 0/ \>0 || fork() && fork() /\ / \ / \ 0/ \>0 * * || fork() * / \ * *
Таким образом, это всего 4 * 5 = 20 процессов, каждый из которых печатает одну строку.
Примечание: Если по какой-то причине fork () не работает (например, у вас есть некоторое ограничение на количество процессы), он возвращает -1, и тогда вы можете получить различные результаты.
Выполнение
fork() && (fork() || fork())
, что происходитКаждый
fork
дает 2 процесса с соответственно значениями pid (Родительский) и 0 (дочерний)Первая вилка:
- родительское возвращаемое значение - pid not null = > выполняет
&& (fork() || fork())
- второе значение родительской вилки - pid not null останавливает выполнение
||
part = > printforked
- вторая дочерняя вилка value = 0 = > выполняет
|| fork()
- третья вилка родительских отпечатков
forked
- отпечатки третьего ребенка вилки
forked
- дочернее возвращаемое значение равно 0 остановить выполнение & & part = > prints
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