Почему эта программа печатает " раздвоенный!- 4 раза?


Почему эта программа печатает " раздвоенный!- 4 раза?

#include <stdio.h>
#include <unistd.h>

int main(void) {

  fork() && (fork() || fork());

  printf("forked!n");
  return 0;
}
6 75

6 ответов:

Первый fork() возвращает ненулевое значение в вызывающем процессе (назовем его p0) и 0 в дочернем (назовем его p1).

В p1 берется короткое замыкание для &&, и процесс вызывает printf и завершается. В p0 процесс должен вычислить остаток выражения. Затем он снова вызывает fork(), создавая таким образом новый дочерний процесс (p2).

В p0 fork() возвращает ненулевое значение, и короткое замыкание для || берется, поэтому процесс вызывает printf и прекращает.

В p2, fork() возвращает 0, поэтому остаток || должен быть вычислен, который является последним fork(); это приводит к созданию дочернего элемента для p2 (назовем его p3).

P2 затем выполняет printf и завершает работу.

P3 затем выполняет printf и завершает работу.

4 printfs затем выполняются.

Один исходит от 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, то остальные операнды не нуждаются в вычислении. Это происходит потому, что остальные операнды не могут изменить результат логического выражения, поэтому их не нужно выполнять, таким образом мы экономим время.

См. пример ниже.


Процесс

Помните, что родительский процесс создает дочерние процессы, которые, в свою очередь, создают другие процессы и так далее. Это приводит к иерархии процессов (или дереву, которое можно было бы сказать). Имея это в виду, стоит взглянуть на этуаналогичную проблему , а такжеэтот ответ.

Описательный образ

Я сделал и эту цифру, которая может помочь, я думаю. Я предположил, что возвращаемые pid fork() равны 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 = > print forked
    • вторая дочерняя вилка 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

Этот код:

fork();
fork() && fork() || fork();
fork();

Получает 20 процессов для себя и 20 раз Printf пойдет.

И для

fork() && fork() || fork();

Printf будет идти в общей сложности 5 раз.