Chinaunix首页 | 论坛 | 博客
  • 博客访问: 510948
  • 博文数量: 68
  • 博客积分: 5011
  • 博客等级: 大校
  • 技术积分: 806
  • 用 户 组: 普通用户
  • 注册时间: 2007-11-30 22:06
文章分类
文章存档

2011年(1)

2009年(8)

2008年(59)

我的朋友

分类: LINUX

2008-08-11 12:56:20

如果父进程在子进程之前退出,linux当然回重新为它找到新的父亲(可见linux是很温暖的)。否则这些
孤儿进程就会在退出时成为永远的僵死进程(wait4函数没有执行,僵死进程的进程描述符并为释放)。
解决办法是给子进程在当前线程组内找一个线程作为父亲,如果不行就让init做它们的父亲。
在父进程do_exit()最终会调用forget_original_parent()为孤儿进程找到父亲。

forget_original_parent()定义:linux/kernel/exit.c
 673/*
 674 * When we die, we re-parent all our children.
 675 * Try to give them to another thread in our thread
 676 * group, and if no such member exists, give it to
 677 * the child reaper process (ie "init") in our pid
 678 * space.
 679 */
 
 680static void
 681forget_original_parent(struct task_struct *father, struct list_head *to_release)
 682{
 683        struct task_struct *p, *reaper = father;
 684        struct list_head *_p, *_n;
 685
 686        do {
 687                reaper = next_thread(reaper);
 688                if (reaper == father) {
 689                        reaper = child_reaper(father);
 690                        break;
 691                }
 692        } while (reaper->exit_state);
 
     /**next_thread(reaper)找到当前线程组另一个线程。如果if判断条件不成立,找到新线程是孤儿进程新父亲
        if条件成立,那么说明当前线程组没有其他线程,那么只有init为其父进程
     
 694        /*
 695         * There are only two places where our children can be:
 696         *
 697         * - in our child list
 698         * - in our ptraced child list
 699         *
 700         * Search them and reparent children.
 701         */
 702        list_for_each_safe(_p, _n, &father->children) {
 703                int ptrace;
 704                p = list_entry(_p, struct task_struct, sibling);
 706                ptrace = p->ptrace;
 707
 708                /* if father isn't the real parent, then ptrace must be enabled */
 
         /**当前退出的线程不是其真正的父亲,那么必然是被跟踪的进程.否则在系统日志打印错误。
 709                BUG_ON(father != p->real_parent && !ptrace);
 
           
              
 /**如果当前退出进程是其真正的父亲,为退出进程的子进程设置新的父进程
 
 711                if (father == p->real_parent) {
 712                        /* reparent with a reaper, real father it's us */
 713                        choose_new_parent(p, reaper);
 714                        reparent_thread(p, father, 0);
 715                }
          
          /**如果当前EXIT_ZOMEBIE状态.父进程关系发生了改变,则应该向新的父进程发送信号
                   
                  else {
 716                        /* reparent ptraced task to its real parent */
            
         /**本代码进程是跟踪即将退出的进程,被挂到退出进程的子链表上,后面详细解释.
           _ptrace_unlink(p)解除与退出进程的父子关系(因为本进程即将退出),并挂到生父进程的子链表中。
          
 717                        __ptrace_unlink (p);
 718                        if (p->exit_state == EXIT_ZOMBIE && p->exit_signal != -1 &&
 719                            thread_group_empty(p))
 720                                do_notify_parent(p, p->exit_signal);
 721                }
 
        /**如果子进程为僵尸状态,且退出时不给父进程信号就将其收集起来,到时候统一退出处理
                  
 723                /*
 724                 * if the ptraced child is a zombie with exit_signal == -1
 725                 * we must collect it before we exit, or it will remain
 726                 * zombie forever since we prevented it from self-reap itself
 727                 * while it was being traced by us, to be able to see it in wait4.
 728                 */
 729                if (unlikely(ptrace && p->exit_state == EXIT_ZOMBIE && p->exit_signal == -1))
 730                        list_add(&p->ptrace_list, to_release);
 731        }
 
         /**为当前退出进程,跟踪子进程设置新的父亲
 
 732        list_for_each_safe(_p, _n, &father->ptrace_children) {
 733                p = list_entry(_p, struct task_struct, ptrace_list);
 734                choose_new_parent(p, reaper);
 735                reparent_thread(p, father, 1);
 736        }
 737}


在694后续代码,主要遍历了子进程链表(father->children)和跟踪子进程链表(father->ptrace_children),给每个子进程设置
新的父进程。当一个进程被跟踪时,它被暂且设为调式进程的子进程。此时如果父进程退出,系统会为它们重新找到一个父进程。

对部分代码详细解释:

  对BUG_ON定义:
  23#ifndef HAVE_ARCH_BUG
  24#define BUG() do { \
  25        printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __FUNCTION__); \
  26        panic("BUG!"); \
  27} while (0)
  28#endif
 
  30#ifndef HAVE_ARCH_BUG_ON
  31#define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while(0)

 
  作用:一些内核调用可以用来方便标记bug,提供断言并输出信息。最长用的两个是BUG()和BUG_ON()。当被调用的时候,它们会引发oops,导致栈的回溯和错误信息的打印。为什么这些声明会导致oops跟硬件的体系结构是相关的。大部分体系结构把BUG()和BUG_ON()定义成某种非法操作,这样自然会产生需要的oops。你可以把这些调用当作断言使用,想要断言某种情况不该发生:
if (bad_thing)
       BUG();
或者使用更好的形式:
BUG_ON(bad_thing);

了解这个函数得区分几种子进程:
       1    parent == real_parent 的子进程
             这是本进程的子进程,并且没有被ptrace,处于本进程的children list上。
       2   parent == father && parent != real_parent的子进程
            被本进程ptrace的其它进程的子进程,处于本进程的children list上。
       3   real_parent == father && parent != real_parent的子进程
            本进程的子进程,但正在被其它进程ptrace,处于本进程的ptrace_children list上。
                       
其中代码711~715和732~737对上述的(1)和(3)分别进程操作,设置其父进程为新找的父进程。
choose_new_parent()只是为子进程设置新的父亲,这个源代码很简单,看完我信心倍增。

注意:716~721是对第2种状态操作,和退出进程解除跟踪关系,将跟踪进程加入其生父进程。
阅读(2933) | 评论(1) | 转发(1) |
给主人留下些什么吧!~~

chinaunix网友2008-08-14 10:43:00

看完你的分析,我也信心大增,对你们阅读源代码的能力!