一个进程调用exit命令结束自己的生命的时候,其实它并没有真正被销毁,而是留下一个成为zombie的数据结构。僵尸进程是非常特殊的一种,因为它已经放弃了几乎所有的内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留了一个位置,记录这该进程的退出状态等信息以供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。

       僵尸进程需要它的父进程来为它收尸,O_O!! 如果它的父进程没有安装SIGCHLD信号处理函数调用wait()或waitpid()等待子进程结束,又没有显式忽略SIGCHLD信号,那么它就一直保持僵尸状态。如果这时候父进程结束了,那么init祖先进程会接手这个子进程,为它收尸。更不幸的情况是,如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,无法结束,此时即使是root身份kill -9也不能杀死僵尸进程。补救办法是杀死僵尸进程的父进程(僵尸进程的父进程必然存在),僵尸进程成为孤儿进程,进而由init负责清理僵尸进程。由于系统所能使用的进程号是有限的,如果存在大量的僵尸进程,将会导致系统没有可用的进程号,而导致不能创建进程。所以我们要避免僵尸进程!

       当一个进程创建了一个子进程时,它们的运行是异步的。即父进程无法预知子进程会在什么时候结束,那么如果父进程很繁忙来不及wait 子进程时,那么当子进程结束时,会不会丢失子进程的结束时的状态信息呢?处于这种考虑unix提供了一种机制可以保证只要父进程想知道子进程结束时的信息,它就可以得到。这种机制是:在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存。但是仍然保留了一些信息(如进程号pid 退出状态 运行时间等)。这些保留的信息直到进程通过调用wait/waitpid时才会释放。 *^_^*

       这里有一个需要注意的地方。如果子进程先结束而父进程后结束,即子进程结束后,父进程还在继续运行但是并未调用wait/waitpid那子进程就会成为僵尸进程。    


       但如果子进程后结束,即父进程先结束了,但没有调用wait/waitpid来等待子进程的结束,此时子进程还在运行,父进程已经结束。那么并不会产生僵尸进程。应为每个进程结束时,系统都会扫描当前系统中运行的所有进程,看看有没有哪个进程是刚刚结束的这个进程的子进程,如果有,就有init来接管它,成为它的父进程。

       同样的在产生僵尸进程的那种情况下,即子进程结束了但父进程还在继续运行(并未调用wait/waitpid)这段期间,假如父进程异常终止了,那么该子进程就会自动被init接管。那么它就不再是僵尸进程了。因为intit会发现并释放它所占有的资源。(当然如果进程表越大,init发现它接管僵尸进程这个过程就会变得越慢,所以在init未发现他们之前,僵尸进程依旧消耗着系统的资源)

我们知道了产生僵尸进程的原因,那么如何避免僵尸进程呢?

       如果父进程并不是很繁忙我们就可以通过直接调用wait()或waitpid()来等待子进程的结束。但这会导致父进程被挂起。但是如果父进程很忙。我们不希望父进程一直被挂起直到子进程的结束,那么我们可以使用信号函数sigaction为SIGCHLD设置wait处理函数。这样子进程结束后,父进程就会收到子进程结束的信号。并调用wait回收子进程的资源。