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

76

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

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

int main(void) {

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

  printf("forked!\n");
  return 0;
}
  • 1
    Я держу пари, что он не знал об оценке короткого замыкания или запутался с возвращаемым значением fork() @LightnessRacesinOrbit.
Теги:
fork
systems-programming

6 ответов

78
Лучший ответ

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

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

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

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

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

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

4 printf.

  • 3
    Не могли бы вы объяснить, "короткое замыкание для && принято"?
  • 8
    @ rona-altico, проверьте ссылку на короткое замыкание в моем ответе. В основном это означает, что если первый операнд && равен нулю, то другие операнды не оцениваются. По той же логике, если операнд || равен 1, тогда остальные операнды не нуждаются в оценке. Это происходит потому, что остальные операнды не могут изменить результат логического выражения, поэтому их не нужно выполнять, поэтому мы экономим время. Лучше сейчас Рона? Хороший вопрос, кстати, я не понимаю, почему отрицательные голоса. Ты получил мой +1.
Показать ещё 6 комментариев
183

Он исходит от 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 для каждого вызова.

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

fork() наверху не будут выполняться, потому что первый операнд оператора && равен 0, поэтому целое выражение приведет к 0, поэтому никакая сущность в выполнении остальной части операнда ( s) of &&.

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

И на следующем рисунке вы можете увидеть иерархию процессов: Изображение 7839 на основе предыдущего рисунка.


Пример короткого замыкания

#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
13

Для всех downvoters это связано с объединенным, но другим вопросом. Обвинение SO. Спасибо.

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

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

Операторы короткозамкнуты, поэтому вы получаете:

       fork()
      /      \
    0/        \>0
 || fork()     && fork()
     /\            /   \
    /  \         0/     \>0
   *    *     || fork()  *
                /   \
               *     *

Таким образом, это всего 4 * 5 = 20 процессов, каждый из которых печатает одну строку.

Примечание. Если по какой-либо причине fork() не работает (например, у вас есть ограничение на количество процессов), он возвращает -1, а затем вы можете получать разные результаты.

  • 0
    Спасибо @yi_H за ваш ответ и одно: если в нашем коде есть только эта строка, то результат будет 5 раз разветвлен?
  • 0
    А также fork1 () || fork2 () && fork3 (); это утверждение приводит к 16 процессу.
Показать ещё 11 комментариев
9

Выполнение fork() && (fork() || fork()), что произойдет

Каждый fork дает 2 процесса с соответственно значениями pid (parent) и 0 (child)

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

  • родительское возвращаемое значение pid не равно null = > выполняет && (fork() || fork())
    • второе родительское значение fork - это pid, а не null останавливает выполнение || part = > print forked
    • второе дочернее значение fork = 0 = > выполняет || fork()
      • третий родительский шрифт fork forked
      • третий шрифт отпечатка вилки forked
  • Возвращаемое дочернее значение - 0 останавливает выполнение && & part = > prints forked

Всего: 4 forked

8

Мне нравятся все ответы, которые уже были представлены. Возможно, если вы добавили еще несколько переменных в ваш оператор 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
  • 5
    Как насчет значений, возвращаемых каждым вызовом fork ()? Как насчет: long f1,f2,f3; (f1 = fork()) && ((f2 = fork()) || (f3 = fork())); а затем распечатайте PID и три отдельных значения.
5

Этот код:

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

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

И для

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

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

  • 2
    Это отвечает на вопрос, который использует fork() && fork() || fork(); в то время как вопрос здесь использует fork() && (fork() || fork()); , Произошло слияние, как обсуждалось здесь: « meta.stackoverflow.com/questions/281729/… ». Вы можете отредактировать свой ответ, уведомив будущих читателей.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню