当前位置: 首页 > news >正文

【Linux 学习计划】-- 进程状态 | 进程运行、阻塞和挂起的本质 | 并行、并发与进程切换 | 进程优先级

目录

进程状态

五状态进程模型

运行、就绪状态的本质

阻塞状态的本质

挂起状态

并行与并发

进程切换

进程优先级

结语


进程状态

进程状态的本质是什么?

首先我们知道,在操作系统中,进程是需要被管理起来的,具体则是用一个struct(task_struct)描述进程本身,接着再用内核数据结构将所有的进程管理起来,这也就是所谓的先描述,再组织

而我们的进程状态,本质就是task_struct里面的其中一个属性,仅此而已(比如是一个个的int)

这就是内核中关于进程状态的描述

接下来我们详细讲讲这里面的 7 个进程状态

第一个是R状态

首先需要提前说的是,我们在Linux中是可以用指令查到进程状态的,也就是ps命令

我们来看这样一个代码,我们可以看到这其实就是一个死循

然后当我们将进程跑起来之后,我们再来看一看这个进程的状态:

我们可以看到,这里显示的确实是R(R+中的+代表前台执行,不需要管)

这其实也就代表说,当进程处于R状态的时候,代表进程正在运行或者准备运行

S状态

S状态表示进程正在可中断睡眠状态,简称睡眠状态,s也就是sleep

来看这样一组代码:

我们能看到,这里和死循环的唯一区别就是,他有在打印一行字符串

这时我们将进程跑起来,然后看看进程状态:

我们能看到,仅仅只是将加了一行打印,就变成了一直在睡眠

这其实是因为,我们CPU运行速度过快,而我们的显示器是外设,将数据拷贝到外设的速度比CPU的运算速度慢多了,所以进程大部分的时间并不是在运行,而是在等数据拷贝到显示器上,然后再运行

所以从理论上来说,如果我们运气好的话,是能看到一两个R状态的,只不过主包今天运气不是很好,就不做演示了

T状态

可以看到,我们现在用kill命令将进程停止了之后,进程的状态自动就变成了T,这也是停止状态

t 状态(tracing stop)

这个状态其实也是停止,和上面的 T 状态其实差不多

但是这个和 T 不一样的就是,他是调试专属的状态

试想一下,我们的调试本身就是进程已经在跑了,然后我们给他打断点,让他停在某一个位置,所以这其实也是一种进程的暂停,如下图:

左边是在用gdb调试代码,右边显示状态的时候就是 t 状态(前提是gdb是用 r 跑起来的并且打了断点)

z 僵尸进程

什么是僵尸进程呢,先来举一个例子:

如果一个人si了,那么警cha到了之后第一反应应该是封锁现场,等法医过来收集完信息之后,才会清理现场,办理后续的各种事情

而我们的进程也是一样的,如果一个进程没了,那么也需要有进程回收他,而这个进程一般情况下都是父进程(孤儿进程或者waitpid都是os直接回收)

那是否有这么一种情况,父进程还在,但是子进程已经挂掉了,这个时候父进程还没有干完自己的事情,所以是没有办法回收子进程的,只有等父进程也退出之后,才会回收子进程

那如果父进程一直不回收子进程,那么子进程这个时候将会一直是僵尸进程

如下:

我们写一个简单的fork,然后我们再在外面使用kill -9直接将子进程杀掉,这时候再去查看子进程的状态,我们就能看到 Z 状态了

可以看到,当我们将子进程给 kill 掉之后但是父进程一直不回收,这时候子进程将会一直是 Z 状态

而且很关键的一点是,如果我们在未来写了一些项目几万行代码,你甚至都不知道有僵尸进程的问题,那么父进程一直不回收,子进程将会一直 Z,Z 的时候,子进程的代码和数据都没释放掉了,之后对应的task_struct还在,因为要等父进程拿完对应的子进程的信息之后才会被释放

那么,子进程将会一直占用内存资源,这也就造成了一个大问题——内存泄漏

而在未来,我们将会学到使用waitpid等待,最后子进程完成任务退出之后将会被等待,最后直接被os拿信息接着释放,这样就不会内存泄漏了,但这不是今天你的重点,这里就不做讲解了

X状态(死亡状态)

这是一个瞬时状态,只会出现在进程死亡的一瞬间

D状态(不可中断睡眠状态)

这个状态和前面的 S 状态其实多多少少有点联系,这里就讲一个小故事:

当我们有重要的业务的时候,我们的进程就是D状态

