我的编程空间,编程开发者的网络收藏夹
学习永远不晚

【看表情包学Linux】进程状态解析 | 运行态 | 终止态 | 进程挂起与阻塞 | 运行态R | 阻塞态S/D | 死亡态X | 僵尸态Z | 暂停态T/t | 僵尸进程 | 孤儿进程

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

【看表情包学Linux】进程状态解析 | 运行态 | 终止态 | 进程挂起与阻塞 | 运行态R | 阻塞态S/D | 死亡态X | 僵尸态Z | 暂停态T/t | 僵尸进程 | 孤儿进程

  451dfef5a53e4c94a3ecbfb02d167973.png🤣 爆笑教程 👉 《看表情包学Linux》👈 猛戳订阅  🔥 

fd152574bd804637aaa0801db19ae204.png

3adfa1dc7a6246d6884d39255e8e560b.png

💭 写在前面:本章我们专门讲解进程的状态。我们先学习具体的 Linux 系统状态,再去介绍 OS 学科面对的概念如何理解 —— 运行态、终止态、阻塞态以及挂起态。最后我们再把操作系统的概念对接,理解终止、阻塞和挂起。

   本篇博客全站热榜排名:未上榜


 Ⅰ. 进程状态(Process Status)

0x00 引入:什么是进程状态?

" 所谓的进程状态,本质上其实就是个整数。"

进程状态在 Linux 内核中就是个 整数,这个整数在进程的 task_stuct 中:

int status

a238ee82fbb64fa0a1898b7c1b4e6552.jpeg 我们先来看看一些操作系统的书上是如何讲解的,以 《OSTEP》 为例:

学过操作系统这门课的应该都知道,一个进程通常有三种状态:就绪、运行、阻塞。

Ready(就绪):进程已经准备好运行,但由于某种原因,操作系统选择不在此时运行。

  • 在就绪状态下,进程已经准备好运行,但由于某种原因,操作系统选择不在此时运行。

Running(运行):进程正在处理器上运行

  • 在运行状态下,进程正在处理器上运行(这意味着它正在执行指令)。

Blocked(阻塞):一个进程执行了某个操作(比如I/O),因此其他进程可以使用处理器。

  • 在阻塞状态下,一个进程执行了某种操作,直到发生其他事时才会准备运行。比如进程向磁盘发起 I/O 请求时,它会被阻塞。因此其他进程可以使用处理器。

将这些状态映射到图上表示:

3dad447eb43d410ebd995c46130f0552.png(进程:状态转换)

这些操作系统教材的理论,必须保证在 Linux 下是正确的,在 Windows 下也是正确的。

5ef263e02ce546af91d72a4393b2afa2.jpeg 既然要保证它的理论哪里都是正确的,那这不就是哲学么……

" 方法论:经济基础决定上层建筑 "

这句话放在哪里都错不了,所以操作系统教材里的理论,解释什么系统都说得通的。

这也是操作系统这门课,为什么称之为 " 计算机学科的哲学 "

我们这是 Linux 教学专栏,我们具体问题具体分析,我们为了清楚的讲解,我们不得不避开哲学。

我们先学习具体的 Linux 系统状态,再去介绍 OS 学科面对的概念如何理解。

0x01 运行态

📚 运行态:进程在运行队列中,代表我已经准备好了,随时可以调度。

beac0e74416a4e77b667b6848d3e41e0.gif 进程只要在运行队列中,就叫做 运行态

每一个 task_struct 都能找到对应的代码和数据,让进程排队。

ab18f8054ce34185a3c988952efb3115.jpeg

只有在运行队列里的进程才叫做运行态,而运行态不代表我正在运行,而代表已准备就绪。

0x02 终止态

📚 终止态:进程还在,只不过永远不会被调度器调度运行了,它随时等待被释放。

❓ 思考:进程都终止了,为什么不立马释放对应的资源,而要维护一个中止态呢?

释放要花时间吗?有没有可能,当前你的操作系统很忙呢?

24321f717d32463fb606d6bcf071b6a0.gif 释放也需要成本和时间!

所以说,你退出程序系统直接退出,只是理想状态罢了。既然操作系统不一定能立马释放,那我们就必须得维护一个终止态,来告诉操作系统自己已经退出了,等操作系统不忙的时候再来释放。

