Чому ця програма друкує «роздвоєно!» 4 рази?


Відповіді:


86

Перший fork()повертає ненульове значення в процесі виклику (назвіть його p0) і 0 у дочірньому (назвіть це p1).

У p1 прийнято коротке замикання для &&, і процес викликає printfта припиняється. У p0 процес повинен оцінити залишок виразу. Потім він fork()знову викликає , таким чином створюючи новий дочірній процес (p2).

У p0 fork()повертає ненульове значення, і ||приймається коротке замикання для , тому процес викликає printfі завершується.

У p2 fork()повертає 0, тому залишок || повинен бути оцінений, що є останнім fork(); що призводить до створення дитини для p2 (назвіть це p3).

Потім Р2 виконується printfі завершується.

Потім P3 виконується printfі завершується.

printfПотім виконується 4 с.


3
чи не могли б ви пояснити "зайнято коротке замикання для &&"?
Rawan Lezzeik

8
@ rona-altico, перевірте посилання про коротке замикання в моїй відповіді. Це в основному означає, що якщо перший операнд &&дорівнює нулю, то інші операнди не обчислюються. За тією ж логікою, якщо операнд a ||дорівнює 1, то інші операнди не потребують оцінки. Це відбувається тому, що решта операнди не можуть змінити результат логічного виразу, тому їх не потрібно виконувати, таким чином ми економимо час. Краще зараз Рона? Приємне питання до речі, я не можу зрозуміти, чому голоси проти. Ви отримали мій +1.
gsamaras

3
@ rona-altico: Мені важко зрозуміти, чому ви хочете використовувати fork(), але я навіть не знаю, що таке коротке замикання. Це було питання в школі?
Себастьян Мах

2
@ G.Samaras: Гадаю, це голосування проти, тому що це один із багатьох дублікатів, і він не намагається навіть пошукати в Google, перш ніж запитувати тут.
chrk

4
@ G.Samaras: Ви не розумієте, чому голоси проти? Це жахливе питання. Є точний дублікат, і він не потрудився пояснити, який інший результат він очікував. Чому це 41 голос проти, мені цілком не під силу; зазвичай такі речі швидко досягають -3 або -4.
Гонки легкості на орбіті

210

Один походить від, main()а інші три - від кожного fork().

Зверніть увагу, що всіх трьох forks()буде страчено. Можливо, ви захочете поглянути на посилання :

ПОВЕРНЕННЯ ЗНАЧЕННЯ

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

Зверніть увагу, що ідентифікатор процесу не може бути нульовим, як зазначено тут .


То що насправді відбувається?

Ми маємо:

Отже, перший fork()поверне батькові його ненульовий ідентифікатор процесу, тоді як поверне 0 дочірньому процесу. Це означає, що перший форк логічного виразу буде оцінений як істина в батьківському процесі, тоді як у дочірньому процесі він буде оцінений як хибний, і, через оцінку короткого замикання , він не викликатиме два інших fork()s.

Отже, тепер ми знаємо, що збираємося отримати принаймні два відбитки (один з основних і один з 1-го fork()).

Тепер fork()буде виконано друге у батьківському процесі, і воно повертає ненульове значення батьківському процесу та нульове одиницю в дочірньому процесі.

Отже, тепер батько не буде продовжувати виконання до останнього fork()(через коротке замикання), тоді як дочірній процес буде виконувати останній форк, оскільки перший операнд ||0 дорівнює.

Це означає, що ми отримаємо ще два відбитки.

В результаті ми отримуємо в цілому чотири відбитки.


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

Тут коротке замикання в основному означає, що якщо перший операнд && дорівнює нулю, тоді інший операнд (и) є / не оцінюються. За тією ж логікою, якщо операнд || дорівнює 1, тоді решта операндів не потребують оцінки. Це відбувається тому, що решта операндів не можуть змінити результат логічного виразу, тому їх не потрібно виконувати, таким чином ми економимо час.

Див. Приклад нижче.


Процес

Пам’ятайте, що батьківський процес створює процеси нащадків, які, у свою чергу, створюють інші процеси тощо. Це призводить до ієрархії процесів (або дерева, яке можна сказати).

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


Описне зображення

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

