1.简介
解释
一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是使进程退出,但是也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)
进程的危害
由于子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束。那么会不会因为父进程太忙来不及wait子进程,或者说不知道子进程什么时候结束,而丢失子进程结束时的状态信息呢?不会。因为Unix提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息,就可以得到。这种机制就是:在每个进程退出的时候,内核释放该进程的所有的资源,包括打开的文件,占用的内存等。但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait/waitpid来取时才释放,但是这样就导致了问题,如果进程不调用wait/waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程。此即为僵尸进程的危害,应该避免。
僵尸进程的避免
1.父进程通过wait和waitpid等函数等待子进程结束,这回导致父进程挂起。
2.如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为结束后,父进程会收到该信号,可以在handler中调用wait回收。
3.如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD,SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再父进程发送信号。
4.还有一些技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。
进程处理
它需要它的父进程来为它收尸,如果他的父进程没安装SIGCHLD信号处理函数调用wait或者waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态;存在的问题:如果父进程是一个循环,不hi结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程,系统的性能可能会收到影响。如果这是父进程结束了,那么init进程就会自动接受这个子进程,为它收尸,它还是能被清除的。
1.子进程结束后为什么要进入僵尸状态?
因为父进程可能要取得子进程的退出状态等信息。
2.僵尸状态是每一个子进程必经的状态吗?
是的。任何一个子进程(init进程除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这是用ps命令就能看到子进程的状态是"Z"。如果父进程能够及时处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。
3.如果父进程在子进程结束之前退出,则子进程将有init接管。init将会以父进程的身份对僵尸状态的子进程进行处理
4.如何查看僵尸进程?
$ps -el 其中,有标记为Z的进程就是僵尸进程 S代表休眠状态;D代表不可中断的休眠状态;R代表运行状态;Z代表僵死状态;T代表停止或者跟踪状态
2.概述
假设
在fork()/execve()过程中,假设子进程结束时父进程仍存在,而父进程fork()之前既没安装SIGCHLD信号处理函数调用waitpid()等待子进程结束,又没有显式忽略该信号,则子进程成为僵尸进程,无法正常结束,此时即使是root身份kill -9也不能杀死僵尸进程
补救方法
杀死僵尸进程的父进程(僵尸进程的父进程必然存在),僵尸进程成为“孤儿进程”,过继给1号进程init,init始终会负责清理僵尸进程。
最后,希望有兴趣的童鞋能够通过本文有所启发,那就再好不过了.如果还有童鞋有什么疑问,可以关注微信公众号Yongf
或者扫描下面的二维码关注下,在上面留下问题,我看到都会回答的.