0x03 进程阻塞

进程阻塞,作为用户看到的就是 —— 草,我的进程卡住了!

92864bc6f22b4b82823ef95a623f74cd.png

📚 阻塞:进程等待某种资源(非CPU),资源没有就绪的时候,进程需要在该资源的等待队列中进行排队,此时进程的代码并没有运行,此时进程所处的状态就叫做阻塞。

1e3145e6b96b404b9e9530cd540a1ca2.gif为了讲解进程阻塞,我们先了解两个知识点:

① 一个进程使用资源的时候,可不仅仅是在申请 CPU 资源
② 进程可能会申请其它资源:磁盘、网卡、显卡,显示器资源……

如果我们申请 CPU 资源无法暂时无法得到满足,这就需要排队的 "运行队列" 。那么如果我们申请其他慢设备的资源呢?也是需要排队的(task_struct 在进程排队)。

1145d28afecd49479bb24ee50ec42c87.jpeg CPU 的轮转周期既然很快,CPU 得多快呢?

外设速度满,CPU 太快了,所以才会有内存这样的设备。操作系统的核心工作叫做先描述再组织,通过这样的方式来对软硬件资源作管理。

来,我们举个例子:

struct cpu_info {    // 速度    // 帧数    // 频率    // runqueue    task_struct* queue;};

正在等待运行的进程,里面的每一个格子都是一个 task_struct:

queue -> [ ] -> [ ] -> [ ] -> [ ]

a74d273006db4f48b6faa28b7e173163.gif 其它设备在操作系统内也需要管理起来,仍然是先描述再组织:

struct disk_div {    // 磁盘属性    task_struct* wait_queue;};struct net_div {    // 网卡的属性    task_struct* wait_queue;};...struct other_div {    // 属于自己的属性    task_struct* wait_queue;};

我们所看到的软件在系统中一定会存在数据结构维护描述,每一个都有对应的资源。

假设现在有一个进程正在被 CPU 调度,它要读 1G 的数据到内存,此时 CPU 就开始执行它读数据的代码,可是磁盘当前正在忙着呢:

 " 磁盘兄,你先别急,你先等一等,我还没有好,等我能读的时候再给你读 "

这种情况传统意义上就是 IO 读取时,可是磁盘数据没有就绪,进程该怎么办?

难道就眼睁睁地看着该进程占 CPU,在 CPU 上傻等吗?占着茅坑不拉屎?

702144c62f5e4744a1ffee11fa594087.jpeg 不!不可能,绝对不可能,我操作系统天下无敌。

如果磁盘没就绪,我就把你这个进程丢到磁盘的等待队列当中。

在你等待的期间,我们的 CPU 正在同时处理其他任务,如果此时又有个读网卡的进程,操作系统还在忙着呢,就直接丢到网卡的等待队列当中。

58a5cdb7b5db4bad8a39d135f333dce3.jpeg 所以,当访问某些资源(磁盘,网卡等),如果该资源暂时没有准备好,或者正在给其他进程提供服务,那么此时:

① 当前进程要从 runqueue 中逐出。
② 将当前进程放入对应设备的描述结构体中的等待队列。

上面这些任务都是由操作系统完成的,实际上就是对进程的管理任务,仍然是 "管理" 的本质。

当我们对应的设备就绪,在硬件层面上准备完毕,一定会通过某种方式让操作系统知道。

(这种方式我们放到后面说)

b8f4e172f31745ce8ff2fdd5f7137821.jpeg 如果已经可以入场了,会把该进程的 gif.latex?%5Ctextrm%7BPCB%7D 从等待队列放入运行队列中。放到运行队列中,CPU 就可以去处理这个进程了。

当我们的进程此时在等待外部资源的时(处于等待队列),该进程的代码不会被执行。

当前进程去等待某种资源就绪而导致并不运行时所处的状态,就叫做 进程阻塞

操作系统要进行管理,必须先描述再组织,所有的资源都是需要排队的,本质上都是 gif.latex?%5Ctextrm%7BPCB%7D 在排。

0x04 进程挂起

a9b63146db8a427b96835ab89ffe346c.gif (挂起???)

进程挂起:一个进程对应的代码和数据被操作系统因为资源不足而导致操作系统将该进程的代码和数据临时地置换到磁盘当中,此时叫做进程挂起。

挂起和阻塞很像,最终挂起也是卡住,但是挂起和阻塞在操作系统的定义上是不一样的。

我们还是带着大家理解一下挂起,挂起就要换另一种讲法了:

220bbe51e0554febb2871ecab3ebef2d.png

60dae54dafae4643911488cd5c4ef97f.jpeg如果内存不足了怎么办?操作系统就要帮我们进行 辗转腾挪

短期内不会调度(你等的资源,短期内不会就绪)进程,它的代码和数据依旧在内存中,

那岂不是在白白的浪费空间?

你这种浪费空间的进程,留你在内存里 只会把米吃贵 ¥%@##¥*!"

0933986a865c4703b5f885177a7dd67b.png

