【Linux系统编程】僵尸进程与孤儿进程

Linux系统
81
0
0
2024-05-02

1. 僵尸进程

上一篇文章进程的状态中最后我们提出了僵尸状态:

为了方便子进程退出后父进程或操作系统获取该进程的退出结果,Linux进程退出时,进程一般不会立即死亡,而是要维持一个Z状态即——僵尸状态。

那处在僵尸状态的进程即僵尸进程,那首先我们就要来重点理解一下僵尸进程

那我们来给大家讲一个故事:

假如你呢是一名非常自律上进的大学生,每天早晨都有到外面跑步的好习惯。 今天早上呢你正在跑步的时候,忽然后面来了一个程序员,也在跑步,但他跑的非常快,边跑边掉头发,很快就超过你跑到前面去了,你依然在后面慢慢的跑着。 突然,这个程序员跑到在你前面100M左右的地方,口吐白沫,一阵抽搐,就倒下了。 后面不远处的你看到了这个情况,非常慌张,赶紧打了120并报了警。 然后警察很快就到了,到了之后发现这个人已经不行了。 然后请问大家警察会怎么做? 会不会直接把这个人弄走,通知它的家属,然后销毁现场。 那正常情况呢应该是不会这样的。 警察应该会封锁现场,然后刚好120的人过来了,警察说,你帮我们确认了,这个人是不是已经不行了,120的医务人员说确实不行了。 然后呢警察可能会通知法医来确认这个人的死因,是自己死亡,还是被谋杀了啥的,然后进行一些相关的调查啥的。 假设最后发现这个人是自己发病死亡了,那然后警察就通知家属,让它们把人带走,然后就可以撤离现场了。

那上述的故事中:

警察发现这个人死亡后应不应该立即把这个人弄走,然后销毁现场。 不应该,而是要维护好现场,便于调查它的死因啥的… 那其实这就对应了我们上面提到的一个进程退出后不会立即死亡,而是维持一个僵尸状态,便于父进程或者操作系统获取该进程的退出结果。

那么简单总结一下:

僵尸状态(Zombies)是一种比较特殊的状态。当进程退出并且其父进程没有读取到该进程退出的返回代码时就会产生僵死(尸)进程。 僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。 所以,只要子进程退出,但父进程还在运行,且父进程没有读取子进程退出的返回代码,子进程就会进入僵尸状态。

那我们写个代码给大家演示一下:


在这里插入图片描述

代码就不解释了,都是之前讲过的内容 我们运行一下

在这里插入图片描述

那我们用ps命令查看一下进程

在这里插入图片描述

我们看到现在父子进程两个的状态都是S。 那按我们上面讲的,子进程退出,父进程还在运行,且没有回收子进程获取返回码(我们现在也不会),那么子进程就会进入僵尸状态 🆗,那我们现在干掉子进程

在这里插入图片描述

然后我们再来查看

在这里插入图片描述

子进程就变成了僵尸进程

2. 僵尸进程的危害

进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态? 是的! 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护? 是的! 那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存泄漏? 是的!因为数据结构对象本身就要占用内存,如果一直不释放那就内存泄漏了。 如何避免?后面讲

等这个进程真正被回收了,它的状态才会变成X死亡状态,此时该进程的所有资源才会被释放。

至此,值得关注的进程状态全部讲解完成,下面来认识另一种进程:

3. 孤儿进程

首先,我们给出孤儿进程的概念:

孤儿进程指的是在其父进程执行完成退出或被终止后仍继续运行的一类进程。

即如果父进程先退出,子进程继续还在运行,那么该子进程就被称为孤儿进程。

那下面我们还是写写代码来帮助大家理解:

这里我们写这样一个程序 子进程循环打印运行,父进程打印10次退出 写个Makefile 然后这里给大家解释一下这两个之前没见过的符号: @就代表:左边的目标文件; ^代表:右边的所有的依赖文件 所以后续如果我们开发的时候,这个依赖文件列表里面有多个文件,我们不用一个个都列在后面,直接用这个符号就行了。

那下面我们就运行这个程序并观察一下对应的现象:

那这里呢我们要用到一条shell语句 while :; do ps axj | head -1&&ps axj | grep mytest|grep -v grep; sleep 1;echo "--------"; done 大家看不懂没关系,它的作用其实就是每个1秒去显示一下对应搜索的进程信息,并打印了一个“----------”的分割线

在这里插入图片描述

那现在我们把程序跑起来

在这里插入图片描述

那我们发现呢? 运行一段时间之后,父进程就结束退出了,后面就只剩子进程在运行了。

但是呢?这里有些问题值得我们去思考和探索:

首先第一个问题:父进程也是一个进程啊,那按照我们前面学的内容,一个进程退出时,并不会立即变为X死亡状态,而是要维持一个僵尸状态,但是这里这个父进程退出之后为啥我们没有看到Z状态呢?它直接就没了?

🆗,那大家想一下僵尸状态存在的意义是啥? 其实我们前面已经说了,是为了方便子进程退出后父进程或操作系统获取该进程的退出结果。 所以如果子进程一直没有被回收的话,那么它就会一直处在僵尸进程,那它就会一直占据资源,进而导致内存泄漏,这都是我们前面讲过的。 那这里为什么没有看到父进程处于僵尸状态,是不是因为它退出后就被回收了呢? 🆗,是的! 大家说这里的这个父进程它的父进程是谁啊? 是不是bash啊,这也是我们前面讲过的内容——命令行启动的所有程序,最后变成进程其对应的父进程都是bash。 所以,我们这里之所以没有看到父进程处于僵尸状态,就是它的父进程bash把它直接回收了

然后第二个问题我们发现


在这里插入图片描述

原来这个子进程的父进程的PID是2618,但是它的父进程退出后,它的父进程的PID变成了1,它被一个新的爹领养了 那这里我们得出一个结论: 如果一个进程的父进程退出了,而这个进程自己还在运行,那么它将会被1号进程(init进程)自动领养,那么这个被领养的进程即孤儿进程。

那大家想一下为什么?为什么操作系统要领养孤儿进程?

或者大家思考一下如果不领养孤儿进程,会发生什么? 那其实这个问题可以说我们前面已经讲过了。 如果孤儿进程不被领养,那它就没有父进程了嘛,那这样的话未来这个孤儿进程退出的时候谁来回收它,给它收尸呢? 那退出后没有人回收它,它就会一直处于僵尸状态,等待回收而没有人回收,那么就导致内存泄漏。

那再来总结一下:

在操作系统领域中,孤儿进程指的是在其父进程执行完成或被终止后仍继续运行的一类进程。这些孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

除此之外,还有一个细节也提一下:


在这里插入图片描述

我们还可以发现,它的父进程退出后,它的状态由S+变成了S,+没了。 那进程状态里面的这个+其实我们前面也提过: 带+的是前台进程,不带的是后台进程。 那他变成后台进程后呢我们CTRL+c就终止不掉它了

在这里插入图片描述

这个我们前面也演示过,而且它在后台运行我们还可以正常执行命令

在这里插入图片描述

但是这样很影响我们的使用。 所以我们可以使用kill命令把它杀掉

在这里插入图片描述

这也是之前讲过的 那这里我们再补充一个方法 killall +进程名称

在这里插入图片描述

就可以杀掉指定名称的进程。

那以上就是关于僵尸进程和孤儿进程的相关内容…