谈谈守护进程与僵尸进程

来源:互联网 发布:网络机房接地电阻标准 编辑:程序博客网 时间:2024/04/29 13:04

04年时维护的第一个商业服务就用了两次fork产生守护进程的做法,前两天在网上看到许多帖子以及一些unix书籍,认为一次fork后产生守护进程足够了,各有道理吧,不过多了一次fork到底是出于什么目的呢?


进程也就是task,看看内核里维护进程的数据结构task_struct,这里有两个成员:

[cpp] view plaincopy
  1. struct task_struct {  
  2.     volatile long state;  
  3.     int exit_state;  
  4.     ...  
  5. }  

看看include/linux/sched.h里的value取值:
[cpp] view plaincopy
  1. #define TASK_RUNNING        0  
  2. #define TASK_INTERRUPTIBLE  1  
  3. #define TASK_UNINTERRUPTIBLE    2  
  4. #define __TASK_STOPPED      4  
  5. #define __TASK_TRACED       8  
  6. /* in tsk->exit_state */  
  7. #define EXIT_ZOMBIE     16  
  8. #define EXIT_DEAD       32  
  9. /* in tsk->state again */  
  10. #define TASK_DEAD       64  
  11. #define TASK_WAKEKILL       128  
  12. #define TASK_WAKING     256  
  13. #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进程导致机器挂掉。


来看看内核代码吧。进程在退出时执行sys_exit(C程序里在main函数返回会执行到),而它会调用do_exit,do_exit首先清理进程使用的资源,然后调用exit_notify方法,将进程置为僵尸ZOMBIE状态,决定是否要以init进程做为当前进程的父进程,最后通知当前进程的父进程:

kernel/exit.c

[cpp] view plaincopy
  1. static void exit_notify(struct task_struct *tsk)  
  2. {  
  3.     int state;  
  4.     struct task_struct *t;  
  5.     struct list_head ptrace_dead, *_p, *_n;  
  6.   
  7.     if (signal_pending(tsk) && !tsk->signal->group_exit  
  8.         && !thread_group_empty(tsk)) {  
  9.         /* 
  10.          * This occurs when there was a race between our exit 
  11.          * syscall and a group signal choosing us as the one to 
  12.          * wake up.  It could be that we are the only thread 
  13.          * alerted to check for pending signals, but another thread 
  14.          * should be woken now to take the signal since we will not. 
  15.          * Now we'll wake all the threads in the group just to make 
  16.          * sure someone gets all the pending signals. 
  17.          */  
  18.         read_lock(&tasklist_lock);  
  19.         spin_lock_irq(&tsk->sighand->siglock);  
  20.         for (t = next_thread(tsk); t != tsk; t = next_thread(t))  
  21.             if (!signal_pending(t) && !(t->flags & PF_EXITING)) {  
  22.                 recalc_sigpending_tsk(t);  
  23.                 if (signal_pending(t))  
  24.                     signal_wake_up(t, 0);  
  25.             }  
  26.         spin_unlock_irq(&tsk->sighand->siglock);  
  27.         read_unlock(&tasklist_lock);  
  28.     }  
  29.   
  30.     write_lock_irq(&tasklist_lock);  
  31.   
  32.     /* 
  33.      * This does two things: 
  34.      * 
  35.      * A.  Make init inherit all the child processes 
  36.      * B.  Check to see if any process groups have become orphaned 
  37.      *  as a result of our exiting, and if they have any stopped 
  38.      *  jobs, send them a SIGHUP and then a SIGCONT.  (POSIX 3.2.2.2) 
  39.      */  
  40.   
  41.     INIT_LIST_HEAD(&ptrace_dead);  
  42.     <strong><span style="color:#ff0000;">forget_original_parent(tsk, &ptrace_dead);</span></strong>  
  43.     BUG_ON(!list_empty(&tsk->children));  
  44.     BUG_ON(!list_empty(&tsk->ptrace_children));  
  45.   
  46.     /* 
  47.      * Check to see if any process groups have become orphaned 
  48.      * as a result of our exiting, and if they have any stopped 
  49.      * jobs, send them a SIGHUP and then a SIGCONT.  (POSIX 3.2.2.2) 
  50.      * 
  51.      * Case i: Our father is in a different pgrp than we are 
  52.      * and we were the only connection outside, so our pgrp 
  53.      * is about to become orphaned. 
  54.      */  
  55.        
  56.     t = tsk->real_parent;  
  57.       
  58.     if ((process_group(t) != process_group(tsk)) &&  
  59.         (t->signal->session == tsk->signal->session) &&  
  60.         will_become_orphaned_pgrp(process_group(tsk), tsk) &&  
  61.         has_stopped_jobs(process_group(tsk))) {  
  62.         __kill_pg_info(SIGHUP, (void *)1, process_group(tsk));  
  63.         __kill_pg_info(SIGCONT, (void *)1, process_group(tsk));  
  64.     }  
  65.   
  66.     /* Let father know we died  
  67.      * 
  68.      * Thread signals are configurable, but you aren't going to use 
  69.      * that to send signals to arbitary processes.  
  70.      * That stops right now. 
  71.      * 
  72.      * If the parent exec id doesn't match the exec id we saved 
  73.      * when we started then we know the parent has changed security 
  74.      * domain. 
  75.      * 
  76.      * If our self_exec id doesn't match our parent_exec_id then 
  77.      * we have changed execution domain as these two values started 
  78.      * the same after a fork. 
  79.      *   
  80.      */  
  81.       
  82.     if (tsk->exit_signal != SIGCHLD && tsk->exit_signal != -1 &&  
  83.         ( tsk->parent_exec_id != t->self_exec_id  ||  
  84.           tsk->self_exec_id != tsk->parent_exec_id)  
  85.         && !capable(CAP_KILL))  
  86.         tsk->exit_signal = SIGCHLD;  
  87.   
  88.   
  89.     /* If something other than our normal parent is ptracing us, then 
  90.      * send it a SIGCHLD instead of honoring exit_signal.  exit_signal 
  91.      * only has special meaning to our real parent. 
  92.      */  
  93.     if (tsk->exit_signal != -1 && thread_group_empty(tsk)) {  
  94.         int signal = tsk->parent == tsk->real_parent ? tsk->exit_signal : SIGCHLD;  
  95.         <span style="color:#ff0000;">do_notify_parent(tsk, signal);</span>  
  96.     } else if (tsk->ptrace) {  
  97.         do_notify_parent(tsk, SIGCHLD);  
  98.     }  
  99.   
  100.     <span style="color:#ff0000;"><strong>state = EXIT_ZOMBIE;</strong></span>  
  101.     if (tsk->exit_signal == -1 && tsk->ptrace == 0)  
  102.         state = EXIT_DEAD;  
  103.     tsk->exit_state = state;  
  104.   
  105.     /* 
  106.      * Clear these here so that update_process_times() won't try to deliver 
  107.      * itimer, profile or rlimit signals to this task while it is in late exit. 
  108.      */  
  109.     tsk->it_virt_value = 0;  
  110.     tsk->it_prof_value = 0;  
  111.   
  112.     write_unlock_irq(&tasklist_lock);  
  113.   
  114.     list_for_each_safe(_p, _n, &ptrace_dead) {  
  115.         list_del_init(_p);  
  116.         t = list_entry(_p,struct task_struct,ptrace_list);  
  117.         release_task(t);  
  118.     }  
  119.   
  120.     /* If the process is dead, release it - nobody will wait for it */  
  121.     if (state == EXIT_DEAD)  
  122.         release_task(tsk);  
  123.   
  124.     /* PF_DEAD causes final put_task_struct after we schedule. */  
  125.     preempt_disable();  
  126.     tsk->flags |= PF_DEAD;  
  127. }  

