Nagios让我配置child_processes_fork_twice=<0/1>
。
此选项确定Nagios在执行主机和服务检查时是否会fork()子进程两次。默认情况下,Nagios fork()两次。但是,如果启用了use_large_installation_tweaks选项,则只会fork()一次。
据我所知fork()
会产生一个新的子进程。 为什么我要两次这样做?
答案 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
进程完成时将被回收。