zoukankan      html  css  js  c++  java
  • do_exit——>exit_notify()【转】

    [cpp] view plain copy
     
    1. /* 
    2.  * Send signals to all our closest relatives so that they know 
    3.  * to properly mourn(悼念) us.. 
    4.  */  
    5. static void exit_notify(struct task_struct *tsk, int group_dead)  
    6. {  
    7.     int signal;  
    8.     void *cookie;  
    9.   
    10.     /* 
    11.      * This does two things: 
    12.      * 
    13.      * A.  Make init inherit all the child processes 
    14.      * B.  Check to see if any process groups have become orphaned 
    15.      *  as a result of our exiting, and if they have any stopped 
    16.      *  jobs, send them a SIGHUP and then a SIGCONT.  (POSIX 3.2.2.2) 
    17.      */  
    18.     forget_original_parent(tsk);  
    19.     exit_task_namespaces(tsk);  //将task的namespace置为NULL  
    20.   
    21.     write_lock_irq(&tasklist_lock);  
    22.     if (group_dead)  
    23.         kill_orphaned_pgrp(tsk->group_leader, NULL);  
    24.   
    25.     /* Let father know we died 
    26.      * 
    27.      * Thread signals are configurable, but you aren't going to use 
    28.      * that to send signals to arbitrary processes. 
    29.      * That stops right now. 
    30.      * 
    31.      * If the parent exec id doesn't match the exec id we saved 
    32.      * when we started then we know the parent has changed security 
    33.      * domain. 
    34.      * 
    35.      * If our self_exec id doesn't match our parent_exec_id then 
    36.      * we have changed execution domain as these two values started 
    37.      * the same after a fork. 
    38.      */  
    39.     if (tsk->exit_signal != SIGCHLD && !task_detached(tsk) &&  
    40.         (tsk->parent_exec_id != tsk->real_parent->self_exec_id ||  
    41.          tsk->self_exec_id != tsk->parent_exec_id))  
    42.         tsk->exit_signal = SIGCHLD;  
    43.   
    44.     signal = tracehook_notify_death(tsk, &cookie, group_dead);  
    45.     if (signal >= 0)  
    46.         signal = do_notify_parent(tsk, signal);  
    47.   
    48.     tsk->exit_state = signal == DEATH_REAP ? EXIT_DEAD : EXIT_ZOMBIE;  
    49.   
    50.     /* mt-exec, de_thread() is waiting for group leader */  
    51.     if (unlikely(tsk->signal->notify_count < 0))  
    52.         wake_up_process(tsk->signal->group_exit_task);  
    53.     write_unlock_irq(&tasklist_lock);  
    54.   
    55.     tracehook_report_death(tsk, signal, cookie, group_dead);  
    56.   
    57.     /* If the process is dead, release it - nobody will wait for it */  
    58.     if (signal == DEATH_REAP)  
    59.         release_task(tsk);  
    60. }  

    如这个函数开头注释的,这个函数负责通知所有与这个进程相关的亲属,以便让他们做出相应的改变,或者“悼念”即将死去的进程。

    我们碰到的第一个重要的函数就是:forget_original_parent()

    注释中给出了这个函数的作用:(1)让init进程继承这个进程的所有子进程;(2)检查该进程所在的进程组在该进程死后是否会变为孤儿进程组,如果他们已经停止了工作,

    则想他们先后发送SIGHUP信号和SIGCONT信号。

    在这个函数中,关键要搞清楚进程的“养父”进程——p_pptr和“生父进程”——p_optr的关系,详细的解释可以看《情景分析》 P343。

    [cpp] view plain copy
     
    1. static void forget_original_parent(struct task_struct *father)  
    2. {  
    3.     struct task_struct *p, *n, *reaper;  
    4.     LIST_HEAD(dead_children);  
    5.   
    6.     write_lock_irq(&tasklist_lock);  
    7.     /* 
    8.      * Note that exit_ptrace() and find_new_reaper() might 
    9.      * drop tasklist_lock and reacquire it. 
    10.      */  
    11.     exit_ptrace(father);  
    12. /* 
    13.  * Detach all tasks we were using ptrace on. Called with tasklist held 
    14.  * for writing, and returns with it held too. But note it can release 
    15.  * and reacquire the lock. 
    16.  */  
    17.     reaper = find_new_reaper(father); //为即将死去的进程的子进程寻找一个父进程。这个函数的代码还是比较简单的。  
    18. /* 
    19.  * When we die, we re-parent all our children. 
    20.  * Try to give them to another thread in our thread 
    21.  * group, and if no such member exists, give it to 
    22.  * the child reaper process (ie "init") in our pid 
    23.  * space. 
    24.  */  
    25.     list_for_each_entry_safe(p, n, &father->children, sibling) { //逐个处理将死进程的子进程  
    26.         struct task_struct *t = p;  
    27.         do {  
    28.             t->real_parent = reaper;  //将将死进程的子进程的兄弟进程的real_parent指向reaper进程  
    29.             if (t->parent == father) {  
    30.                 BUG_ON(task_ptrace(t));  
    31.                 t->parent = t->real_parent;  
    32.             }  
    33.             if (t->pdeath_signal)  
    34.                 group_send_sig_info(t->pdeath_signal,  
    35.                             SEND_SIG_NOINFO, t); //发送SEND_SIG_NOINFO信号,注意task_struct结构中的pdeath_dignal就是当父进程死亡的时候才会被设置,也就是这个时候。  
    36.         } while_each_thread(p, t); //逐个处理将死进程的子进程的兄弟进程  
    37.         reparent_leader(father, p, &dead_children);//Any that need to be release_task'd are put on the @dead list.见下文详解:  
    38.     }  
    39.     write_unlock_irq(&tasklist_lock);  
    40.   
    41.     BUG_ON(!list_empty(&father->children));  
    42.   
    43.     list_for_each_entry_safe(p, n, &dead_children, sibling) {  
    44.         list_del_init(&p->sibling);  //?????????????????????????????????将p从兄弟队列中摘除,<span style="color:#FF0000;">但是兄弟关系是怎么产生的呢?</span>  
    45.         release_task(p);//做一些收尾工作,但是并未将task_struct结构删除,因为后者是其新的父进程的任务。  
    46.     }  
    47. }  

    在forget_original_parent函数里边,为什么是两个循环来处理其子进程呢?我是因为父进程的子进程不一定在同一个slibing队列中,比如我们前文讲到的,如果是因为杀死了某个

    进程A,而将A进程的子进程C的父进程指向了进程B,但是,我们并没有看到B进程的子进程D的slibing队列有什么变化,因此,D的slibing队列应该是没有将C进程链接进去的,因此就出现了这样的情况,即父进程有多个子进程,但是这些某些子进程之间可能是相互独立的,因此需要两层循环来将要撤销的父进程的所有子进程的父进程指向reaper.

    来看一下forget_original_parent()中一个重要的函数reparent_leader():

    [cpp] view plain copy
     
    1. /* 
    2. * Any that need to be release_task'd are put on the @dead list. 
    3.  */  
    4. static void reparent_leader(struct task_struct *father, struct task_struct *p,  
    5.                 struct list_head *dead)  
    6. {  
    7.     list_move_tail(&p->sibling, &p->real_parent->children); //将进程p从其现有的兄弟队列中退出来,并将其挂到新的real_parent进程的children队列上。  
    8.   
    9.     if (task_detached(p)) //如果进程已经脱离了,即p->exit_signal == -1,那么就直接返回。  
    10.         return;  
    11.     /* 
    12.      * If this is a threaded reparent there is no need to 
    13.      * notify anyone anything has happened. 
    14.      */  
    15.     if (same_thread_group(p->real_parent, father)) //如果将死进程的子进程的新的父进程和将死进程同数一个线程组,那么就返回。因为不需要杀死这个线程组。  
    16.                                                        //有关进程组/线程组/会话的知识请参看《UNIX环境高级编程》相关章节  
    17.         return;  
    18.   
    19.     /* We don't want people slaying init.  */  
    20.     p->exit_signal = SIGCHLD;  
    21.   
    22.     /* If it has exited notify the new parent about this child's death. */  
    23.     if (!task_ptrace(p) &&  
    24.         p->exit_state == EXIT_ZOMBIE && thread_group_empty(p)) { //如果将死进程的子进程没有被跟踪,且其状态是僵死状态并且所在线程组已经是空的了  
    25.         do_notify_parent(p, p->exit_signal); //则 Let a parent know about the death of a child. 下文将详述该函数。  
    26.         if (task_detached(p)) {  
    27.             p->exit_state = EXIT_DEAD;  
    28. /* 
    29.  *EXIT_DEAD is the state after an appropriate wait system call has been issued and before the task 
    30.  *is completely removed from the system. This state is only of importance if multiple threads issue 
    31.  *wait calls for the same task. 
    32.  *EXIT_DEAD状态和EXIT_ZOMBIE状态的区别可以参看《深入Linux内核框架》 
    33. */  
    34.             list_move_tail(&p->sibling, dead); //<span style="color:#FF0000;">将进程p从其sibling队列中删除并添加到dead队列中。子进程间的兄弟关系是怎么建立的呢?</span>  
    35.         }  
    36.     }  
    37.   
    38.     kill_orphaned_pgrp(p, father);  //一个重要的函数,下文详述:  
    39. }  

    来看一下reparent_leader函数中的一个重要的函数do_notify_parent():如果在进程没有被跟踪,并且其状态已经是僵死状态,而且其所在的线程组已经是空的了,那就用这个函数向其父进程发送一个信号,让其知道子进程的生命已经结束,而来料理后事儿:

    [cpp] view plain copy
     
    1. /* 
    2.  * Let a parent know about the death of a child. 
    3.  * For a stopped/continued status change, use do_notify_parent_cldstop instead. 
    4.  * 
    5.  * Returns -1 if our parent ignored us and so we've switched to 
    6.  * self-reaping, or else @sig. 
    7.  */  
    8. int do_notify_parent(struct task_struct *tsk, int sig)  
    9. {  
    10.     struct siginfo info;  
    11.     unsigned long flags;  
    12.     struct sighand_struct *psig;  
    13.     int ret = sig;  
    14.   
    15.     BUG_ON(sig == -1);  
    16.   
    17.     /* do_notify_parent_cldstop should have been called instead.  */  
    18.     BUG_ON(task_is_stopped_or_traced(tsk));  
    19.   
    20.     BUG_ON(!task_ptrace(tsk) &&  
    21.            (tsk->group_leader != tsk || !thread_group_empty(tsk)));  
    22.   
    23.     info.si_signo = sig;  
    24.     info.si_errno = 0;  
    25.     /* 
    26.      * we are under tasklist_lock here so our parent is tied to 
    27.      * us and cannot exit and release its namespace. 
    28.      * 
    29.      * the only it can is to switch its nsproxy with sys_unshare, 
    30.      * bu uncharing pid namespaces is not allowed, so we'll always 
    31.      * see relevant namespace 
    32.      * 
    33.      * write_lock() currently calls preempt_disable() which is the 
    34.      * same as rcu_read_lock(), but according to Oleg, this is not 
    35.      * correct to rely on this 
    36.      */  
    37.     rcu_read_lock();  
    38.     info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns);  
    39.     info.si_uid = __task_cred(tsk)->uid;  
    40.     rcu_read_unlock();  
    41.   
    42.     info.si_utime = cputime_to_clock_t(cputime_add(tsk->utime,  
    43.                 tsk->signal->utime));  
    44.     info.si_stime = cputime_to_clock_t(cputime_add(tsk->stime,  
    45.                 tsk->signal->stime));  
    46.   
    47.     info.si_status = tsk->exit_code & 0x7f;  
    48.     if (tsk->exit_code & 0x80)  
    49.         info.si_code = CLD_DUMPED;  
    50.     else if (tsk->exit_code & 0x7f)  
    51.         info.si_code = CLD_KILLED;  
    52.     else {  
    53.         info.si_code = CLD_EXITED;  
    54.         info.si_status = tsk->exit_code >> 8;  
    55.     }  
    56.   
    57.     psig = tsk->parent->sighand;  
    58.     spin_lock_irqsave(&psig->siglock, flags);  
    59.     if (!task_ptrace(tsk) && sig == SIGCHLD &&  
    60.         (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN ||  
    61.          (psig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT))) {  
    62.         /* 
    63.          * We are exiting and our parent doesn't care.  POSIX.1 
    64.          * defines special semantics for setting SIGCHLD to SIG_IGN 
    65.          * or setting the SA_NOCLDWAIT flag: we should be reaped 
    66.          * automatically and not left for our parent's wait4 call. 
    67.          * Rather than having the parent do it as a magic kind of 
    68.          * signal handler, we just set this to tell do_exit that we 
    69.          * can be cleaned up without becoming a zombie.  Note that 
    70.          * we still call __wake_up_parent in this case, because a 
    71.          * blocked sys_wait4 might now return -ECHILD. 
    72.          * 
    73.          * Whether we send SIGCHLD or not for SA_NOCLDWAIT 
    74.          * is implementation-defined: we do (if you don't want 
    75.          * it, just use SIG_IGN instead). 
    76.          */  
    77.         ret = tsk->exit_signal = -1;  
    78.         if (psig->action[SIGCHLD-1].sa.sa_handler == SIG_IGN)  
    79.             sig = -1;  
    80.     }  
    81.     if (valid_signal(sig) && sig > 0)  
    82.         __group_send_sig_info(sig, &info, tsk->parent);  
    83.     __wake_up_parent(tsk, tsk->parent);  
    84.     spin_unlock_irqrestore(&psig->siglock, flags);  
    85.   
    86.     return ret;  
    87. }  

    看一下reparent_leader的最后调用的一个函数kill_orphanded_pgrp:
    [cpp] view plain copy
     
    1. /* 
    2.  * Check to see if any process groups have become orphaned as 
    3.  * a result of our exiting, and if they have any stopped jobs, 
    4.  * send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2) 
    5.  */  
    6. static void  
    7. kill_orphaned_pgrp(struct task_struct *tsk, struct task_struct *parent)  
    8. {  
    9.     struct pid *pgrp = task_pgrp(tsk);  
    10.     struct task_struct *ignored_task = tsk;  
    11.   
    12.     if (!parent)  
    13.          /* exit: our father is in a different pgrp than 
    14.           * we are and we were the only connection outside. 
    15.           */  
    16.         parent = tsk->real_parent;  
    17.     else  
    18.         /* reparent: our child is in a different pgrp than 
    19.          * we are, and it was the only connection outside. 
    20.          */  
    21.         ignored_task = NULL;  
    22.   
    23.     if (task_pgrp(parent) != pgrp &&  //进程tsk和其父进程不属于同一个组  
    24.         task_session(parent) == task_session(tsk) && //进程tsk和其父进程属于一个会话  
    25.         will_become_orphaned_pgrp(pgrp, ignored_task) && //进程组将会成为孤儿******************I ask you, have you ever kown what it is to be an orphan?******************************  
    26.         has_stopped_jobs(pgrp)) { //是否有被停止的job,被停止的原因可能是因为debugger,可以参看《深入Linux内核框架》  
    27.         __kill_pgrp_info(SIGHUP, SEND_SIG_PRIV, pgrp); //向进程组发送SIGHUP信号,干什么用呢?  
    28.         __kill_pgrp_info(SIGCONT, SEND_SIG_PRIV, pgrp);//向进程组发送SIGCONT信号,干什么用呢?  
    29.     }  
    30. }  

    那么怎么判断进程组将变成孤儿呢?在kill_orphanded_pgrp中will_become_orphanded_pgrp完成了这项工作:

    [cpp] view plain copy
     
    1. /* 
    2.  * Determine if a process group is "orphaned", according to the POSIX 
    3.  * definition in 2.2.2.52.  Orphaned process groups are not to be affected 
    4.  * by terminal-generated stop signals.  Newly orphaned process groups are 
    5.  * to receive a SIGHUP and a SIGCONT. 
    6.  * 
    7.  * "I ask you, have you ever known what it is to be an orphan?" 
    8.  */  
    9. static int will_become_orphaned_pgrp(struct pid *pgrp, struct task_struct *ignored_task)  
    10. {  
    11.     struct task_struct *p;  
    12.   
    13.     do_each_pid_task(pgrp, PIDTYPE_PGID, p) { //循环查询线程组中的每一个线程  
    14.         if ((p == ignored_task) ||  //<span style="color:#FF0000;">如果p是pgrp中的一员??</span>  
    15.             (p->exit_state && thread_group_empty(p)) || //如果pgrp中的p进程正处于退出状态而且其所在的线程组是空的了  
    16.             is_global_init(p->real_parent)) //或者pgrp中p进程的父进程是init进程,说明在该线程组中已经没有其他线程了(即list_empty(&p->thread_group) == 1;)。  
    17.             continue;  
    18.   
    19.         if (task_pgrp(p->real_parent) != pgrp && //如果p的父进程和p不再同一个组中  
    20.             task_session(p->real_parent) == task_session(p)) //但是p的父进程和p进程功属于一个会话  
    21.             return 0; //返回0,不会变成孤儿  
    22.     } while_each_pid_task(pgrp, PIDTYPE_PGID, p);  
    23.   
    24.     return 1; //返回1,会变成孤儿。  
    25. }  

    现在,我们先来总结一下有关孤儿进程的东西,详细的解释可以看《情景分析》:

    进程的“生父(p_opptr)”进程和“养父(p_pptr)”进程通常是一致的,但是如果进程被跟踪时,p_pptr就会指向跟踪进程,而p_opptr的指向不变。当一个进程在其子进程之前去世时,就要把他的子进程托付给某个进程。如果当前进程是一个线程,那就托付给同一线程组的下一个线程,是子进程的p_opptr指向这个线程。否则就只好托付给init进程了。如果当前进程和生父进程属于不同的session,不同的组,同时又是其所在组与其父进程之间的唯一纽带,那么一旦当前进程不存在了,这整个组就会变成孤儿。

  • 相关阅读:
    mybatis使用说明
    maven相关说明,以及使用Testng相关
    java注解相关
    maven安装,使用说明,及maven Repository如何使用.
    css相关知识
    Spring Ioc 基于Java的容器配置
    Spring DI的配置使用
    Spring —IoC的配置使用
    Spring IoC 之IoC容器的基本原理
    Spring IOC之IOC基础
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/5587293.html
Copyright © 2011-2022 走看看