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

【Linux操作系统】简学深悟启示录:进程状态优先级

文章目录

  • 1.进程状态
    • 1.1 运行态R
    • 1.2 阻塞态S(浅度睡眠)
    • 1.3 阻塞态D(深度睡眠)
    • 1.4 挂起态T
    • 1.5 挂起态t(追踪)
    • 1.6 终止态X
    • 1.7 僵尸态Z(僵尸进程)
    • 1.8 孤儿进程
  • 2.进程优先级
    • 2.1 查看优先级
    • 2.2 修改优先级
    • 2.3 优先级调度本质
  • 3.其他概念
  • 希望读者们多多三连支持
  • 小编会继续更新
  • 你们的鼓励就是我前进的动力!

为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在 Linux 内核里,进程有时候也叫做任务),同时谁先运行也是很重要的

1.进程状态

static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};

进程状态在 Linux 系统里大致分为这几种

1.1 运行态R

在这里插入图片描述

我们知道每个进程是一个 task_struct 具体化的 PCB,以双向链表链接在一起,当需要运行进程时,CPU 会调用头指针,依次调度,如果有新的进程的话,就用尾指针加入到队列后面就行了

运行态R 指的不是正在运行,而是完全准备好了,可随时被调度器调度分配时间片给 CPU,每个时间片大概是 10ms,并不是一个进程全部运行完了才运行下一个,而是每个进程运行一点就被放下来,不断 进程切换,这样可以保证进程不会等待太久而被误杀,所有的进程会在一个时间段内被执行,叫做 并发执行

1.2 阻塞态S(浅度睡眠)

在这里插入图片描述

如图所示,对于设备来说在操作系统里也有一个队列链接起来,举个例子,当某个进程在等待键盘输入数据用于读取时,该进程会从原本的运行队列被链入到键盘的队列中,等待键盘该外部设备输入数据,该状态就叫做 阻塞态S

由于 CPU 的速度远远大于设备,因此捕捉运行中的状态很大概率都是 S 状态

1.3 阻塞态D(深度睡眠)

在这里插入图片描述

阻塞态其实有两种状态,假设有一个进程要往磁盘中写入 1GB 数据,此时写入到一半的过程中,磁盘的空间满了,该进程就有可能因为写入失败而被操作系统杀掉,数据代码等都没有了,万一这些数据很重要不就完犊子了吗?因此可以将进程设置为 深度睡眠的阻塞态D,相当于提示系统不能杀掉该进程,很重要!可以随时杀掉或唤醒的进程就是 浅度睡眠的阻塞态S

一般来说,出现一个 D 状态就说明该系统快崩溃了,有多个 D 状态进程说明这个系统基本已经崩溃了

1.4 挂起态T

在这里插入图片描述

系统中的进程只要处于闲置状态,一般都可以属于挂起状态,就拿阻塞态来说,当操作系统的内存资源严重不足的时候,就需要调整资源,会将一些不那么重要的进程,保留其 PCB,数据代码放到磁盘的 swap 区保存,此时就处于 挂起状态,当需要的时候再把数据交换回去就好了,T 状态出现的时间很短,一般很难捕捉到

1.5 挂起态t(追踪)

当进程被调试器(如 gdb)追踪时,会显示为小写 t 状态。此时进程因调试需求被暂停,等待调试指令(如断点处)

1.6 终止态X

X 状态的全称是 TASK_DEAD,表示进程已经完成执行,正处于退出清理阶段。当一个进程终止(无论是正常退出、被信号终止还是崩溃),内核会:

  • 回收进程占用的资源(如内存、文件描述符)
  • 更新进程表信息(如设置退出码)
  • 最终将进程标记为 X 状态,等待父进程通过 wait() 系列系统调用 “回收”(即清理进程表项)

这个状态只是一个返回状态,你不会在任务列表里看到这个状态

1.7 僵尸态Z(僵尸进程)

在这里插入图片描述

其实在 终止态X 之前还存在一个父子关系的状态:僵尸态Z,我们将以如图代码为例子,理解僵尸进程

在这里插入图片描述

当子进程终止之后,原先的 S 状态就会变成 Z 状态,子进程的 PCB 释放大部分资源,但是仍然有一部分资源(如进程 ID、退出码、终止原因,尤其是 task_struct 结构体)需要父进程通过 wait()waitpid() 等系统调用回收子进程,获取其退出信息并彻底清除残留数据

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

1.8 孤儿进程

孤儿进程是指其父进程已经终止或退出,而自身仍在运行的子进程

正常情况下,子进程由父进程创建并管理,父进程会监控子进程的运行状态。但如果:

  • 父进程意外崩溃或被强制终止(如收到 SIGKILL 信号)
  • 父进程正常退出但未正确处理子进程

此时,仍在运行的子进程就会成为孤儿进程,因为到最后都是需要退出这个进程的,此时子进程的父进程会变成 1 号进程(操作系统),相当于被操作系统领养了,所以叫 孤儿进程

