23

Nagios lets me configure child_processes_fork_twice=<0/1>.

The documentation says

This option determines whether or not Nagios will fork() child processes twice when it executes host and service checks. By default, Nagios fork()s twice. However, if the use_large_installation_tweaks option is enabled, it will only fork() once.

As far as I know fork() will spawn a new child process. Why would I want to do that twice?

4

5 回答 5

75

好的,所以现在首先:什么是僵尸进程?

这是一个已经死亡的进程,但是它的父进程正忙于做一些其他的工作,因此它无法收集孩子的退出状态。

在某些情况下,孩子跑了很长时间,父母不能等那么久,并且会继续它的工作(注意,父母并没有死,而是继续其剩余的任务但不关心孩子)。

这样就创建了一个僵尸进程。


现在让我们开始谈正事。两次分叉在这里有什么帮助?

需要注意的重要一点是,孙子进程执行父进程希望其子进程执行的工作。

现在第一次调用 fork 时,第一个孩子只是再次分叉并退出。这样,父母不必等待很长时间才能收集孩子的退出状态(因为孩子唯一的工作就是创建另一个孩子并退出)。所以,第一个孩子不会变成僵尸。

至于孙子,它的父母已经去世了。因此,孙子进程将被init进程采用,该进程始终收集其所有子进程的退出状态。所以,现在父进程不用等待很长时间,也不会创建僵尸进程。


还有其他方法可以避免僵尸进程;这只是一种常见的技术。


希望这可以帮助!

于 2013-05-20T17:42:15.653 回答
24

在 Linux 中,通常通过分叉两次来创建守护进程,中间进程在分叉孙子后退出。这具有孤立孙进程的效果。因此,如果它终止,则操作系统有责任在它之后进行清理。原因与所谓的僵尸进程有关,它们在退出后继续生存并消耗资源,因为通常负责清理的父进程也已经死亡。

于 2012-06-07T13:22:48.920 回答
5

同样来自文档

通常 Nagios 在执行主机和服务检查时会 fork() 两次。这样做是为了 (1) 确保对出错和段错误的插件具有高水平的抵抗力,以及 (2) 使操作系统在孙子进程退出后处理清理它。

于 2012-06-07T13:20:41.577 回答
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()or wait3()。有关完整程序,请参阅示例部分。

于 2012-06-07T13:22:39.840 回答
2

这段代码演示了如何使用 doublefork方法让孙子进程被 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完成后将通过该过程回收。

于 2014-05-29T16:21:38.700 回答