比如在银行,如果今天这个进程要将一万个用户的转账数据放到内存中,但是如果放到一般,因为如果没有D的话,那么进程就将是S状态(睡眠),所以如果OS比较紧张的话,那么就有可能将这个进程直接kill掉或者直接打断然后派发其他任务,那么这时候数据就流失了

所以这种时候就是D状态,意味着不可被打断,你就是再怎么紧张,你也不能动我,这就是D

孤儿进程

这是进程的一种,有这么一种情况,也就是,如果父进程没了,但是子进程还在呢?那么子进程这时候如果也没了,那么数据应该被谁回收

首先父进程如果是第一个在程序里被开辟的,那么父进程的父进程就是bash,他会被bash回收,那么这时候子进程就变成了孤儿进程

直接说结论,这时候子进程会被 1 号进程(其实就是bash)领养,最后由bash进行善后处理

五状态进程模型

我们会看到,这个五状态进程模型,和我们上面讲的各种状态一个都对不上啊

其实不是对不上,而是因为本身的性质就不一样

我们的五状态进程模型是理论,而上面说的S、D、t、T等等,这些是Linux的具体实现

比如运行状态和就绪状态,这不就是R吗,阻塞,就是S等等

接下来我们直接来讲一讲,运行状态与就绪状态的本质,阻塞状态的本质,挂起状态的本质

运行、就绪状态的本质

我们来看这样一张图片,首先我们在内存中是有一个runqueue的,也就是运行队列

我们将进程管理起来之后,还需要将等待运行的队列放进runqueue这个内核数据结构中管理起来

这也就代表,只要你是被链进runqueue里面的进程,那么就是就绪和运行状态

当然你也可以这么理解,因为后续进程还需要被放进cpu中执行相关任务,你可以理解成:放在runqueue中但是还不在CPU的就是就绪状态,也就是已经准备好了,等待运行,而被CPU调度的进程则是运行状态

而在Linux中,无论是就绪状态还是运行状态,都是用 R 来表示的

阻塞状态的本质

在我们之前写代码的时候,一定会用过scanf函数

当我们将程序跑起来的时候,如果我们不在键盘上输入数据,那么进程将会一直停在那个地方

这其实就是所谓阻塞状态了,而这个状态下,进程自然就不会停留在runqueue中

那么进程会在哪里呢?

首先我们的外设都是硬件,而操作系统如果要管理外设的话,就需要驱动层的帮助,而驱动层是经过struct描述且用特定数据结构管理起来的软件

而我们外设的驱动中会有这么一个字段,叫做wait_queue(等待队列)

当我们的进程需要用外设参与操作的时候,这时候在我们看来,其实就是阻塞了

而阻塞的进程就不应该再浪费CPU资源了,所以就不能放进runqueue里面,而是会被放进对应驱动软件中的等待队列字段里面

如此一来,当我们的外设完成了对应操作之后,进程重新加载到runqueue中,这就是阻塞状态的本质了

挂起状态

当我们的内存空间过于紧张的时候,就会有挂起状态的出现

我们在磁盘当中有一个区域叫做——swap分区

当我们的磁盘空间严重不足的时候,就会出现这么一个情况:一个进程可能暂时在执行别的不需要他管的任务,或者是一些其他的进程,os 会将他们全部移动到磁盘(外设)的swap分区上
这样省出来的空间,就可以供操作系统做别的事情
等晚些这个进程要被用到的时候,再从swap分区中移动回来

但是这也会导致一个问题,也就是效率

在日常使用的感受就是——卡

因为OS中最消耗时间的就是拷贝数据问题了,现在还要将数据从内存拷贝到外设,要用的时候再从外设拷贝回来,这显然占用了很大一部分的效率

但是注意,这里的前提条件是,内存很紧张,所以这也是一种时间换空间的做法

并行与并发

我们刚刚说到了runqueue,那么进程在操作系统中到底是怎么运行的呢?

我们看到这张图,我们的进程会一个一个地被加载到CPU里面接着进行调度

但是又有不一样的地方,即他是用时间片轮转的方式进行进程调度的

一般来说,如果我们将一个进程调度完之后,才跑下一个进程,那其实不是很合理且不现实

比如我们今天打开一个网易云音乐,这个软件现在正在跑,我们就打开不了其他软件了吗?当然不

所以在OS中,他会给每一个进程一个特定的时间段,过了这个时间段就直接中止这个进程转而执行下一个进程

一直反复这个过程,只要转换的速度够快,我们人其实是察觉不出来的,就达到了每一个软件,每一个进程都在同时跑的效果

