转自: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. python向多个邮箱发邮件--注意接收是垃圾邮件

    群发邮件注意:三处标红的地方 # -*- coding: UTF-8 -*- import smtplib from email.mime.text import MIMEText from emai ...

  2. Python locale 多语言模块和我遇到的坑

    Table of Contents 1. locale遇到的问题 1.1. locale 简介 1.1.1. 什么是locale 1.1.2. locale 相关命令 1.2. Python loca ...

  3. jquery跨域解决方案JSONP

    1.在互联网中我们的计算机是通过IP来定位的,但是IP比较难记忆,因此通过domain name(域名)来取代IP 2.什么是跨域? (1)默认浏览器为了安全问题,禁止了xmlhttprequest跨 ...

  4. ADB连接手机遇到的问题:list of devices attached

    今天工作时想尝试一下使用ADB无线连接手机,结果遇到了下面这样的问题,浪费了几十分钟的时间,挺闹心的,因此想分享出来... 首先 第一步:使用USB数据线连接手机,手机弹出选项时,选择仅充电,然后wi ...

  5. C#泛型和泛型约束

    一.泛型: 所谓泛型,即通过参数化类型来实现在同一份代码上操作多种数据类型.泛型编程是一种编程范式,它利用“参数化类型”将类型抽象化,从而实现更为灵活的复用. 二.泛型约束: 转自:http://ww ...

  6. ASP NET Core 部署 IIS 和发布

    1. 微软官网原文链接: https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/iis/index?view=aspnetcore- ...

  7. Oracle常用数据库系统表单以及SQL的整理

    因为最近涉及到了一些数据库的归档,备份等工作,所以一部分的重心放在了数据库上,毕竟之前对数据库的了解也只停留在了一般的建表,查询,最多最多再写一写触发器之类的东西. 通常都是自己瞎搞搞,也就懂一点皮毛 ...

  8. chrome浏览器console拓展用法

    chrome 浏览器console打印 使用CSS美化输出信息 console.log("%cThis will be formatted with large, blue text&quo ...

  9. Win10 WSL Ubuntu18.04 编译安装MySQL5.7

    ---恢复内容开始--- 在win10 商店中选择 ubuntu18.04 下载地址 http://dev.mysql.com/downloads/mysql/ wget https://cdn.my ...

  10. 有许多部分没有在cgroup中显示啊,current/high/low/min等等

    没看见current/high/low/min 在cgroup中的显示内容