 操作系统就会把 该进程的代码和数据置换到磁盘上,这样的进程就是 进程挂起

b6f7c9f24f994d63b3edf18f8490c7f6.png

往往内存不足的时候,伴随着磁盘被高频率访问,就可能是因为操作系统一直在做辗转操作。

Ⅱ. Linux 进程状态

0x00 引入:我只知道 R 运行态,剩下的 SDTTZX 都是啥?

文章开头说的 ——

" 所谓的进程状态,本质上其实就是个整数。"

那这些 整数 究竟是什么?我们现在就来研究一下。

为了研究 Linux 进程状态,我们把源码先拿出来看看:

165e840bcf3e4733a6e7b793cd05967a.png

其中 gif.latex?R 就是运行态,其他的 gif.latex?SDTTZX 我们都还没讲,且听我娓娓道来!在逐个讲解之前,

我们先给大家做一个实践,请读者认真按顺序阅读以下 gif.latex?%5Ctextrm%7B0x01%7D 部分的内容,即可留下深刻的印象!

(我会用魔性的表情包去征服你,让你很长时间都忘不了的)

0x01 实践:CPU 的速度可能远超你的想象

哪里可以看某个进程的状态呢?可以用 ps 看!

a934744aefe249d4ae7861ee1bb9d68b.png

我们还是用上一章学的 ps 方式,当时是为了看 gif.latex?%5Ctextrm%7Bpid%7D 的。

这里我们也能拿 ps 指令来看进程的状态,我们开小窗输入:

ps axj | head -1 && ps axj | grep process | grep -v grep

这个 gif.latex?%5Ctextrm%7BSTAT%7D 栏记录的就是该 process 可执行程序进程的状态了:

2e0b3b3ec4144ee4ba7a81e24ab3c0b9.png

7bfd42866843445b93d43f0abab30d99.jpeg怎么是 gif.latex?S+,我们的 process 不是在运行吗,怎么不是 gif.latex?R

"不要用你的猜测和感受去衡量 CPU 的速度"

首先需要声明一点,状态后面跟加号,表示是一个 前台进程,你只需要知道的是,能够在键盘上 Ctrl+c 暂停的都可以叫前台进程。

我们 process.c 里的代码值得执行的也就一个 printf 输出语句而已,很快时间就完了。

所以大部分时间你都在 sleep(1) !

cc14f1f962da465eb916586323c9f424.gif 好,既然如此,那我们把 sleep(1) 给砍了,这下总该是一直在运行了吧?

63ebf1e340854a1bbf9a2764785955ac.png

你看,没有  sleep(1) 的束缚,我们的 printf 都刷得 发癫 了:2da357cc04af9bc9864724fea4f2258b.gif

fc488ac87acd90ce3c5b9bada53b1d61.gif

我们在用 ps 指令看看状态 gif.latex?%5Ctextrm%7BSTAT%7D 栏是个什么:

fcdcba920ef94f2a983a21e5284d548d.png

dde4819b73c542d2b5c83bfece9159a3.jpeg啊这,还是 gif.latex?S 状态啊,这是为什么呢?

💡 真相

呵呵,你说 printf 就 printf 吗?这个死循环在不断像显示器打印的时候,显示器本身是个外设,它非常慢,即便它闲着呢,准备好刷新它也要花时间的,所以这个进程它看起来像死循环地进行 printf 打印,实际上这个进程 90% 的情况都在等所对应的显示器就绪进行打印,因为显示器太慢了!只是因为打印的东西很快一瞬间就完成,所以我们 ps 查看到的这个进程,大部分情况在内核中都处于 gif.latex?S 状态。

不要认为刚才刷的那么猛,很快,那就错了,人怎么能和机器比呢?你的 printf 代码就那么一点,CPU 一瞬间就跑完了,跑完之后发现显示器设备没有就绪,没办法给你刷新,冯诺依曼那一章我们说过,打印数据可不是直接打印到外设上的,是刷到我们对应的内存里的,你 pritnf 里面带了 \n 所以要刷盘,所以显示器不一定就绪,几遍就绪它也很慢。所以大部分情况下我们的进程都在等待显示器的资源。我们虽然还没开始介绍 gif.latex?S,但是我们能猜测出来了:

" S 是一个休眠状态 "

举这个例子就是为了让大家有一个深刻的印象:CPU 远比你想象中 —— 快,猛,狠 !

e47870fecf3e4dc08d5587617e5a38e7.jpeg

OK 我知道了,那我现在就是想看看 gif.latex?%5Ctextrm%7BSTAT%7D 是 gif.latex?R 状态呢?

好,现在我们再修改一下 process.c,我们就单纯的给它改成一个死循环:

82b39bd57fe34785b51c0c30f4e736be.png

这个代码也没有访问其他外设资源,页没有读文件也没有读磁盘也没打印,就纯纯的死循环。

所以这个进程不访问任何资源,只等你 CPU,只要你被运行期间不访问外设,就不会被阻塞。

不访问外设,那么死也会在等待队列里,一直在等待队列中,这就让 process 达成 gif.latex?R 状态!

0x01 S 状态(阻塞状态)

92289f192e8845d29b15835e5f096511.png

若一个进程是 gif.latex?S 状态,那么它也能称作是 阻塞状态,这也意味着它一定是在等待某种资源。

" 这就对应了我们刚才讲的 阻塞状态 的概念了,知识点桥接成功 "

我们目前等待的是硬件,刚才举的 process.c 代码例子中 sleep 其实就是等待软件。

gif.latex?S 是阻塞状态,其实是一种 休眠状态,S 代表 Sleep!睡大觉!

f1587f653ece47b28934e694c954bcf8.png 在 Linux 中它是以休眠状态的方式,让我们的进程去等待某种资源。

#include #include int main(void) {    while (1) {        printf("I am a process: %d\n", getpid());        // sleep(1);    }}

a74567c615504879a1b4e88077310676.png

为什么睡?因为它要等待资源不就绪。既然睡了,那谁会去叫醒它呢?

操作系统会去做叫醒它,将它的 gif.latex?S 状态设置为 gif.latex?R 状态,将其列入运行队列中。

gif.latex?S%5Crightarrow%20R

c23abbccb7c24d5f9c7b3c75e73e4174.png

