进程的死亡

2015-6-17 chenhui 进程管理

进程的死亡,有两种方式:

  1. 进程杀死自己
  2. 进程进入僵死状态,由父进程杀死自己

进程死亡流程

  1. 判断自身是否可杀死,不可杀死则杀死失败
  2. 剥离进程占用的资源
  3. 杀死所有僵死状态的子进程, 并为正常子进程重新寻找父进程
  4. 如果进程需要向父进程发送信号或者他被跟踪,就通知父进程,父进程通过 wait 系统调用接收,但父进程只是把他的信息写到用户空间
  5. 如果进程不需要向父进程发送信号,则释放自己

当父进程死亡时,就会像当前进程死亡时一样,杀死所有僵死的子进程,当前进程也就被杀死了。



一个进程可以使用 exit 系统调用来结束自己并进入僵死状态。他最后在内核中执行到的函数为 sys_exit() 。



这个 error_code,一般有两个值,-1 和 SIGCLHLD。如果是 -1 就表示不需要通知父进程直接杀死自身;如果是 SIGCLHLD 就表示自身进入僵死状态,并通知父进程,最后在父进程死亡时杀死。


他调用 do_exit() 来执行真正的操作,实际上 do_exit() 涉及到很多其他内容,所以我们只讲解部分最为关键的代码,下面分析一下 do_exit() 函数。




867 行,结束的就是当前进程。


874 行,in_interrupt() 函数用来判断当前是否正在执行中断服务处理程序。exit() 系统调用只能结束进程(线程),那么中断服务程序调用他是想结束谁呢?我们并不知道,因为我们并不知道中断会在什么时候发生,所以这里不允许在执行中断服务程序时调用到本函数。


876 行,进程号(PID)为 0 的进程是 idle 进程,这个进程是系统中没有其他进程在执行时执行的函数,他不能被杀死。


879~882 行,不允许杀死 init 进程。





935~946 行,和 copy_process() 的那一堆 copy_xxx 相对应,这里就是剥离他的那些资源(还记得使用 vfork() 创建进程时的父进程吗?他就是在 exit_mm() 里被唤醒的)。


955 行,退出代码。


958 行,发送死亡通知。一会我们再来详细讲解这个函数。




974 行,由于完成了进程退出操作,所以为他设置 PF_EXITPIDONE 标记。

983 行,把进程状态设为 TASK_DEAD(注意,这个是 state,不是 exit_state),表示他已经死亡了。

985 行,这里被调度出去后,tsk 进程再也不会执行了。

 

在 do_exit() 里,调用exit_notify() 通知内核中其他相关进程已经有进程死了。这个函数非常重要,下面我们再来看看 exit_notify() 是怎么实现的。




770 行,为当前进程的所有子进程都找一个新的父进程,因为他们现在的父进程要死亡了。当然,如果子进程是僵死状态的线程,则他会被放到 ptrace_dead 链表里,在下面会释放他。




787~897 行,如果当前进程不需要自己释放自己(exit_signal == -1),就通知父进程来处理死亡信息。不过,在当前进程被跟踪时,即使 exit_signal == -1,他也要通知父进程(被跟踪时,父进程就是跟踪进程)


800~805 行,如果父进程不接受我们的 SIGCLHLD 信号,或者我们不需要通知父进程,那么就把状态设为 EXIT_DEAD,这意味着当前进程的释放操作由自己来完成,在 817 行会释放自身。否则就是 EXIT_ZOMBIE,对于 EXIT_ZOMBIE,在父进程死亡时,他就会所有处于该状态的子进程全部释放


809~813 行,还记得 770 行的forget_original_parent() 吗?他把子进程里所有僵死进程都挑了出来并放到 ptrace_dead 链表里,这里就是彻底消灭这些进程,释放他们(如果当前进程成为EXIT_ZOMBIE,那么父进程死亡时,在这样释放当前进程)。


816 行,不需要父进程来释放,直接释放。


然后我们再来看看 do_notify_parent() 是怎么实现的,这个函数极为重要。




402 行,先定义一个信号描述符,他用于描述发送给父进程的信号。

413~432 行,初始化这个信号描述符。




子进程退出的时候,会向其父进程发送某个信号作为通知(多数情况为 SIGCHLD)。然后子进程等待父进程调用 wait4() 系统调用。 

 

434 行,取出父进程的信号处理器描述符。


436~438 行,如果我们要发送给父进程的是 SIGCHLD 信号(通知父进程我死了,需要你帮我做下一步工作)但是父进程却忽略这个信号或者父进程不会把子进程转变为僵死进程,那么就说明父进程不接受我们的信号,则把当前进程的 exit_signal 设为 -1,然后当本函数返回到 exit_notify() 时,他就会直接把 tsk 进程彻底杀死。


444~446 行,如果信号没有任何问题,那么就把信号发送给父进程,然后唤醒他,这样当父进程醒来后就会处理这个子进程死亡信息。


父进程怎么处理呢?其实父进程不会释放他,而是在父进程死亡时释放。他只是把子进程的一些死亡子进程的信息写到用户空间而已。

 


发表评论:

Copyright ©2015-2016 freehui All rights reserved