目录
一、概念介绍
僵尸进程:
僵尸进程的状态被称为僵死状态,这是一个比较特殊的状态。当子进程结束但是父进程没有接收到子进程退出的返回代码时,子进程就会处于僵死状态。僵尸进程会以终止状态保持在进程表中,并且会一直等待父进程读取退出状态代码,因此,只要子进程退出,父进程还在运行,但父进程没有读取子进程退出状态代码,子进程就会进入僵死(Z)状态。
孤儿进程:
如果父进程提前结束,且子进程还没结束,此时的子进程就被称为孤儿进程,那么最后子进程结束后会被1号PID的进程init(现在也叫systemd)回收。
二、僵尸进程的出现与回收
1.创建僵尸进程
要模拟僵死状态,首先得创建出子进程,然后让子进程先于父进程结束,并不对该进程做任何操作了处理,即让父进程不读取子进程的退出状态代码,我们以下面的代码进行演示:

编译后我们直接让它运行,看看是什么效果:

很显然,子进程仅仅执行了三次就结束了,此时父进程依旧在执行,且不读取子进程的退出代码,那么结束了的子进程就变成了僵尸进程,我们可以来查看一下进程状态:

很显然,子进程变成了僵尸进程,正处于僵死状态。
2.僵尸进程的回收方案
那么僵死状态的进程我们如何回收处理呢?首先明确一点,僵尸进程本身不占用内CPU和内存资源,但是大量的僵尸进程会损耗尽ID资源,此时就需要手动回收处理。怎么处理呢?刚才我们已经探究出了僵尸进程的出现条件,那就是父进程还在运行,但是没有接收已经结束的子进程返回的结束代码,那么就可以通过一下两种方法回收僵尸进程:
正常回收:
第一种:如果父进程是我们自己编写的程序,可以在父进程代码中调用wait()或waitpid()函数,主动回收子进程资源。如下:


第二种:如果父进程是系统或者第三方程序,可尝试重启父进程解决,因为父进程重启后僵的子进程会被init进程(PID = 1)接管,init会定期回收僵死进程。
强制回收:
若父进程已经无响应或者无法正常处理,可通过直接抹杀父进程的方式,让僵死进程被init接管并回收。如下:

3.僵尸进程的危害:
1.进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态。
2.维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB就要一直维护。
3.如果一个父进程创建了很多子进程且就是不回收,那就会造成内存资源的浪费,因为数据结构对象本身就要占用内存,比如定义一个结构体变量,就是要在内存的某个位置开辟空间
三、孤儿进程的出现
要模拟出孤儿进程的情况非常简单,只需要让父进程先于子进程结束即可,这样子进程就会变成孤儿进程了,此时我们只需要关注好两个地方即可,一是子进程的状态,二是子进程的PPID,也即是收养它的进程PID,那我们修改一下刚刚的代码,如下:

执行结果如下:

我们可以发现,当父进程结束后,子进程的PPID变成1,那么它就是被init进程收养了,此时的它就是孤儿进程,有意思的是,它的进程状态虽然不是Z+了,但是也变成了S,也就是后台进程,系统自动把孤儿进程变成了后台进程。

前面我们讲进程状态的时候提到过状态字母后面有加号就是前台进程,没有加号就是后台进程,因此进入死循环的时候,ctrl+c已经无法强制停止了,只能用kill -9 PID强行抹杀。

四、区分前后台进程
这里拓展一个知识,如何区分前后台进程,这里直接讲结论:
能够从键盘获得数据读取输入的进程就是前台进程!!!因为键盘只有一个,在获取输入的时候,只能有一个进程,可以获取键盘数据,得出核心结论:任何时刻,前台进程都只能有一个,而后台进程只能有多个!!!
本期讲解到此结束,感谢观看,后续更多进程相关精彩内容待更新,喜欢的可以给博主点一个小小的三连