Яка причина виконання подвійної вилки при створенні демона?


165

Я намагаюся створити демон в пітоні. Я знайшов наступне питання , в якому є деякі хороші ресурси, за якими я зараз перебуваю, але мені цікаво, чому потрібна подвійна вилка. Я подряпав Google і знайшов безліч ресурсів, де заявив, що потрібен, але не чому.

Деякі згадують, що це не дозволяє демону придбати контрольний термінал. Як би це зробити без другої вилки? Які є наслідки?



2
Одна з складнощів із подвійною вилкою полягає в тому, що батько не може легко отримати PID процесу онука ( fork()виклик повертає PID дитини батькові, тому отримати PID дочірнього процесу легко, але це не так просто отримати ПІД процесу онука ).
Крейг МакКвін

Відповіді:


105

Дивлячись на код, на який посилається питання, обґрунтування таке:

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

Таким чином, це забезпечити повторне панування демона на init (про всяк випадок, якщо процес, що починається від демона, довгожив), і усуває будь-який шанс демона знову придбати контрольний tty. Тож якщо жоден із цих випадків не застосовується, то однієї виделки має бути достатньо. " Мережеве програмування Unix - Стівенс " про це має хороший розділ.


28
Це не зовсім правильно. Стандартний спосіб створити демон - це просто зробити p=fork(); if(p) exit(); setsid(). У цьому випадку батько також виходить і перший дочірній процес відновлюється. Магія з подвійною вилкою потрібна лише для того, щоб демон не придбав десятку.
parasietje

1
Отже, як я розумію, якщо моя програма запуститься і forksбуде childпроцес, цей перший перший дочірній процес буде a session leaderі зможе відкрити термінал TTY. Але якщо я знову відхилюся від цієї дитини та скасую цю першу дитину, друга дитина, яка роздвоєна, не буде session leaderі не зможе відкрити термінал TTY. Чи правильно це твердження?
tonix

2
@tonix: просто розгортання не створює лідера сеансу. Це робиться setsid(). Таким чином, перший процес розщеплення стає лідером сеансу після виклику, setsid()а потім ми знову розщелимося, щоб остаточний процес з подвійним розщепленням вже не був лідером сесії. Окрім вимоги setsid()бути лідером сесії, ви на місці.
dbmikus

169

Я намагався зрозуміти подвійну вилку і натрапив на це питання тут. Після багатьох досліджень це я зрозумів. Сподіваємось, це допоможе прояснити речі краще для кожного, у кого є те саме питання.

У Unix кожен процес належить до групи, яка в свою чергу належить до сеансу. Ось ієрархія ...

Сесія (SID) → Група процесів (PGID) → Процес (PID)

Перший процес у групі процесів стає лідером групи процесів, а перший процес у сеансі стає лідером сесії. Кожен сеанс може мати один TTY, пов'язаний з ним. Тільки ведучий сесії може взяти під контроль TTY. Щоб процес був по-справжньому демонізованим (протікав у фоновому режимі), ми повинні забезпечити загибель лідера сесії, щоб не було можливості сеансу взяти під контроль TTY.

Я запустив цю програму демонстрації демонстрації програми пітона Sander Marechal з цього сайту на своєму Ubuntu. Ось результати з моїми коментарями.

1. `Parent`    = PID: 28084, PGID: 28084, SID: 28046
2. `Fork#1`    = PID: 28085, PGID: 28084, SID: 28046
3. `Decouple#1`= PID: 28085, PGID: 28085, SID: 28085
4. `Fork#2`    = PID: 28086, PGID: 28085, SID: 28085

Зауважте, що процес є лідером сесії після Decouple#1, тому що це PID = SID. Це все ще могло взяти під контроль TTY.

Зауважте, що Fork#2це вже не лідер сесії PID != SID. Цей процес ніколи не може взяти під контроль TTY. Воістину демонізований.

Я особисто вважаю термінологічну вилку двічі заплутаною. Кращою ідіомою може бути вилка-декупаж-вилка.

Додаткові посилання, що цікавлять:


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

Але другий за також може зателефонувати в декупаж і стати лідером сесії, а потім придбати термінал.
Trismegistos

2
Це не правда. Перший fork()вже запобігає створенню зомбі, за умови, що ви закриєте батька.
parasietje

1
Мінімальний приклад для отримання наведених вище результатів: gist.github.com/cannium/7aa58f13c834920bb32c
може.

1
Чи було б корисно зателефонувати setsid() перед синглом fork()? Насправді я думаю, що відповіді на це питання відповідають на це.
Крейг МакКуін

118

Строго кажучи, подвійний вил не має нічого спільного з повторним вихованням демона в дитинстві init. Все, що потрібно для того, щоб перевчити дитину, це те, що батько повинен вийти. Це можна зробити лише однією виделкою. Крім того, виконання подвійної вилки само по собі не відновлює процес демона init; батько демона повинен вийти. Іншими словами, батько завжди виходить, коли розпалює належний демон, щоб процес демона був повторно налаштований на init.

То чому подвійний вилка? POSIX.1-2008 Розділ 11.1.3 " Термінал керування " містить відповідь (наголос додано):

Керуючий термінал для сеансу розподіляється керівником сесії у визначеному реалізацією порядку. Якщо у лідера сеансу немає керуючого терміналу і відкриває файл термінального пристрою, який вже не пов'язаний з сеансом, не використовуючи O_NOCTTYпараметр (див. open()), Він визначається реалізацією, чи стає термінал контрольним терміналом лідера сеансу. Якщо процес, який не є лідером сеансу, відкриває файл терміналу або використовується O_NOCTTYпараметр open(), то цей термінал не повинен стати контрольним терміналом процесу виклику .

