В чем причина выполнения двойной вилки при создании демона?

Я пытаюсь создать демона в python. Я нашел следующий вопрос, в котором есть некоторые хорошие ресурсы, в которых я сейчас следую, но мне любопытно, почему нужна двойная вилка. Я поцарапал Google и нашел много ресурсов, объявив, что это необходимо, но не почему.

Некоторые упоминают, что он должен предотвратить получение демона управляющим терминалом. Как бы это сделать без второй вилки? Каковы последствия?

Ответ 1

Глядя на код, указанный в вопросе, обоснование:

# Fork a second child and exit immediately to prevent zombies.  This
# causes the second child process to be orphaned, making the init
# process responsible for its cleanup.  And, since the first child is
# a session leader without a controlling terminal, it possible for
# it to acquire one by opening a terminal in the future (System V-
# based systems).  This second fork guarantees that the child is no
# longer a session leader, preventing the daemon from ever acquiring
# a controlling terminal.

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

Ответ 2

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

В Unix каждый процесс принадлежит группе, которая, в свою очередь, принадлежит сеансу. Вот иерархия...

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

Первый процесс в группе процессов становится лидером группы процессов, а первый процесс в сеансе становится лидером сеанса. С каждой сессией может быть связан один TTY. Только лидер сеанса может взять под контроль TTY. Чтобы процесс был по-настоящему демонизирован (запущен в фоновом режиме), мы должны убедиться, что лидер сеанса убит, чтобы исключить возможность того, что сеанс когда-либо получит контроль над TTY.

Я запустил программу-демон Sander Marechal на этом Python с этого сайта в моей 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 лидера сеанса PID != SID. Этот процесс никогда не может взять под контроль TTY. Истинно демонизирован.

Лично я нахожу терминологическую форк-двойку запутанной. Лучшей идиомой может быть вилка-развязка-вилка.

Дополнительные ссылки, представляющие интерес:

Ответ 3

Строго говоря, двойная вилка не имеет ничего общего с переопределением демона как потомка init. Все, что необходимо для повторного воспитания ребенка, это то, что родитель должен выйти. Это может быть сделано только с одной вилкой. Кроме того, двойное разветвление само по себе не переопределяет процесс демона для init; родитель демона должен выйти. Другими словами, родитель всегда выходит, когда разветвляет правильного демона, так что процесс демона повторно связан с init.

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

Управляющий терминал для сеанса назначается руководителем сеанса в зависимости от реализации. Если руководитель сеанса не имеет управляющего терминала и открывает файл терминального устройства, который еще не связан с сеансом, без использования опции O_NOCTTY (см. open()), то определяется, будет ли реализация управлять терминалом сеанса. лидер. Если процесс, который не является лидером сеанса, открывает файл терминала или в open() используется опция O_NOCTTY, то этот терминал не должен становиться управляющим терминалом вызывающего процесса.

Это говорит нам, что если процесс демона делает что-то вроде этого...

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

... тогда процесс-демон может получить /dev/console качестве управляющего терминала, в зависимости от того, является ли процесс-демон лидером сеанса, и в зависимости от реализации системы. Программа может гарантировать, что вышеуказанный вызов не получит управляющий терминал, если программа сначала гарантирует, что он не является лидером сеанса.

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

Техника двойной вилки немного параноидальна. В этом нет необходимости, если вы знаете, что демон никогда не откроет файл терминального устройства. Кроме того, в некоторых системах это может не потребоваться, даже если демон действительно открывает файл оконечного устройства, так как это поведение определяется реализацией. Однако одна вещь, которая не определяется реализацией, состоит в том, что только лидер сеанса может выделить управляющий терминал. Если процесс не является лидером сеанса, он не может выделить управляющий терминал. Следовательно, если вы хотите быть параноиком и быть уверенным, что процесс-демон не может случайно получить управляющий терминал, независимо от специфики, определенной реализацией, тогда метод двойной вилки необходим.

Ответ 4

Взято из Bad CTK:

"В некоторых вариантах Unix вы вынуждены делать двойную вилку при запуске, чтобы перейти в режим демона. Это связано с тем, что однократное форсирование не может отделиться от управляющего терминала".

Ответ 5

В соответствии с "Расширенным программированием в среде Unix", Стивенсом и Раго, вторая версия является скорее рекомендацией, и это делается для того, чтобы гарантировать, что демон не получит управляющий терминал в системах на основе System V.

Ответ 6

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

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

Ответ 7

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

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

Итак, я полагаю, что все это просто сводится к традициям в конце - достаточно одной вилки, пока родитель умирает вкратце. В любом случае.

Ответ 8

Достойное обсуждение этого вопроса похоже на http://www.developerweb.net/forum/showthread.php?t=3025

Цитата из тамплива:

... подумайте о вызове setsid() как о "новом" способе делать что-то (disassociate from the terminal) и [second] fork() после него как избыточность для работы с SVr4...

Ответ 9

Это может быть легче понять следующим образом:

  • Первый форк и setsid создадут новый сеанс (но идентификатор процесса == идентификатор сеанса).
  • Вторая ветвь удостоверяется, что идентификатор процесса! = Идентификатор сессии.