维护的第一个商业服务就用了两次fork产生守护进程的做法,前两天在网上看到许多帖子以及一些unix书籍,认为一次fork后产生守护进程足够了,各有道理吧,不过多了一次fork到底是出于什么目的呢?
进程也就是task,看看内核里维护进程的数据结构task_struct,这里有两个成员:
[cpp]
struct task_struct { volatile long state; int exit_state; ... } 看看include/linux/sched.h里的value取值:
[cpp]
#define TASK_RUNNING 0 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define __TASK_STOPPED 4 #define __TASK_TRACED 8 /* in tsk->exit_state */ #define EXIT_ZOMBIE 16 #define EXIT_DEAD 32 /* in tsk->state again */ #define TASK_DEAD 64 #define TASK_WAKEKILL 128 #define TASK_WAKING 256 #define TASK_STATE_MAX 512 可以看到,进程状态里除了大家都理解的running/interuptible/uninterruptible/stop等状态外,还有一个ZOMBIE状态,这个状态是怎么回事呢?这是因为linux里的进程都属于一颗树,树的根结点是linux系统初始化结束阶段时启动的init进程,这个进程的pid是1,所有的其他进程都是它的子孙。除了init,任何进程一定有他的父进程,而父进程会负责分配(fork)、回收(wait4)它申请的进程资源。这个树状关系也比较健壮,当某个进程还在运行时,它的父进程却退出了,这个进程却没有成为孤儿进程,因为linux有一个机制,init进程会接管它,成为它的父进程。这也是守护进程的由来了,因为守护进程的其中一个要求就是希望init成为守护进程的父进程。
如果某个进程自身终止了,在调用exit清理完相关的内容文件等资源后,它就会进入ZOMBIE状态,它的父进程会调用wait4来回收这个task_struct,但是,如果父进程一直没有调用wait4去释放子进程的task_struct,问题就来了,这个task_struct谁来回收呢?永远没有人,除非父进程终止后,被init进程接管这个ZOMBIE进程,然后调用wait4来回收进程描述符。如果父进程一直在运行着,这个ZOMBIE会永远的占用系统资源,用KILL发任何信号量也不能释放它。这是很可怕的,因为服务器上可能会出现无数ZOMBIE进程导致机器挂掉。