大家可以看到这段内核代码的注释非常全。forget_original_parent这个函数还会把该进程的所有子孙进程重设父进程,交给init进程接管。


回过头来,看看为什么守护进程要fork两次。这里有一个假定,父进程生成守护进程后,还有自己的事要做,它的人生意义并不只是为了生成守护进程。这样,如果父进程fork一次创建了一个守护进程,然后继续做其它事时阻塞了,这时守护进程一直在运行,父进程却没有正常退出。如果守护进程因为正常或非正常原因退出了,就会变成ZOMBIE进程。

如果fork两次呢?父进程先fork出一个儿子进程,儿子进程再fork出孙子进程做为守护进程,然后儿子进程立刻退出,守护进程被init进程接管,这样无论父进程做什么事,无论怎么被阻塞,都与守护进程无关了。所以,fork两次的守护进程很安全,避免了僵尸进程出现的可能性。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 最爱最恨都是你怎么办 耳朵长疱疹很痛怎么办 刚怀孕就有霉菌怎么办 车牌选错了想换怎么办 足癣传染到身上怎么办 金鱼生病了立鳞怎么办 脸上有闭口痘痘怎么办 泰迪得了皮肤癣怎么办 qq截图发不出去怎么办 半夜2点肚子饿怎么办 半夜两三点饿了怎么办 科目三预约不上怎么办 心脏支架又堵了怎么办 车辆验车过期了怎么办 车辆年审过期2天怎么办 违停单子丢了怎么办 沙发弹簧包坏了怎么办 bt种子被和谐了怎么办 苹果手机id密码忘了怎么办 墙漆颜色太深了怎么办 油漆颜色太深了怎么办 墙面漆颜色太深怎么办 古代打仗牙掉了怎么办 乐视电视没声音怎么办 老公疑心病很重怎么办啊 被安装了尿道锁怎么办 狼青小狗腿罗圈怎么办 备孕期间有霉菌怎么办 虫子进皮肤里了怎么办 生完孩子肚子越来越大怎么办 怀孕8个月肚子小怎么办 孕晚期胎儿不长怎么办 肚子上肉特别多怎么办 奶堵了有硬块怎么办 便秘5天肚子胀怎么办 上火大便拉不出来怎么办 大便拉不出来肚子痛怎么办 戒奶奶涨有硬块怎么办 忌奶的时候涨奶怎么办 娃儿隔奶,,奶涨怎么办 狗肚子很大很鼓怎么办