这就叫做并发

那么什么是并行呢?

我们上面的并发是只有一个CPU的情况下,很多进程在一个CPU下进行轮转调度

而并发就是现在不止一个CPU,由多个CPU同时在进行进程轮转调度的工作,这就是所谓并行了

进程切换

这个其实是接着并行与并发这两个知识点的,我们要讨论的是,进程的并发是如何进行的

我们来看这张图,首先我们进程要想加载到CPU中的话,数据是必须要拷贝到CPU中的

而CPU中的寄存器就可以暂时保存这些数据

接着,当我们这个进程运行到一半的时候,突然时间到了,他需要被切换了,那么他就需要将这些数据全部保存下来,不然的话,下次再轮到他的时候,不就相当于没跑过吗

为了保存这些数据,所以我们的PCB也就是task_struct中有专门的结构体类型,就是用来保存上下文数据的

当我们要切换进程的时候,CPU里面的寄存器的数据就会被拷贝到task_struct中的特定字段,然后再运行下一个进程

等到下一次再回到这个进程的时候,就可以将上一次保存下来的上下文数据全部放进去继续跑了

这就是所谓进程切换了

将个故事:你是一名大学生,今天你想去当兵,那么你就需要先和辅导员打个招呼,这就相当于是将CPU中寄存器的数据加载进进程的特定字段中,然后你就去入伍了。两年回来以后,你会学校还是要和辅导员打个招呼,接着就是恢复学籍数据等等,这一步其实就是将进程中的数据重新加载回CPU进行调度了

进程优先级

顾名思义,就是哪个进程最先调度

更重要的进程就需要更高的优先级

那么和上面的进程状态一样,进程的优先级其实也只是进程PCB里面的一个字段而已,这个我们可以在命令行中直接查看到:

(使用的是 ps -al 命令)

但是这里面有两个值得注意的字段:一个是最新的优先级的值,还有一个就是nice值:

这也就意味着,我们的进程优先级是可以更改的,只不过更改的方式只是更改nice值

这里更改nice值有很多方式,比如renice,但是主包这里直接用 top -> r -> 进程pid -> 你要的nice值

但是我们可以看到,这里面的优先级最多就是到了99,而nice值则是19

这是因为我们的nice值是有范围的,也就是 -20~19

再加上默认的进程优先级就是80,所以进程优先级的范围就是 60~99

结语

这篇文章到这里就结束啦!!~( ̄▽ ̄)~*

如果觉得对你有帮助的,可以多多关注一下喔

http://www.xdnf.cn/news/726085.html

相关文章:

  • Flink2.0及Flink-operater在K8S上部署
  • 基于51单片机的音乐盒键盘演奏proteus仿真
  • git查看commit属于那个tag
  • LangChain完全指南:从入门到精通,打造AI应用开发新范式
  • lua的笔记记录
  • MSTNet:用于糖尿病视网膜病变分类的多尺度空间感知 Transformer 与多实例学习方法|文献速递-深度学习医疗AI最新文献
  • LLM 使用 MCP 协议及其原理详解
  • SQL进阶之旅 Day 8:窗口函数实用技巧
  • 极简以太彩光网络解决方案4.0正式发布,“彩光”重构园区网络极简之道
  • PostgreSQL ALTER TABLE 命令详解
  • Visual Studio 2022 发布独立的 exe 文件
  • 1,QT的编译教程
  • (18)混合云架构部署
  • 论文阅读笔记——FLOW MATCHING FOR GENERATIVE MODELING
  • 二、OpenCV图像处理-图像处理
  • QT-Creator安装教程(windows)
  • 【技能篇】RabbitMQ消息中间件面试专题
  • Fusion引擎赋能:流利说如何用阿里云Serverless Spark实现数仓计算加速
  • 世冠科技亮相中汽中心科技周MBDE会议,共探汽车研发数字化转型新路径
  • 农村土地承包经营权二轮延包—生成地块的KJZB字段
  • React---day5
  • Flutter 4.x 版本 webview_flutter 嵌套H5
  • 自证式推理训练:大模型告别第三方打分的新纪元
  • GitHub 趋势日报 (2025年05月29日)
  • FPGA管脚类型,及选择
  • Vue3处理number输入框避免NaN
  • 2025年渗透测试面试题总结-匿名[校招]攻防研究员(应用安全)(题目+回答)
  • 【解决办法】Git报错error: src refspec main does not match any.
  • 使用 SymPy 操作三维向量的反对称矩阵
  • STL解析——vector的使用及模拟实现