转自:http://blog.csdn.net/sunnybeike/article/details/6907322

版权声明:本文为博主原创文章,未经博主允许不得转载。

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

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

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

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

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

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

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

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

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

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

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

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

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

看一下reparent_leader的最后调用的一个函数kill_orphanded_pgrp:

  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. if (!parent)
  12. /* exit: our father is in a different pgrp than
  13. * we are and we were the only connection outside.
  14. */
  15. parent = tsk->real_parent;
  16. else
  17. /* reparent: our child is in a different pgrp than
  18. * we are, and it was the only connection outside.
  19. */
  20. ignored_task = NULL;
  21. if (task_pgrp(parent) != pgrp &&  //进程tsk和其父进程不属于同一个组
  22. task_session(parent) == task_session(tsk) && //进程tsk和其父进程属于一个会话
  23. will_become_orphaned_pgrp(pgrp, ignored_task) && //进程组将会成为孤儿******************I ask you, have you ever kown what it is to be an orphan?******************************
  24. has_stopped_jobs(pgrp)) { //是否有被停止的job,被停止的原因可能是因为debugger,可以参看《深入Linux内核框架》
  25. __kill_pgrp_info(SIGHUP, SEND_SIG_PRIV, pgrp); //向进程组发送SIGHUP信号,干什么用呢?
  26. __kill_pgrp_info(SIGCONT, SEND_SIG_PRIV, pgrp);//向进程组发送SIGCONT信号,干什么用呢?
  27. }
  28. }

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

  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. do_each_pid_task(pgrp, PIDTYPE_PGID, p) { //循环查询线程组中的每一个线程
  13. if ((p == ignored_task) ||  //<span style="color:#FF0000;">如果p是pgrp中的一员??</span>
  14. (p->exit_state && thread_group_empty(p)) || //如果pgrp中的p进程正处于退出状态而且其所在的线程组是空的了
  15. is_global_init(p->real_parent)) //或者pgrp中p进程的父进程是init进程,说明在该线程组中已经没有其他线程了(即list_empty(&p->thread_group) == 1;)。
  16. continue;
  17. if (task_pgrp(p->real_parent) != pgrp && //如果p的父进程和p不再同一个组中
  18. task_session(p->real_parent) == task_session(p)) //但是p的父进程和p进程功属于一个会话
  19. return 0; //返回0,不会变成孤儿
  20. } while_each_pid_task(pgrp, PIDTYPE_PGID, p);
  21. return 1; //返回1,会变成孤儿。
  22. }

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

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

do_exit——>exit_notify()【转】的更多相关文章

  1. 20135220谈愈敏Linux Book_3

    第3章 进程管理 进程是Unix操作系统抽象概念中最基本的一种,进程管理是操作系统的心脏所在. 3.1 进程 进程:处于执行期的程序以及相关的资源的总称. 线程:在进程中活动的对象,拥有独立的程序计数 ...

  2. ARM Linux 内核 panic 之cache 一致性 ——Cortex-A9多核cache和TLB一致性广播

    ARM Linux 内核 panic 之cache 一致性 ——Cortex-A9多核cache和TLB一致性广播 Cortex-A9的多喝CPU可以接收和执行一致性广播操作,当其使能并处于SMP模式 ...

  3. Linux进程退出详解(do_exit)--Linux进程的管理与调度(十四)

    Linux进程的退出 linux下进程退出的方式 正常退出 从main函数返回return 调用exit 调用_exit 异常退出 调用abort 由信号终止 _exit, exit和_Exit的区别 ...

  4. do_exit【转】

    转自:http://blog.csdn.net/lhf_tiger/article/details/8768874 进程在退出时,必须释放它所拥有的资源,并通过某种方式告诉父进程.进程的退出一般是显示 ...

  5. [轉]Linux kernel <2.6.29 exit_notify() local root exploit分析(2009-1337)

    author : deep_pro目前网上的这个exploit(http://www.milw0rm.com/exploits/8369)的分析是有些问题的(http://forum.evilocta ...

  6. 《Linux内核设计与实现》读书笔记 第三章 进程管理

    第三章进程管理 进程是Unix操作系统抽象概念中最基本的一种.我们拥有操作系统就是为了运行用户程序,因此,进程管理就是所有操作系统的心脏所在. 3.1进程 概念: 进程:处于执行期的程序.但不仅局限于 ...

  7. liunx中的进程与线程

    1. 进程和线程 进程和线程是程序运行时状态,是动态变化的,进程和线程的管理操作(比如,创建,销毁等)都是有内核来实现的. Linux中的进程于Windows相比是很轻量级的,而且不严格区分进程和线程 ...

  8. Linux 进程管理剖析--转

    地址:http://www.ibm.com/developerworks/cn/linux/l-linux-process-management/index.html Linux 是一种动态系统,能够 ...

  9. Linux进程创建和结束

    在Linux中,进程的创建由系统调用fork和vfork完成.它们生成一个子进程并且子进程是父进程的一个复制品. Fork系统调用对应的kernel函数是sys_fork,此函数简单的调用kernel ...

随机推荐

  1. 区间DP入门题目合集

      区间DP主要思想是先在小区间取得最优解,然后小区间合并时更新大区间的最优解.       基本代码: //mst(dp,0) 初始化DP数组 ;i<=n;i++) { dp[i][i]=初始 ...

  2. POJ:3276-Face The Right Way(线性反转)

    Face The Right Way Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 6259 Accepted: 2898 De ...

  3. PAT Basic 1057

    1057 数零壹 给定一串长度不超过 10​5​​ 的字符串,本题要求你将其中所有英文字母的序号(字母 a-z 对应序号 1-26,不分大小写)相加,得到整数 N,然后再分析一下 N 的二进制表示中有 ...

  4. aspx页面 按钮不响应回车键

    aspx页面在IE浏览器中,页面上的按钮默认都响应回车键,但有的时候我们的文本框可能需要响应回车键,这时我们就不想让按钮再响应回车键,这时我们只需要设置按钮的属性即可. 按钮分为两种,一种是<b ...

  5. Linux下添加桌面快捷方式

    这里用Ubuntu下BurpSuite举例 sudo vim /home/user/Desktop/burpsuite.desktop //burpsuite随意起名,系统会系动创建文件 文件写入 # ...

  6. android 文件下载 超简单

    public void downloadPlug(String downloadUrl,String savePath) { try { URL url = new URL(downloadUrl); ...

  7. Java-JNA使用心得

    自上个月20号,历时整整一个月,终于找到工作入职了. 然后这段时间一直看公司的框架还有业务方面的东西.其实由于给分配了一个研究Java调用C语言接口的问题,导致框架业务方面的东西还不熟,然后现在手上又 ...

  8. sdram之乒乓操作

    在实时显示时,为了保证画面显示的完整性需要对SDRAM进行乒乓操作. SDRAM 中有 4 个bank ,地址分别为00 01 10 11,后面将用 0 1 2 3来描述 bank 0和1 作为第一个 ...

  9. Spring 笔记(一)概念梳理

    概念 预备知识 1. POJO POJO是Plain Old Java Object的缩写,是软件开发大师Martin Fowler提出的一个概念,指的是一个普通Java类.也就说,你随便编写一个Ja ...

  10. mysql5.6版本修改密码

     UPDATE user SET Password=PASSWORD('新密码') WHERE User='root';