Це говорить нам, що якщо демон демон робить щось подібне ...

int fd = open("/dev/console", O_RDWR);

... тоді процес демона може набувати /dev/consoleсвого контрольного терміналу, залежно від того, чи є процес демона лідером сесії та залежно від реалізації системи. Програма може гарантувати, що вищевказаний виклик не придбає контрольний термінал, якщо програма спочатку переконається, що він не є лідером сесії.

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

Техніка подвійної вилки трохи параноїчна. Це може не бути необхідним, якщо ви знаєте, що демон ніколи не відкриє файл термінального пристрою. Крім того, для деяких систем це може бути не потрібно, навіть якщо демон відкриває файл термінального пристрою, оскільки така поведінка визначена реалізацією. Однак одне, що не визначено реалізацією, - це те, що лише керівник сесії може виділити керуючий термінал. Якщо процес не є лідером сеансу, він не може виділити контрольний термінал. Тому, якщо ви хочете бути параноїком і бути впевненим, що демон-процес не може ненавмисно придбати контрольний термінал, незалежно від будь-яких визначених реалізацією специфік, то техніка подвійної вилки є важливою.


3
+1 Дуже погано, що ця відповідь надійшла ~ чотири роки після того, як було поставлено запитання.
Тім Сегейн

12
Але це ще не пояснює, чому це так страшно важливо, що демон не може знову придбати контрольний термінал
UloPe

7
Ключове слово "ненавмисно" придбає контрольний термінал. Якщо в процесі відбувається відкриття терміналу, і він стає терміналом, що контролює процеси, ніж якщо хтось видає ^ C з цього терміналу, він може припинити процес. Тож може бути приємно захистити процес від того, щоб це сталося з ним ненавмисно. Особисто я буду дотримуватися однієї вилки та setid () для коду, який я пишу, що я знаю, що не буде відкривати термінали.
BobDoolittle

1
@BobDoolittle, як це могло статися "ненавмисно"? Процес не буде просто мимоволі закінчувати відкриття терміналів, якщо це не написано для цього. Можливо, подвійне розгортання корисно, якщо ви програміст не знаєте коду і не знаєте, чи може він відкрити tty.
Маріус

10
@Marius Уявіть собі , що може статися , якщо ви додати рядок в файл конфігурації вашого демона: LogFile=/dev/console. Програми не завжди контролюють час компіляції над тим, які файли вони можуть відкрити;)
Dan Molding

11

Взято з Bad CTK :

"Для деяких ароматів Unix, ви змушені робити подвійну вилку при запуску, щоб перейти в демон-режим. Це тому, що однократне розгортання не гарантується, щоб відірватися від керуючого терміналу."


3
Як може одна вилка не відірватися від керуючого терміналу, але подвійна виделка може це зробити? На яких уніксах це відбувається?
bdonlan

12
Демон повинен закрити його вхідні та вихідні дескриптори файлів (fds), інакше він все-таки буде приєднаний до терміналу, в якому він був запущений. Процес розщеплення успадковує їх від батьківського. Мабуть, перша дитина закриває фдери, але це не прибирає все. На другому вилку, FDS не існує, тому другу дитину вже не можна підключати ні до чого.
Аарон Дігулла

4
@Aaron: Ні, демон належним чином "відривається" від свого керуючого терміналу, викликаючи setsidпісля початкової розсилки. Потім він гарантує, що він залишається відірваним від керуючого терміналу шляхом повторного розгортання та виходу лідера сеансу (процесу, який викликав setsid).
Dan Molding

2
@bdonlan: Це не forkте, що відривається від керуючого терміналу. Це те, setsidщо це робить. Але setsidне вдасться, якщо його зателефонують від керівника групи процесу. Отже, forkперед тим, setsidщоб переконатися, що setsidвикликається з процесу, який не є лідером групи процесів, необхідно зробити початкове Другий forkзабезпечує, що остаточний процес (той, який буде демон) не є лідером сесії. Тільки лідери сеансу можуть придбати контрольний термінал, тому ця друга вилка гарантує, що демон не буде ненавмисно знову придбати контрольний термінал. Це стосується будь-якої ОС POSIX.
Dan Molding

@DanMoulding Це не гарантує, що друга дитина не придбає контрольний термінал, оскільки він може викликати setid і стати лідером сесії, а потім придбати термінал управління.
Trismegistos

7

Відповідно до "Розширеного програмування в середовищі Unix", від Stephens і Rago, другий вил - це більше рекомендація, і це робиться для того, щоб демон не придбав контрольний термінал в системах на базі системи V.


3

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

Результатом є те, що батьківський процес не повинен знати про дітей, що розщедрилися, а також дає можливість відщеплювати довгі запущені процеси з libs тощо.


2

Виклик daemon () має батьківський виклик _exit (), якщо він успішний. Первісна мотивація, можливо, полягала в тому, щоб дозволити батькові виконати додаткову роботу, поки дитина демонструє.

Це може також грунтуватися на помилковій думці, що це необхідно для того, щоб у демона не було батьківського процесу і його було відновлено до init - але це все одно відбудеться, коли батько помре у випадку одного вилка.

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


2

Гідне обговорення цього питання, як видається, знаходиться на веб- сайті http://www.developerweb.net/forum/showthread.php?t=3025

Цитуючи моломпкін звідти:

... подумайте про виклик setid () як про "новий" спосіб робити річ (від'єднатись від терміналу) та виклик [секунди] fork () після цього як надмірність для роботи з SVr4 ...


-1

Це може бути простіше зрозуміти таким чином:

  • Перший форк та setid створить новий сеанс (але ID процесу == ідентифікатор сесії).
  • Друга вилка переконуєсь, що ідентифікатор процесу! = Ідентифікатор сесії.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.