为什么fork()两次

时间:2012-06-07 13:15:34

标签: c debugging fork

Nagios让我配置child_processes_fork_twice=<0/1>

documentation

  

此选项确定Nagios在执行主机和服务检查时是否会fork()子进程两次。默认情况下,Nagios fork()两次。但是,如果启用了use_large_installation_tweaks选项,则只会fork()一次。

据我所知fork()会产生一个新的子进程。 为什么我要两次这样做?

5 个答案:

答案 0 :(得分:62)

好吧,现在首先:什么是僵尸进程?

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

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

通过这种方式,创建了一个僵尸进程。

现在让我们开始做生意。如何在这里分叉两次?

要注意的重要一点是,孙子做了父母过程想要孩子做的工作。

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

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

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

希望这有帮助!

答案 1 :(得分:20)

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

答案 2 :(得分:4)

同样来自documentation

  

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

答案 3 :(得分:4)

Unix Programming Faq§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信号,并具有   信号处理程序调用{​​{1}}或waitpid()。请参阅示例部分   完整的计划。

答案 4 :(得分:0)

此代码演示了如何使用双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进程完成时将被回收。