 我们一般把 gif.latex?S 状态叫作 浅度睡眠,也叫做 可中断睡眠

  • 顾名思义,当进程处于 gif.latex?S 状态,它可以随时被唤醒。
  • 不仅仅是操作系统可以唤醒,你也可以唤醒,甚至你想杀掉它都行。
$ kill -9 [pid]

0x02 D 状态(阻塞状态)

97a462739466413a8773182bd94d20ef.png

下面我们来看 gif.latex?D 状态,gif.latex?D 状态也是一种阻塞状态,它也是要我们得进程等待某种资源。

资源不就绪,就处于某种等待状态。那么与 gif.latex?S 状态有什么区别呢?我们细看:

S (sleeping)      D (disk sleep)

b3cede26507b451a9e58775767e31dbc.jpeg disk sleep?disk 表示磁盘,那应该适合磁盘有关联了。

一般而言,在 Linux 中,如果我们等待的是磁盘资源,我们进程阻塞所处的状态就是 gif.latex?D 状态。

作为一个应用开发的程序员,可能一辈子都见不到这个 gif.latex?D 状态,但是一般做系统的工程师就经常能见到。

💭 场景例子:

0b6cd87b558847188c3e237af25c33e5.png

某进程在等磁盘干完活期间挂出 gif.latex?D 状态,此时系统繁忙 OS 发现该进程不干活,就把它干掉了。

此时磁盘写入失败了,这 500 MB 数据该如何处理?雇用磁盘的进程被杀死了,这个货该交给谁?

那就直接丢掉?如果这 500 MB 是转账信息呢?是银行系统呢?是重要数据呢?

我们不妨探讨一下,出这种问题,谁应该背锅?是 OS 的锅,还是进程的锅,还是磁盘的锅?


