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

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

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

int main(void) {

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

  printf("forked!\n");
  return 0;
}

6 ответов

Решение

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

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

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

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

P2 затем выполняет printf и заканчивается.

P3 затем выполняет printf и заканчивается.

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

Один приходит из main() а остальные три от каждого fork(),

Обратите внимание, что все три forks() будут казнены. Возможно, вы захотите взглянуть на ссылку:

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

После успешного завершения fork() возвращает 0 дочернему процессу и возвращает идентификатор процесса дочернего процесса родительскому процессу. Оба процесса должны продолжать выполняться из функции fork(). В противном случае -1 должен быть возвращен родительскому процессу, дочерний процесс не должен быть создан, и должно быть установлено значение errno, указывающее на ошибку.

Обратите внимание, что идентификатор процесса не может быть нулевым, как указано здесь.


Так что же на самом деле происходит?

У нас есть:

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

Итак, первый fork() вернет родителю свой ненулевой идентификатор процесса, в то время как он вернет 0 дочернему процессу. Это означает, что первая ветвь логического выражения будет оценена как true в родительском процессе, в то время как в дочернем процессе она будет оценена как false и, вследствие оценки короткого замыкания, она не будет вызывать оставшиеся два fork()s.

Итак, теперь мы знаем, что получим как минимум два отпечатка (один с основного и один с первого fork()).

Теперь 2-й fork() в родительском процессе он будет выполнен, и он возвращает ненулевое значение родительскому процессу и нулевое значение в дочернем процессе.

Так что теперь родитель не будет продолжать выполнение до последнего fork() (из-за короткого замыкания), в то время как дочерний процесс выполнит последний форк, так как первый операнд || это 0.

Это означает, что мы получим еще два отпечатка.

В результате мы получаем четыре отпечатка.


Короткое замыкание

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

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


Процесс

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

Имея это в виду, стоит взглянуть на эту похожую проблему, а также на этот ответ.


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

Я сделал также эту цифру, которая может помочь, я думаю. Я предположил, что пид fork() возвращаются 3, 4 и 5 за каждый звонок.

узлы вилкиОбратите внимание, что некоторые fork()s имеют красный X над ними, что означает, что они не выполняются из-за короткого замыкания логического выражения.

fork()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

Для всех downvoters, это от объединенного, но другого вопроса. Виноват ТАК. Спасибо.

Вы можете разбить задачу на три строки, причем первая и последняя строки просто удваивают количество процессов.

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)

Первая вилка:

  • возвращаемое родительское значение pid не нуль => выполняет && (fork() || fork())
    • родительское значение второй ветки pid, а не NULL прекращает выполнение || часть => печать forked
    • значение второго ответвления вилки = 0 => выполняет || fork()
      • третьи вилки родительские принты forked
      • третья вилка дочерние принты forked
  • возвращаемое значение дочернего элемента равно 0: прекратите выполнение && part => print 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 раз.

Другие вопросы по тегам