вилочні вузли Зверніть увагу, що деякі fork()s мають червоний X над собою, що означає, що вони не виконуються через короткочасне оцінювання логічного виразу.

Значення fork()s у верхній частині не збираються виконувати, оскільки перший операнд оператора &&дорівнює 0, отже, весь вираз призведе до 0, тому жодної суті у виконанні решти операндів (ів) &&.

fork()На дні не буде виконано, так як це другий операнд ||, де його перший операнд є ненульове число, таким чином, результат виразу вже оцінюється істина, незалежно від того , що другий операнд є.

І на наступному малюнку ви можете побачити ієрархію процесів: Ієрархія процесу на основі попередньої фігури.


Приклад короткого замикання

Вихід:


14

Для всіх, хто голосував проти, це з об’єднаного, але іншого питання. Виною тому. Дякую.

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

Оператори замикаються, отже, ось що ви отримуєте:

Отже, це в цілому 4 * 5 = 20 процесів, що друкують один рядок.

Примітка: Якщо з якоїсь причини fork () не вдається (наприклад, у вас є деяке обмеження на кількість процесів), він повертає -1, і тоді ви можете отримати різні результати.


Дякую @yi_H за вашу відповідь і одне, якщо в нашому коді є лише цей рядок, тоді вихід буде 5 разів роздвоєний ??
Amit Singh Tomar

А також fork1 () || fork2 () && fork3 (); це твердження призводить до 16 процесу.
Amit Singh Tomar

Привіт @yi_H, я просто плутаю, як "fork1 () || fork2 () && fork3 ()" призводить до 16. Я маю на увазі, чи можу я отримати таку схему, про яку ви згадали вище
Аміт Сінгх Томар,

Без будь-яких дужок ви можете неправильно аналізувати логічне дерево. Хіба помилковий (0) шлях від першої вилки () не спричиняє коротке замикання всього виразу на &&? Я думаю, що пріоритет асоціативності && та || в C - справа наліво, так що просте обчислення зліва направо може коротко замикати решту підвиразу (у будь-яких містять дужки). Це те саме, що fork () && (fork () || fork ()) Це б тоді пояснило 4 (а не 5) процеси лише з цього рядка і 16 загалом. Це може відрізнятися в C ++ або C #, але це питання було в C.
Роб Паркер,

3
Це відповідає на питання, яке використовує fork() && fork() || fork();, тоді як запитання тут використовується fork() && (fork() || fork());. Відбулося злиття, як обговорювалося тут: " meta.stackoverflow.com/questions/281729/… ". Можливо, ви захочете відредагувати свою відповідь, повідомляючи майбутніх читачів.
gsamaras

9

Виконуючи fork() && (fork() || fork()), що відбувається

Кожен forkдає 2 процеси із відповідно значеннями pid (батьківський) та 0 (дочірній)

Перша вилка:

  • повертається батьківське значення pid не null => виконує && (fork() || fork())
    • друге батьківське значення fork - pid не null - зупиняє виконання ||частини => printforked
    • друге значення дочірньої вилки = 0 => виконує || fork()
      • третя вилка батьківських відбитків forked
      • третя вилка дочірніх відбитків forked
  • Повернене дочірнє значення дорівнює 0 припинити виконання && part => відбитків forked

Всього: 4 forked


8

Мені подобаються всі відповіді, які вже були подані. Можливо, якби ви додали ще кілька змінних до свого оператора printf, вам було б легше побачити, що відбувається.

На моїй машині він видав такий результат:


5
А як щодо значень, що повертаються кожним викликом fork ()? Як щодо: long f1,f2,f3; (f1 = fork()) && ((f2 = fork()) || (f3 = fork()));а потім надрукуйте ПІД та три окремі значення.
Роб Паркер,

5

Цей код:

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

І для

printf піде в цілому 5 разів.


2
Це відповідає на питання, яке використовує fork() && fork() || fork();, тоді як запитання тут використовується fork() && (fork() || fork());. Відбулося злиття, як обговорювалося тут: " meta.stackoverflow.com/questions/281729/… ". Можливо, ви захочете відредагувати свою відповідь, повідомляючи майбутніх читачів.
gsamaras
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.