  1. OS(狂暴):嗷!我有权力杀掉进程,我杀掉进程有错吗?我在执行一个管理者的权利,怎么能怪我呢?
  2. 进程(委屈):从事件开始到事件结束,我这个进程好像没有做过任何事情吧?你叫我给数据给磁盘,我也给了,给的时候我等磁盘结果有什么问题?我怎么就偷懒了,就算偷懒,我也什么都没做,我就在这等磁盘给我结果,过了一会家里就来了个大汉(OS)带着一群人拿着各种斧子砍刀把我做掉了,我是受害者啊!这种事情发生你怎么能怪我啊!我真的是 "人在家中坐,锅从天上来啊。"
  3. 磁盘:看我干啥?这个故事当中,我就是个跑腿的,你进程让我干啥我就干啥,你这个进程既然在那里等,你不就是在等我写入的结果吗?早就走了你还用等?我写入就是有失败的情况,要是每次都 100% 成功你还用在这等?你既然在这等,你就是默许我能失败。况且我还不一定失败呢,我是写完了发现你进程人没了!OS 你也是,你没事杀它什么! 

ee84622ab6324ea1a29572c0bb9b47ff.jpeg它们好像说的都挺有道理哈!

甚至有些人还觉得自己是受害者,那这个锅到底该谁背呢……

此时 OS 想了想,我作为管理者,我应该给自己立个法则,创造一个 OS 都杀不死的:

"disk sleep —— gif.latex?%7B%5Ccolor%7BGray%7D%20D%7D 状态就诞生了!"

我们一般把 gif.latex?D 状态叫做 深度睡眠,也叫 不可中断睡眠

深度睡眠的 gif.latex?D 状态进程,只能通过 "一觉睡到自然醒" 自己醒来,OS 无权唤醒或杀死之。

此时通过 gif.latex?%5Ctextrm%7Bctrl+c%7D  甚至 kill -9 都没有任何卵用,只有等到磁盘读写完毕将数据交付给该进程后,

此时 gif.latex?D 状态的该进程才会醒来,其状态或将变为 gif.latex?D%5Crightarrow%20R 。

但这也并不意味着 gif.latex?D 状态进程真的天下无敌,高枕无忧了!"关机重启" 和 "拔电源" 就能干掉它。

除了这两种情况,一般没有人能将 gif.latex?D 状态进程杀死,如果你足够NB,还有一种方法:

" 直接修改操作系统源代码 "

让 OS 见到 gif.latex?D 状态也能杀掉。但这么做没有意义,既然都能杀,那 gif.latex?S 和 gif.latex?D 就没有任何区别了。

最后:深度睡眠是睡眠,它在等磁盘,由于磁盘是外设所以  D 状态也属于阻塞状态 。

"上面的整个故事,都和这个磁盘有着千丝万缕的关系。"

2fa89be75d0046b6ba44add80302d84e.png  这个 gif.latex?D 状态我们就不模拟了……可能会把我的机子磁盘打满(害怕)

当负载非常大的时,机器关机就会特别墨迹!

实际上,如果一个系统中存在大量的 gif.latex?D 状态进程,关机是关不掉的,要长时间关都关不掉,最后只能是强制断电拔插头,才能关掉。有时候强制断电你可能会发现有些东西坏掉了,这其实并不是硬件坏掉了,而是系统的某个软件就不能用了,可能在你强制断电的时候 操作系统中的某些数据正在让磁盘写,你强制断电就是将所有设备都断电这也当然会包括磁盘,所以导致数据丢了。

0x03 X 状态(死亡状态)

44a5b5d385884edb81285798f566a40c.png

gif.latex?X 状态很简单,我们刚才也介绍过了,我们先说说 gif.latex?X 状态:

dead 代表死亡,所以 gif.latex?X 状态对应的就是 死亡状态

这个没有什么好说的,gif.latex?X 状态的进程就代表死亡了,可以随时等待 OS 来收尸了。

0x04 Z 状态(僵尸进程)

我们下面重点来说一下 gif.latex?Z 状态,gif.latex?Z 表示 Zombie(僵尸),gif.latex?Z 状态就称之为 僵尸状态。 

5b5c527a86eb4b29853e68953c2e012f.png 

僵尸状态:当一个 Linux 中的进程退出的时候,一般不会直接进入 gif.latex?X 状态(死亡,资源可以立马被回收),而是进入 gif.latex?Z 状态。

gif.latex?Z 状态是一种已经死亡的状态,僵尸,什么是僵尸?

僵尸又称为活死人,是一种半死不活的东西。

就是一个进程死了之后,我们等一等,不要它立马把资源释放,阻止 gif.latex?Z 立刻进入 gif.latex?X 状态。

9f0dd8a75dcc4a459509690292480a52.jpeg为什么要先进入 gif.latex?Z ?为什么?

进程为什么被创建出来呢?一定是因为要有任务让这个进程执行,当该进程退出的时候,我们怎么知道这个进程把任务给我们完成的如何了呢?当然要了!一般需要将进程的执行结果告知给父进程或OS。

进程为 gif.latex?Z 状态,就是为了维护退出信息,可以让父进程或者 OS 读取的,退出信息会写入 test_struct。

通过进程等待来进行读取的:

父进程可以通过进程的 gif.latex?%5Ctextrm%7BPCB%7D 中的 exit_state, exit_code, exit_signal 变量查看进程的退出状态。

至于 如何读取 和 如何等待 的问题,我们会放到下面的章节讲解。

bb3e99d71333441782e2771118743bd0.jpeg 僵尸进程存在的意义:表征进程退出时是因为什么原因而退出的。

现在我们来模拟一下僵尸进程,很简单:

如果创建子进程,子进程退出了,父进程不退出也不等待子进程(回收),此时子进程退出之后所处的状态就是 gif.latex?Z 状态。

💬 代码演示:模拟一个僵尸进程

#include #include #include int main(void) {    pid_t id = fork();    if (id == 0) {        // child        int cnt = 5;        while (cnt) {            printf("我是子进程,我还剩下 %ds\n", cnt--);            sleep(1);        }        printf("我是子进程,我已经变僵尸了,等待被检测\n");        exit(0);    }    else {        // father        while (1) {            sleep(1);        }    }}

用 ps 检测一下看看,我们每隔一秒检测一次,写一个监控脚本:

while :; do ps axj | head -1 && ps axj | grep process | grep -v grep; sleep 1; echo "######" ; done

315400de88b84adc99344e2f8d89daa9.jpeg 效果如下所示:

be2b32616d4bbd7d6651e0fac27ab3ea.gif

🚩 运行结果:

7ae7e5564a954f15ba0ca873cd40b9c3.png

❓ 思考:长时间僵尸,会引发什么问题?

如果没有人收尸,该状态会一直维护,该进程的相关资源 (task_struct) 不会被释放!内存泄露!一般必须要求父进程进行回收,如何回收的问题我们会在进程控制章节讲解。

没人收那我 kill 掉它可以吗?

13ab93c96ce34d6f8e1d4e24d18ddbf4.png 拜托林北,他本来就死掉了啦,你还 kill 它有甚么用呐真是的……

0x05 顺带讲解:孤儿进程

我们顺便再讲解一下孤儿进程,孤儿进程顾名思义,就是父亲先退出了,孩子还在的情况。

我们来模拟一下孤儿进程的情况,模拟让子进程一直不退出,父进程倒计时很快退出即可:

#include #include #include int main(void) {    pid_t id = fork();    if (id == 0) {        // child        int cnt = 5;        while (1) {  // 死循环,孩子进程就不退了            printf("我是子进程,我还剩下 %ds\n", cnt--);            sleep(1);        }        printf("我是子进程,我已经变僵尸了,等待被检测\n");        exit(0);    }    else {        // father        int cnt = 3;        while (cnt) {            printf("我是父进程,我: %d\n", cnt--);            sleep(1);        }        exit(0);    }}

b8afc499eedd46e6b9fa9b27c66962ac.jpeg 还是用我们刚才写的监控脚本,监控一下:

while :; do ps axj | head -1 && ps axj | grep process | grep -v grep; sleep 1; echo "######" ; done

🚩 运行结果:

c99a2dc5a85548d5a0119fc6e7184436.png

❓ 疑问:父进程退出,为什么父进程没有变成僵尸?我们怎么没有看到父进程 gif.latex?%5Ctextrm%7BSTAT%7D 为 gif.latex?Z ?

那是因为父进程的父进程是 gif.latex?%5Ctextrm%7B-bash%7D,它会自动回收它的子进程,也就是这里的父进程。这里之所以没有看到父进程变成僵尸,是因为被 gif.latex?%5Ctextrm%7B-bash%7D 回收了,gif.latex?Z%5Crightarrow%20X 的状态很快,所以你没看到。

那为什么刚才我自己代码中的父进程创建的子进程,父进程没有回收子进程呢?那是因为你的代码压根就没有写回收,所以你的子进程就没有回收。

说回孤儿进程,既然子进程需要父进程回收,如果父进程先提前退出了,那孤儿进程如何处理?

e73985fe646343d0b0ea56fce846c1b0.jpeg 你能想到的问题,操作系统也能想到!

细心的读者应该发现了,上图中父进程退出后,子进程 gif.latex?%5Ctextrm%7BPPID%7D 变为了 1,其实就是被 "领养" 了。

也就是说,如果父进程  提前退出,子进程还在运行,子进程会被 1 号进程领养。

1 号进程是何方神圣?其实就是操作系统!

1 号进程,即 gif.latex?%5Ctextrm%7Bpid%7D 为 1 的进程,Linux 系统启动后,第一个被创建的用户态进程。

我们 top 下看看:

31b9cef31c1f4f0db4b2b7c706a152fa.png

我们把被 1 号进程领养的进程,称之为 孤儿进程 

前面我们说了,gif.latex?%5Ctextrm%7BSTAT%7D 后有加号的属于前台进程,只要能被 Ctrl + c 干掉的都是前台进程。

而我们的孤儿进程似乎 gif.latex?%5Ctextrm%7BCtrl+c%7D 都无可奈何:

05f3948ba5404299a240d4fd0fb66f87.png

刚才变成孤儿进程后 gif.latex?%5Ctextrm%7BSTAT%7D也是从 gif.latex?S+ 变为了 gif.latex?S,这就叫做 后台进程

b6ae2927d8bd40c68c625b5b53697dc3.png

后台进程其实还是在运行,只是会影响命令行输入,既然 Ctrl + c 都无法奈其何,kill 算了!

诶,我们刚才说的是僵尸进程 kill 杀不死,但是这是孤儿进程,孤儿还是人!因为……

"人被杀,就会死。"

b0c22b37a24846a587c189b2f3e69c95.gif 所以我们的 kil -9 还是可以大显神威的:

kill -9 [pid]

5c6dd50ba5dd45499569a69a2ab13c1e.png

0x06 T 状态(暂停状态)

讲到这里,想必大家 gif.latex?RSDZX 都非常清楚了,只剩下这两个 gif.latex?T 了:

b048f04b572f4aabb4f5086de6f9a7f5.png

这两个 gif.latex?T 分别是 stopped 和 tracing stop,这两个状态可以说是一种状态:暂停

gif.latex?T 在内核当中实际上没有区别,给用户呈现的都是大 gif.latex?T,只是 tracing stop 比较特殊一些。

2288f1bd90764a70b1414f7870aa7918.gif 我们先介绍一下这个 暂停 究竟是什么,什么情况进程会被暂停呢?

进程暂停与进程休眠(阻塞)  没有关系,只是单纯不想让这个进程跑了。

比如有些进程在执行任务时,用户想让这个进程暂停一下,这其实很好理解。

比如看视频,听音乐,下载,都会有暂停。当你点击暂停的时候下载对应的代码就不跑了,此时这个进程你就可以认为是暂停状态。

再比如说我们调试程序,让程序打断点之后让程序运行起来,程序在打断点处停住的时候是将进程暂停了,所以你在 gif.latex?%5Ctextrm%7Bgdb%7D 调试或在 VS 下调试时你会发现程序会停下来,这就是暂停。

所以,暂停具备很强的功能性。

为了方便讲解,我们将 stopped 记为 gif.latex?T,tacing stop 即为 gif.latex?t ,我们先来讲一下 gif.latex?T 暂停。

💭 演示:gif.latex?T 暂停的概念

再上一章介绍 kill 命令的时候我们说后面跟 -9 就行了,具体的内容我们放到后续的信号章节去讲解,这里我们暂且再讲一下,我们可以输入 kill -l 列出所有的信号:

$ kill -l

