zoukankan      html  css  js  c++  java
  • linux内核情景分析之exit与Wait

    1. //第一层系统调用
    2. asmlinkage long sys_exit(int error_code)
    3. {
    4. do_exit((error_code&0xff)<<8);
    5. }
    其主体是do_exit,接下来我们来看看do_exit的实现
    1. NORET_TYPE void do_exit(long code)
    2. {
    3. struct task_struct *tsk = current;//获取当前进程描述符
    4. if (in_interrupt())//禁止中断时调用do_exit
    5. panic("Aiee, killing interrupt handler!");
    6. if (!tsk->pid)//空转进程也就是0号进程禁止退出
    7. panic("Attempted to kill the idle task!");
    8. if (tsk->pid == 1)//1号进程禁止退出
    9. panic("Attempted to kill init!");
    10. tsk->flags |= PF_EXITING;//退出进程时,设置此标志位
    11. /*
    12. 进程退出时,可能已经设置了实时定时器,real_timer已挂载到内核定时器队列,
    13. 现在进程要退出,没必要存在了,就把当前进程从定时器队列中脱离出来
    14. */
    15. del_timer_sync(&tsk->real_timer);
    16. fake_volatile:
    17. #ifdef CONFIG_BSD_PROCESS_ACCT
    18. acct_process(code);
    19. #endif
    20. /*如果是指针共享,那就只是减少mm->mm_users,
    21. 如果有独立的进程空间,那就直接释放页表,mm_struct,vm_struct
    22. 以及所有的vma*/
    23. __exit_mm(tsk);
    24. //加锁
    25. lock_kernel();
    26. //如果调用exit()之前该信号量还没退出,那就把它撤销
    27. sem_exit();
    28. //如果只是指针共享,那就减少files_struct->count,如果是独享,那就销毁
    29. __exit_files(tsk);
    30. //以上相同,释放fs->count
    31. __exit_fs(tsk);
    32. //释放信号处理函数表
    33. exit_sighand(tsk);
    34. //空函数
    35. exit_thread();
    36. ///表示进程是否为会话主管
    37. if (current->leader)
    38. disassociate_ctty(1);//删除终端,释放tty
    39. //若正在执行的代码是符合iBCS2标准的程序,则减少相对应模块的引用数目
    40. put_exec_domain(tsk->exec_domain);
    41. /* 若正在执行的代码属于全局执行文
    42. 件结构格则减少相对应模块的引用数目 */
    43. if (tsk->binfmt && tsk->binfmt->module)
    44. __MOD_DEC_USE_COUNT(tsk->binfmt->module);
    45. tsk->exit_code = code;
    46. //将当前进程设置为僵死状态;并给父进程发信号;其当前进程的子进程的父进程设置为init进程或者其他线程
    47. exit_notify();
    48. schedule();
    49. BUG();
    接着挨个分析释放资源相关函数(信号量就等到进程间通信学完再分析)
    1. static inline void __exit_mm(struct task_struct * tsk)
    2. {
    3. struct mm_struct * mm = tsk->mm;//获取当前进程的内存描述符
    4. mm_release();//唤醒睡眠的父进程
    5. if (mm) {
    6. atomic_inc(&mm->mm_count);
    7. if (mm != tsk->active_mm) BUG();//确保mm与active_mm一样
    8. /* more a memory barrier than a real lock */
    9. task_lock(tsk);
    10. tsk->mm = NULL;//设置为NULL
    11. task_unlock(tsk);
    12.      //刷新tlb
    13. enter_lazy_tlb(mm, current, smp_processor_id());
    14. mmput(mm);//释放页表等等
    15. }
    16. }
    以上资源释放完后,进程设置为僵尸状态,还保留pcb以及内核栈,自己并不释放而是由父进程负责,将调用exit_notify()通知其父进程
    原因:让父进程可以统计信息,接下来看看exit_notify()
    1. /*
    2. * Send signals to all our closest relatives so that they know
    3. * to properly mourn us..
    4. */
    5. static void exit_notify(void)
    6. {
    7. struct task_struct * p, *t;
    8. //其当前进程的子进程的父进程设置为init进程,如果父进程是线程,那就托孤给其他线程
    9. forget_original_parent(current);
    10. /*
    11. * Check to see if any process groups have become orphaned
    12. * as a result of our exiting, and if they have any stopped
    13. * jobs, send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2)
    14. *
    15. * Case i: Our father is in a different pgrp than we are
    16. * and we were the only connection outside, so our pgrp
    17. * is about to become orphaned.
    18. */
    19. t = current->p_pptr;//获取其养父
    20. /*
    21. 如果当前进程与父进程属于相同的会话,又处于不同的组,当前进程挂了,整个组如果成了孤儿组,那就要
    22. 给这个进程组的所有进程发送一个SIGHUP跟SIGCONT信号
    23. */
    24. if ((t->pgrp != current->pgrp) &&//组不同而会话相同
    25. (t->session == current->session) &&
    26. will_become_orphaned_pgrp(current->pgrp, current) &&//判断是否是孤儿进程组
    27. has_stopped_jobs(current->pgrp)) {////如果进程组中有处于TASK_STOP状态的进程
    28. kill_pg(current->pgrp,SIGHUP,1);//先发送SIGHUP在发送SIGCONT
    29. kill_pg(current->pgrp,SIGCONT,1);
    30. }
    31. /* Let father know we died
    32. *
    33. * Thread signals are configurable, but you aren't going to use
    34. * that to send signals to arbitary processes.
    35. * That stops right now.
    36. *
    37. * If the parent exec id doesn't match the exec id we saved
    38. * when we started then we know the parent has changed security
    39. * domain.
    40. *
    41. * If our self_exec id doesn't match our parent_exec_id then
    42. * we have changed execution domain as these two values started
    43. * the same after a fork.
    44. *
    45. */
    46. if(current->exit_signal != SIGCHLD &&
    47. ( current->parent_exec_id != t->self_exec_id ||
    48. current->self_exec_id != current->parent_exec_id)
    49. && !capable(CAP_KILL))
    50. current->exit_signal = SIGCHLD;//给父进程发的信号是SIGCHLD /*
    51. * This loop does two things:
    52. *
    53. * A. Make init inherit all the child processes
    54. * B. Check to see if any process groups have become orphaned
    55. * as a result of our exiting, and if they have any stopped
    56. * jobs, send them a SIGHUP and then a SIGCONT. (POSIX 3.2.2.2)
    57. */
    58. write_lock_irq(&tasklist_lock);
    59. current->state = TASK_ZOMBIE;//设置为僵尸进程
    60. do_notify_parent(current, current->exit_signal);//由父进程来料理后事
    61. //将子进程队列中的每个进程都转移到托孤的父进程的子进程队列中去
    62. while (current->p_cptr != NULL) {//p_cptr表示子进程
    63. p = current->p_cptr;//p指向子进程
    64. current->p_cptr = p->p_osptr;//子进程指向子进程他哥,形成一个队列
    65. p->p_ysptr = NULL;//子进程的滴滴设置为0
    66. p->ptrace = 0;
    67. p->p_pptr = p->p_opptr;//将养父改为亲父
    68. p->p_osptr = p->p_pptr->p_cptr;//子进程的哥哥改为子进程的养父的子进程,移到子进程队列
    69. if (p->p_osptr)
    70. p->p_osptr->p_ysptr = p;
    71. p->p_pptr->p_cptr = p;
    72. if (p->state == TASK_ZOMBIE)//并且判断每个子进程是否是僵尸状态
    73. do_notify_parent(p, p->exit_signal);
    74. /*
    75. * process group orphan check
    76. * Case ii: Our child is in a different pgrp
    77. * than we are, and it was the only connection
    78. * outside, so the child pgrp is now orphaned.
    79. 孤儿进程组: 一个进程组中的所有进程的父进程要么是该进程组的一个进程,
    80. 要么不是该进程组所在的会话中的进程。 一个进程组不是孤儿进程组的条件是,
    81. 该组中有一个进程其父进程在属于同一个会话的另一个组中。
    82. */
    83. if ((p->pgrp != current->pgrp) &&
    84. (p->session == current->session)) {
    85. int pgrp = p->pgrp;
    86. write_unlock_irq(&tasklist_lock);
    87. //父进程所在的组是否是孤儿进程组,以及是否含有stop进程
    88. if (is_orphaned_pgrp(pgrp) && has_stopped_jobs(pgrp)) {
    89. kill_pg(pgrp,SIGHUP,1);
    90. kill_pg(pgrp,SIGCONT,1);
    91. }
    92. write_lock_irq(&tasklist_lock);
    93. }
    94. }
    95. write_unlock_irq(&tasklist_lock);
    96. }
    子进程托孤给其他进程(如果该进程是线程,也就是含有其他线程),否则托孤给init进程
    1. /*
    2. * When we die, we re-parent all our children.
    3. * Try to give them to another thread in our process
    4. * group, and if no such member exists, give it to
    5. * the global child reaper process (ie "init")
    6. */
    7. static inline void forget_original_parent(struct task_struct * father)
    8. {
    9. struct task_struct * p, *reaper;
    10. read_lock(&tasklist_lock);
    11. /* 获取当前用户空间的下一线程 */
    12. reaper = next_thread(father);
    13. if (reaper == father)//如果相等说明是进程,不是线程组,那就只能托孤给init进程
    14. reaper = child_reaper;//init进程
    15. for_each_task(p) {
    16. if (p->p_opptr == father) {//搜索所有task_struct数据结构,发现其进程生父就是要退出的进程
    17. /* We dont want people slaying init */
    18. p->exit_signal = SIGCHLD;//设置发送信号
    19. p->self_exec_id++;
    20. p->p_opptr = reaper;//将要死的进程的子进程托孤给reaper(当前线程的其他线程或者init进程?
    21. if (p->pdeath_signal) 
    22.             send_sig(p->pdeath_signal, p, 0);//发送信号,告知儿子死了
    23. }
    24. }
    25. read_unlock(&tasklist_lock);
    26. }


    接下来查看do_notify_parent发送信号给父进程
    1. /*
    2. * Let a parent know about a status change of a child.
    3. 让一个父亲知道有关儿子的改变
    4. 参数为当前要退出进程,以及信号
    5. */
    6. void do_notify_parent(struct task_struct *tsk, int sig)
    7. {
    8. struct siginfo info;
    9. int why, status;
    10. info.si_signo = sig;
    11. info.si_errno = 0;
    12. info.si_pid = tsk->pid;
    13. info.si_uid = tsk->uid;
    14. /* FIXME: find out whether or not this is supposed to be c*time. */
    15. info.si_utime = tsk->times.tms_utime;
    16. info.si_stime = tsk->times.tms_stime;
    17. status = tsk->exit_code & 0x7f;
    18. why = SI_KERNEL; /* shouldn't happen */
    19. switch (tsk->state) {
    20. case TASK_STOPPED:
    21. /* FIXME -- can we deduce CLD_TRAPPED or CLD_CONTINUED? */
    22. if (tsk->ptrace & PT_PTRACED)
    23. why = CLD_TRAPPED;
    24. else
    25. why = CLD_STOPPED;
    26. break;
    27. default:
    28. if (tsk->exit_code & 0x80)
    29. why = CLD_DUMPED;
    30. else if (tsk->exit_code & 0x7f)
    31. why = CLD_KILLED;
    32. else {
    33. why = CLD_EXITED;
    34. status = tsk->exit_code >> 8;
    35. }
    36. break;
    37. }
    38. info.si_code = why;
    39. info.si_status = status;
    40. send_sig_info(sig, &info, tsk->p_pptr);//发送信号
    41. wake_up_parent(tsk->p_pptr);//唤醒父进程
    42. }


    1. /*
    2. * This function is typically called only by the session leader, when
    3. * it wants to disassociate itself from its controlling tty.
    4. *
    5. * It performs the following functions:
    6. * (1) Sends a SIGHUP and SIGCONT to the foreground process group
    7. * (2) Clears the tty from being controlling the session
    8. * (3) Clears the controlling tty for all processes in the
    9. * session group.
    10. *当前进程是一个会话的主进程(current->leader非0)那就还要将整个session与中断切断,并释放tty,pcb有个tty指针
    11. * The argument on_exit is set to 1 if called when a process is
    12. * exiting; it is 0 if called by the ioctl TIOCNOTTY.
    13. */
    14. void disassociate_ctty(int on_exit)
    15. {
    16. struct tty_struct *tty = current->tty;//获取当前进程的tty
    17. struct task_struct *p;
    18. int tty_pgrp = -1;
    19. if (tty) {
    20. tty_pgrp = tty->pgrp;//获取进程组的tty
    21. if (on_exit && tty->driver.type != TTY_DRIVER_TYPE_PTY)//统计tty设备打开的次数
    22. tty_vhangup(tty);
    23. } else {
    24. if (current->tty_old_pgrp) {
    25. kill_pg(current->tty_old_pgrp, SIGHUP, on_exit);//给当前进程组发送sighup与sigcont信号
    26. kill_pg(current->tty_old_pgrp, SIGCONT, on_exit);
    27. }
    28. return;
    29. }
    30. if (tty_pgrp > 0) {
    31. kill_pg(tty_pgrp, SIGHUP, on_exit);
    32. if (!on_exit)
    33. kill_pg(tty_pgrp, SIGCONT, on_exit);
    34. }
    35. current->tty_old_pgrp = 0;//进程控制终端所在的组标识设置为0
    36. tty->session = 0;//会话设置为0
    37. tty->pgrp = -1;//组设置为-1
    38. read_lock(&tasklist_lock);
    39. for_each_task(p)//遍历每个进程是否位于同一会话
    40. if (p->session == current->session)//当前进程是会话的主进程
    41. p->tty = NULL;//切断tty终端
    42. read_unlock(&tasklist_lock);
    43. }
    do_exit流程:
    禁止中断调用,0号进程,1号进程退出
    如果有独立空间那就删除独立空间,释放页表,释放信号量,释放文件对象,释放信号处理函数表
    如果是会话控制进程,删除终端,释放tty,接下来调用exit_notify()函数
    如果当前进程是是线程(也就包含其他线程,非独享),托孤给其他线程,否则托孤给init进程
    判断当前进程退出是否会导致孤儿进程组出现
    设置发送信号为SIGCHLD,将当前进程设置为僵尸状态,接着调用do_notify_parent发送信号给父进程,并唤醒父进程
    并将僵尸进程的所有子进程的队列移到托孤的队列.最后shedule()


    1. //等待子进程的pid
    2. asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru)
    3. {
    4. int flag, retval;
    5. DECLARE_WAITQUEUE(wait, current);//为当前进程分配一个waitqueue结构
    6. struct task_struct *tsk;
    7. if (options & ~(WNOHANG|WUNTRACED|__WNOTHREAD|__WCLONE|__WALL))
    8. return -EINVAL;
    9. //添加到当前进程的waitchldexit对列中
    10. add_wait_queue(&current->wait_chldexit,&wait);
    11. repeat:
    12. flag = 0;
    13. current->state = TASK_INTERRUPTIBLE;//设置为睡眠,让其他进程先运行,等待子进程挂了
    14. read_lock(&tasklist_lock);
    15. tsk = current;
    16. do {
    17. struct task_struct *p;
    18. for (p = tsk->p_cptr ; p ; p = p->p_osptr) {//p表示当前进程的子进程
    19. if (pid>0) {
    20. if (p->pid != pid)//是否等于参数pid,不等于就继续
    21. continue;
    22. } else if (!pid) {//不是0号进程
    23. if (p->pgrp != current->pgrp)
    24. continue;
    25. } else if (pid != -1) {//不是-1(随便)
    26. if (p->pgrp != -pid)
    27. continue;
    28. }
    29. /* Wait for all children (clone and not) if __WALL is set;
    30. * otherwise, wait for clone children *only* if __WCLONE is
    31. * set; otherwise, wait for non-clone children *only*. (Note:
    32. * A "clone" child here is one that reports to its parent
    33. * using a signal other than SIGCHLD.) */
    34. //判断子进程的信号是否是sigchld
    35. if (((p->exit_signal != SIGCHLD) ^ ((options & __WCLONE) != 0))
    36. && !(options & __WALL))
    37. continue;
    38. flag = 1;//表示是当前进程的子进程
    39. switch (p->state) {
    40. case TASK_STOPPED://等待子进程被跟踪
    41. if (!p->exit_code)//是否设置了退出码
    42. continue;
    43. if (!(options & WUNTRACED) && !(p->ptrace & PT_PTRACED))//判断条件是否跟踪
    44. continue;
    45. read_unlock(&tasklist_lock);
    46. retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
    47. if (!retval && stat_addr)
    48. retval = put_user((p->exit_code << 8) | 0x7f, stat_addr);
    49. if (!retval) {
    50. p->exit_code = 0;
    51. retval = p->pid;
    52. }
    53. goto end_wait4;//满足直接跳到end_wait4
    54. case TASK_ZOMBIE://僵尸状态
    55. current->times.tms_cutime += p->times.tms_utime + p->times.tms_cutime;
    56. current->times.tms_cstime += p->times.tms_stime + p->times.tms_cstime;
    57. read_unlock(&tasklist_lock);
    58. retval = ru ? getrusage(p, RUSAGE_BOTH, ru) : 0;
    59. if (!retval && stat_addr)
    60. retval = put_user(p->exit_code, stat_addr);//指定位置保存退出码
    61. if (retval)
    62. goto end_wait4;
    63. retval = p->pid;
    64. if (p->p_opptr != p->p_pptr) {//生父与养父是否相同
    65. write_lock_irq(&tasklist_lock);
    66. REMOVE_LINKS(p);//将task_struct从养父队列中脱离出来
    67. p->p_pptr = p->p_opptr;//将养父设置为生父
    68. SET_LINKS(p);
    69. do_notify_parent(p, SIGCHLD);//通知生父进程自己挂了
    70. write_unlock_irq(&tasklist_lock);
    71. } else
    72. release_task(p);//释放残留的资源如pcb等等
    73. goto end_wait4;//子进程处于僵死状态,goto end_wait4
    74. default:
    75. continue;
    76. }
    77. }
    78. if (options & __WNOTHREAD)//如果设置了wnothread直接跳出
    79. break;
    80. tsk = next_thread(tsk);//到同一进程的寻找下一个线程,一线程创建的子进程挂了,其他线程调用wait应该没用吧?
    81. } while (tsk != current);
    82. read_unlock(&tasklist_lock);
    83. if (flag) {//如果pid不是当前进程的子进程,直接到end_wait4
    84. retval = 0;
    85. if (options & WNOHANG)//设置了wnohang
    86. goto end_wait4;
    87. retval = -ERESTARTSYS;
    88. if (signal_pending(current))//当前进程是否有信号未处理
    89. goto end_wait4;
    90. schedule();//被调度.等待被子进程唤醒
    91. goto repeat;
    92. }
    93. retval = -ECHILD;
    94. end_wait4:
    95. current->state = TASK_RUNNING;//将当前进程改为可运行状态
    96. remove_wait_queue(&current->wait_chldexit,&wait);
    97. return retval;
    98. }

     下列条件之一得到满足时才结束,goto end_wait4:

        1、所等待的子进程的状态变成TASK_STOPPED,TASK_ZOMBIE;

        2、所等待的子进程存在,可不在上述两个状态,而调用参数options中的WHONANG标志位为1,或者当前进程接受到了其他的信号;

        3、进程号pid的那个进程根本不存在,或者不是当前进程的子进程。

        否则,当前进程将其自身的状态设成TASK_INTERRUPTIBLE,并调用schedule()。


    释放残余的子进程资源
    1. static void release_task(struct task_struct * p)//释放子进程留下的资源
    2. {
    3. if (p != current) {
    4. #ifdef CONFIG_SMP
    5. /*
    6. * Wait to make sure the process isn't on the
    7. * runqueue (active on some other CPU still)
    8. */
    9. for (;;) {
    10. task_lock(p);
    11. if (!p->has_cpu)
    12. break;
    13. task_unlock(p);
    14. do {
    15. barrier();
    16. } while (p->has_cpu);
    17. }
    18. task_unlock(p);
    19. #endif
    20. atomic_dec(&p->user->processes);//子进程数目减少
    21. free_uid(p->user);//是否uid
    22. unhash_process(p);//把子进程的pcb从队列摘下来
    23. release_thread(p);//检查进程的LDT是否已释放
    24. current->cmin_flt += p->min_flt + p->cmin_flt;
    25. current->cmaj_flt += p->maj_flt + p->cmaj_flt;
    26. current->cnswap += p->nswap + p->cnswap;
    27. /*
    28. * Potentially available timeslices are retrieved
    29. * here - this way the parent does not get penalized
    30. * for creating too many processes.
    31. *
    32. * (this cannot be used to artificially 'generate'
    33. * timeslices, because any timeslice recovered here
    34. * was given away by the parent in the first place.)
    35. */
    36. current->counter += p->counter;
    37. if (current->counter >= MAX_COUNTER)
    38. current->counter = MAX_COUNTER;
    39. free_task_struct(p);//将2个物理页大小的pcb释放
    40. } else {
    41. printk("task releasing itself ");
    42. }
    43. }












  • 相关阅读:
    OC拨打电话
    oc唯一标时一部设备
    去掉UITableView多余的分割线
    UICollectionView的使用
    设置ulabel的行间距
    uitextfield
    iOS导航栏适配
    App Store 升级问题
    mac中使用终端生成RSA私钥和公钥文件
    js document
  • 原文地址:https://www.cnblogs.com/zengyiwen/p/570d0f083a35a97a6d72212a831f058e.html
Copyright © 2011-2022 走看看