Почему fork() дважды
Nagios позволяет мне настроить child_processes_fork_twice=<0/1>
.
документация говорит
Этот параметр определяет, будет ли Nagios дочерний процесс fork() обрабатывать два раза, когда он выполняет проверки хоста и службы. По умолчанию Nagios fork() s дважды. Однако если параметр use_large_installation_tweaks включен, он будет только fork() один раз.
Насколько я знаю, fork()
создаст новый дочерний процесс. Зачем мне это делать дважды?
Ответы
Ответ 1
В Linux демона обычно создаются путем двухкратного разворачивания промежуточного процесса, выходящего после раздвоения внука. Это приводит к сиротству процесса внука. В результате, ОС становится ответственным за очистку после нее, если она завершается. Причина связана с тем, что известно как зомби-процессы, которые продолжают жить и потребляют ресурсы после выхода, поскольку их родитель, который обычно несет ответственность за очистку, также умер.
Ответ 2
Хорошо, так что теперь, прежде всего: что такое зомби-процесс?
Это процесс, который мертв, но его родитель был занят выполнением какой-то другой работы, поэтому он не смог собрать статус выхода ребенка,
В некоторых случаях ребенок работает очень долго, родитель не может дождаться этого долгого времени и продолжит его работу (обратите внимание, что родитель не умирает, но продолжает свои оставшиеся задачи, но не делает не заботьтесь о ребенке).
Таким образом, создается зомбический процесс.
Теперь давайте приступим к делу. Как разворачивается двойная помощь здесь?
Важно отметить, что внук выполняет работу, которую родительский процесс хочет сделать своим ребенком.
Теперь, когда вызывается первый раз fork, первый ребенок просто вилки снова и выходит, Таким образом, родитель не должен долго ждать, чтобы получить статус выхода ребенка (так как задание только для ребенка - создать другого ребенка и выйти). Итак, первый ребенок не станет зомби.
Что касается внука, его родитель уже умер. Следовательно, внук будет принят процессом init
, который всегда собирает статус выхода всех его дочерних процессов. Итак, теперь родитель не должен ждать очень долго, и никакой зомби-процесс не будет создан.
Есть другие способы избежать зомби-процесса; это всего лишь обычная техника.
Надеюсь, это поможет!
Ответ 3
Также из документации,
Обычно Nagios будет fork() дважды, когда он выполняет проверки хоста и службы. Это делается для того, чтобы (1) обеспечить высокий уровень сопротивления плагинам, которые идут наперекосяк и segfault, и (2) заставить ОС заниматься очисткой процесса внука после его выхода.
Ответ 4
Вопрос о программировании Unix §1.6.2:
1.6.2 Как я могу предотвратить их появление?
Вам нужно убедиться, что ваш родительский процесс вызывает wait()
(или waitpid()
, wait3()
и т.д.) для каждого дочернего процесса, который завершается; или, в некоторых системах, вы можете проинструктировать систему, что вы не интересуется состояниями выхода ребенка.
Другой подход заключается в fork()
дважды и имеет непосредственный дочерний немедленно выйдите из процесса. Это приводит к тому, что процесс внука сирот, поэтому процесс init отвечает за его очистку. Для кода для этого, см. функцию fork2()
в разделе примеров.
Чтобы игнорировать состояния дочернего выхода, вам необходимо сделать следующее (проверьте система видит, работает ли это):
struct sigaction sa;
sa.sa_handler = SIG_IGN;
#ifdef SA_NOCLDWAIT
sa.sa_flags = SA_NOCLDWAIT;
#else
sa.sa_flags = 0;
#endif
sigemptyset(&sa.sa_mask);
sigaction(SIGCHLD, &sa, NULL);
Если это успешно, то функции wait()
не могут за работой; если какой-либо из них вызывается, они будут ждать, пока все дочерние процессы завершались, а затем возвращались с ошибкой errno == ECHILD
.
Другой метод состоит в том, чтобы поймать сигнал SIGCHLD и иметь вызов обработчика сигнала waitpid()
или wait3()
. См. Раздел примеров для полной программы.
Ответ 5
Этот код демонстрирует, как использовать метод double fork
, чтобы позволить процессу внука стать принятым init без риска возникновения процессов зомби.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
int main()
{
pid_t p1 = fork();
if (p1 != 0)
{
printf("p1 process id is %d", getpid());
wait();
system("ps");
}
else
{
pid_t p2 = fork();
int pid = getpid();
if (p2 != 0)
{
printf("p2 process id is %d", pid);
}
else
{
printf("p3 process id is %d", pid);
}
exit(0);
}
}
Родитель будет fork
новый дочерний процесс, а затем wait
для его завершения. Ребенок будет fork
процессом внука, а затем exit(0)
.
В этом случае внук не делает ничего, кроме exit(0)
, но может быть сделан, чтобы делать все, что вам нужно, чтобы процесс демона. Внук может долго жить и будет восстановлен процессом init
, когда он будет завершен.