 dbfefe0d4d8f4a33bda907ff621eefa8.png

我们可以看到 -9 实际上就是 gif.latex?%5Ctextrm%7BSIGKILL%7D,我们现在需要用一下 -19 的 gif.latex?%5Ctextrm%7BSIGSTOP%7D 信号。

这就是用来暂停的!我们让一个进程跑起来,然后输入  kill -19 [pid]  看看效果如何:

83c094c9e0db44a4bd7cbc6fd99145ba.png

此时,我们查看一下该进程的状态,发现 gif.latex?%5Ctextrm%7BSTAT%7D 已经变为 gif.latex?T 状态了:

35268adb681949979063431d7f3cbde2.png

我们将该进程暂停了,现在如何将进程解除暂停呢?用 -18 gif.latex?%5Ctextrm%7BSIGCONT%7D 信号。

CONT 就是 continue,继续的意思,输入  kill -18 [pid]  即可解除暂停:

390b13eeca944c16b39e7e2499624845.png

我们可以通过信号的方式来控制进程的起和停。

讲完 gif.latex?T (stopped) 暂停的概念,我们来讲一下 gif.latex?t (tracing stop) ,我们还是通过一个例子去讲解。

💭 演示:gif.latex?t 暂停的概念

我们先修改一下我们 process.c 的 Makefile 文件,加上 gif.latex?%5Ctextrm%7B-g%7D 以获得调试信息:

f115f1520f9e4374bc6b55db1b777a28.png

然后我们通过 gif.latex?%5Ctextrm%7Bgdb%7D 调试我们的 process:

$ gdb process  # 进入gdb调试(gdb) l        # 查看代码(gdb) b 9      # 打断点

