Nagios让我配置child_processes_fork_twice=<0/1>
。
文档说:
该选项确定Nagios在执行主机和服务检查时是否会fork()子进程两次。默认情况下,Nagios会fork()两次。但是,如果启用了use_large_installation_tweaks选项,则只会fork()一次。
据我所知,fork()
将生成一个新的子进程。为什么要做两次呢?
Nagios让我配置child_processes_fork_twice=<0/1>
。
文档说:
该选项确定Nagios在执行主机和服务检查时是否会fork()子进程两次。默认情况下,Nagios会fork()两次。但是,如果启用了use_large_installation_tweaks选项,则只会fork()一次。
据我所知,fork()
将生成一个新的子进程。为什么要做两次呢?
首先,什么是僵尸进程?
它是一个已经死亡的进程,但其父进程正在忙于执行其他任务,因此无法收集子进程的退出状态。
在某些情况下,子进程运行时间非常长,父进程无法等待那么久,并会继续执行其工作(请注意,父进程不会死亡,而是继续执行其剩余任务,但不关心子进程)。
这样就创建了一个僵尸进程。
现在让我们开始正题。为什么要进行两次fork才能解决问题?
需要注意的重要事项是孙子进程执行了父进程希望其子进程执行的工作。
现在第一次调用fork时,第一个子进程只是再次调用fork并退出。这样,父进程不必等待很长时间来收集子进程的退出状态(因为子进程的唯一工作是创建另一个子进程并退出)。因此,第一个子进程不会成为僵尸进程。
至于孙子进程,其父进程已经死亡。因此,孙子进程将被init
进程接管,后者始终收集其所有子进程的退出状态。现在父进程不必等待很长时间,也不会产生僵尸进程。
还有其他避免僵尸进程的方法;这只是一种常见技术。
希望这可以帮助!
在Linux中,通常通过fork两次并在中间进程退出后创建守护进程。这样会导致孙子进程成为孤儿进程。因此,如果进程终止,操作系统就需要负责清理它。原因与所谓的僵尸进程有关。这些进程由于它们的父进程已经死亡,通常应该负责清理工作,因此在退出后仍然存在并消耗资源。
此外,根据文档的说明:
通常情况下,当Nagios执行主机和服务检查时,它会fork()两次。这样做是为了(1)确保高度抗插件失效和段错误,并且(2)使操作系统在孙进程退出后清理孙进程。
Unix编程常见问题解答 §1.6.2:
1.6.2 How do I prevent them from occuring?
You need to ensure that your parent process calls
wait()
(orwaitpid()
,wait3()
, etc.) for every child process that terminates; or, on some systems, you can instruct the system that you are uninterested in child exit states.Another approach is to
fork()
twice, and have the immediate child process exit straight away. This causes the grandchild process to be orphaned, so the init process is responsible for cleaning it up. For code to do this, see the functionfork2()
in the examples section.To ignore child exit states, you need to do the following (check your system's manpages to see if this works):
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);
If this is successful, then the
wait()
functions are prevented from working; if any of them are called, they will wait until all child processes have terminated, then return failure witherrno == ECHILD
.The other technique is to catch the SIGCHLD signal, and have the signal handler call
waitpid()
orwait3()
. See the examples section for a complete program.
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);
}
}