Почему 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, когда он будет завершен.