 2a9f152183f8415bbe688515e56f4990.png

gif.latex?%5Ctextrm%7Bgdb%7D 跑起来后我们输入我们启动刚才的监控脚本:

while :; do ps axj | head -1 && ps axj | grep process | grep -v grep; sleep 1; echo "######" ; done

此时我们能看到有一个 gdb process 正在被调试,这是我们的 gif.latex?%5Ctextrm%7Bgdb%7D 进程:

5fcf85259f704c0a8242a1af2df26730.png

此时 gif.latex?t 状态就出现了,它叫做 tracing stop,即 追踪暂停

追踪暂停指的是:进程被调试的时候遇到断点时所处的状态。

  • gif.latex?T (stoped):常规暂停
  • gif.latex?t  (tracing stop)  :追踪暂停

🔺 总结:至此,所有的 gif.latex?RSDTtZX 全部讲解完毕!

  • 运行状态:gif.latex?R
  • 终止状态:gif.latex?Z%2CX
  • 进程阻塞:gif.latex?S%2CD
  • 进程挂起:gif.latex?S%2CD%2CT%28t%29

以上就是操作系统原理和具体操作系统实现之间的查别!本章结束!

4996e24d83714ba898bded7a0de56a73.gif

7a80245f0b5f4021a033b3789a9efdeb.png

📌 [ 笔者 ]   王亦优📃 [ 更新 ]   2023.1.17❌ [ 勘误 ]   📜 [ 声明 ]   由于作者水平有限,本文有错误和不准确之处在所难免,              本人也很想知道这些错误,恳望读者批评指正!

📜 参考资料 

C++reference[EB/OL]. []. http://www.cplusplus.com/reference/.

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

百度百科[EB/OL]. []. https://baike.baidu.com/.

比特科技. Linux[EB/OL]. 2021[2021.8.31 xi

来源地址:https://blog.csdn.net/weixin_50502862/article/details/128719921

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

【看表情包学Linux】进程状态解析 | 运行态 | 终止态 | 进程挂起与阻塞 | 运行态R | 阻塞态S/D | 死亡态X | 僵尸态Z | 暂停态T/t | 僵尸进程 | 孤儿进程

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

目录