2.进程优先级

2.1 查看优先级

[zzh_test@hcss-ecs-6aa4 zombie]$ ps -al
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S  1002 32721 32562  0  80   0 -  1054 hrtime pts/0    00:00:00 zombie_test
1 Z  1002 32722 32721  0  80   0 -     0 do_exi pts/0    00:00:00 zombie_test <defunct>
0 R  1002 32746 32723  0  80   0 - 38332 -      pts/1    00:00:00 ps

利用 ps -al 命令查看,PRI 表示优先级,即进程的优先级,或者通俗点说就是程序被CPU 执行的先后顺序,此值越小,进程的优先级别越高,那 NI 呢?就是我们所要说的 nice 值了,其表示进程可被执行的优先级的修正数值,PRI 值越小越快被执行,那么加入 nice 值后,将会使得 PRI 变为:PRI(new)=PRI(old)+nice

这样,当 nice 值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行,所以,调整进程优先级,在 Linux 下,就是调整进程 nice 值,nice 其取值范围是 -2019,一共 40 个级别

2.2 修改优先级

修改优先级用 top 命令更改已存在进程的 nice

在这里插入图片描述

R 键进入修改 nice 值的模式,然后输入对应正在运行的进程的 PID

在这里插入图片描述

接着输入范围 -19~20nice 值进行修改

在这里插入图片描述

可以看到优先值确实被调整了,一般来说是不需要调整的,只有特定的情况需要调整

2.3 优先级调度本质

在这里插入图片描述

CPU 中有一个 runqueue 结构体,存有一个 waiting 哈希表(wait 指针指针指向该表)和一个 running 哈希表(run指针指针指向该表),由于进程太多了,实际上这个哈希表是个位图,按照特定的计算方式将进程放到比特位里,对于优先级的进程来说,进程 [60,99] 对应比特位 [100,139][0,99] 是别的种类的进程,每个进程 task_struct 按照优先级从上往下,从左到右挂在 running 哈希表,运行完之后就挂到 waiting 哈希表相应的优先级位置,在这过程中可能会有些新创建的进程或者等待的进程,那么将这些进程放在 waiting 哈希表即可,当 running 哈希表执行完之后,其被指向的 run 指针,和 waiting 哈希表的 wait 指针交换,就能调度另一个队列变成运行队列了,依次循环往复即可

🤔那么如何判断该队列已经调度完了?

由于我们这个是位图,只要遍历一遍,每个比特位上是 0 就能判断已经调度完了

3.其他概念

  • 竞争性: 系统进程数目众多,而 CPU 资源只有少量,甚至 1 个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
  • 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
  • 并行: 多个进程在多个 CPU 下分别,同时进行运行,这称之为并行
  • 并发: 多个进程在一个 CPU 下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发
  • 上下文数据: 简单而言即为临时数据,进程切换时,部分上下文数据会被保存到寄存器方便下一次直接启动进程

希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

请添加图片描述

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

相关文章:

  • Android RxBinding 使用指南:响应式UI编程利器
  • 数据转换细节揭秘:ETL如何精准映射复杂业务逻辑
  • 27.Linux 使用yum安装lamp,部署wordpress
  • 【自动化测试】Selenium详解-WebUI自动化测试
  • Linux: RAID(磁盘冗余阵列)配置全指南
  • 作业标准化:制造企业的效率基石与品质保障
  • 可编辑150页PPT | 某制造集团产业数字化转型规划方案
  • idea部署到docker
  • 【MyBatis-Plus】一、快速入门
  • kafka 发送消息有哪些模式?各自的应用场景是什么?
  • 秋招笔记-8.17
  • Java 学习笔记(基础篇5)
  • 【OpenAI】 GPT-4o-realtime-preview 多模态、实时交互模型介绍+API的使用教程!
  • 宋红康 JVM 笔记 Day05|运行时数据区内部结构、JVM中的线程说明、程序计数器
  • RAID服务器
  • PDF处理控件Aspose.PDF教程:将 PNG 合并为 PDF
  • HTTP协议-4-浏览器是怎么抉择HTTP版本的?
  • 【Java基础】反射,注解,异常,Java8新特性,object类-详细介绍
  • 01.Linux小技巧
  • 「Flink」业务搭建方法总结
  • Chromium base 库中的 Observer 模式实现:ObserverList 与 ObserverListThreadSafe 深度解析
  • 《Python学习之第三方库:开启无限可能》
  • 【K8s】harbor安装与推送镜像
  • 104、【OS】【Nuttx】【周边】文档构建渲染:安装 Sphinx 扩展(上)
  • 【笔记】位错的定义和分类
  • K8s的命名空间需要创建吗
  • HAProxy使用方法以及和LVS区别
  • InfluxDB 开发工具链:IDE 插件与调试技巧(二)
  • 【DDIA】第十章:解析Reduce端连接与分组技术
  • 基于C语言实现的HRV分析方法 —— 与Kubios和MATLAB对比