在被誉为UNIX编程“圣经”的一书《unix环境高级编程》中有提到僵尸进程和孤儿进程。不少同学对这两个概念会混淆,这篇文章总结一下。

在unix/linux系统中,大多情况下,子进程是通过父进程fork创建的,注:系统调用fork,是一个比较有意思系统调用,它调用一次,返回两个值,失败返回-1,成功时在子进程返回0,父进程返回所创建子进程的pid。本文暂时只需了解这些就够了,有兴趣的同学可以查阅man文档,描述的非常清楚。

子进程创建后,子进程的结束和父进程的运行是一个异步过程,也就是说父进程没办法预测子进程什么时候结束。 当一个子进程完成它的工作终止之后,其父进程需要调用wait()或waitpid()去获取子进程的终止状态。

孤儿进程

首先来认识下什么叫孤儿进程,所谓孤儿进程,顾名思义,和现实生活中的孤儿有点类似,当一个进程的父进程结束时,但是它自己还没有结束,那么这个进程将会成为孤儿进程。最后孤儿进程将会被init进程(进程号为1)的进程收养,当然在子进程结束时也会由init进程完成对它的状态收集工作,因此一般来说,孤儿进程并不会有什么危害。

下面看一个关于孤儿进程的例子:在main函数中,创建子进程,然后让父进程睡眠1s,让子进程先运行打印出其进程id(pid)以及父进程id(ppid);随后子进程睡眠3s(此时会调度到父进程运行直至结束),目的是让父进程先于子进程结束,让子进程有个孤儿的状态;最后子进程再打印出其进程id(pid)以及父进程id(ppid);观察两次打印 其父进程id(ppid)的区别。 从运行结果来看:当其父进程结束后,子进程成为了孤儿进程,其父进程id(ppid)为1,也就是说,init进程成为该子进程的父进程了。

僵尸进程

好了,说完了孤儿进程,再来谈谈僵尸进程,其实对于这2两个概念(指孤儿进程与僵尸进程)不少人容易混淆,其实如果仔细理解的话,还是很容易区分的。

僵尸进程是指:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的某些信息如进程描述符仍然保存在系统中。这种进程称之为僵死进程。

下面是1个关于僵尸进程的例子:在main函数中,创建子进程,然后让父进程睡眠10s,让子进程先终止(注意和孤儿进程例子的区别);这里子进程结束后父进程没有调用wait/waitpid函数获取其状态,用ps查看进程状态可以看出子进程为僵尸状态。 注:任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。 如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。 讲了僵尸进程产生的原因,再来讲讲僵尸进程的危害:僵尸进程会在系统中保留其某些信息如进程描述符、进程id等等。以进程id为例,系统中可用的进程id是有限的,如果由于系统中大量的僵尸进程占用进程id,就会导致因为没有可用的进程id系统不能产生新的进程,这种问题可就大了,这就是僵尸进程带来的危害。因此大部分情况下,我们都应当避免僵尸进程的产生,至于如何避免僵尸进程的产生,本来想这一篇总结完,不过感觉已经昏昏欲睡,一看时间快凌晨一点了,因此只能拖到下一篇单独总结了。

推荐阅读:

精心整理 | 2017下半年文章目录 从机器学习谈起(2) 存储数据包的一生(下) 理解、使用Docker(下) 各种字符串Hash函数比较

专注服务器后台技术栈知识总结分享

欢迎关注交流共同进步

码农有道 coding

码农有道,为您提供通俗易懂的技术文章,让技术变的更简单!