【Linux】进程状态和优先级

个人主页~
进程状态和优先级
- 一、进程状态
- 1、操作系统进程状态
- (一)运行态
- (二)阻塞态
- (三)挂起态
- 2、Linux进程状态
- (一)R-运行状态
- 并发执行
- (二)S-浅度睡眠状态
- (三)D-磁盘休眠状态(深度睡眠)
- (四)Z-僵尸状态
- 3、孤儿进程
- 二、进程优先级
- 1、基本概念
- 2、优先级的计算
- 三、Linux的调度
一、进程状态
1、操作系统进程状态
操作系统的进程属性状态一共有三种模型,分别是三态、五态、七态模型,我们下图给出的是五态模型,七态模型多出的两态就是两种类型的挂起态,所以我们可以说操作系统进程一共有六种状态
其中新建态和退出态顾名思义就是新建进程的状态和结束进程的状态,就绪态就是进程加载完成后等待调度的状态,下面我们着重来强调一下运行态、阻塞态和挂起态

(一)运行态
运行态是指进程正在处理器上运行,它所对应的程序代码正在被 CPU 执行的状态,处于运行态的进程能够占用 CPU 资源,并按照程序的逻辑顺序依次执行指令,进行数据处理、与其他进程或系统进行交互等操作,是进程真正处于活动执行的状态
(二)阻塞态
阻塞态是指进程因等待某种事件的发生而暂时无法继续执行的状态,处于阻塞态的进程不能占用 CPU 资源,此时进程会暂停执行,直到所等待的事件完成或条件满足,才有可能重新进入就绪态或运行态
(三)挂起态
挂起态是指进程暂时被操作系统从内存中转移到外存(如磁盘)上,或者虽然仍在内存中但被标记为暂停执行的一种状态,处于挂起态的进程不再参与 CPU 的调度,其运行被暂停,直到满足特定条件后才可能恢复到其他可执行状态
2、Linux进程状态
操作系统的进程状态分有运行态-R、睡眠态(浅度睡眠)-S、磁盘休眠态(深度睡眠)-D、停止态-T(t)、死亡态-X、僵尸态-Z
其中睡眠态和停止态是有区别的,睡眠态是在进程等待的过程中可能的一种状态,停止态是无论进程的状态如何,都都可以进行的一种状态
死亡态就是进程结束后资源全部回收的一种状态,不再对系统产生负担

//kernel源代码里关于Linux进程状态的定义: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 */};
(一)R-运行状态
Linux内核在运行状态时,除了我们上面操作系统都有的行为以外,它是一个双向链表的结构,在Linux中存在一个结构体叫做运行队列,它负责维护两个 task_struct* 类型的指针:head、tail,分别指向task_struct链表的头和尾,这样我们就可以通过它对双向链表中的节点进行移动
因为一个进程一般都是死循环的状态,即我们之前谈到过的while(1)这样的,那么如果我们把进程放到CPU上它就一直跑下去吗?显而易见的是不会的,原因是每一个进程都有一个叫做时间片的概念,在CPU上执行一段时间它自己就要下来,让其他进程执行,这时,head = head -> next , tail = tail -> next,让下一个进程进到CPU中执行,由于这个CPU的速度是非常快的,所以我们是感知不到进程切换的,这样就可以做到在一个时间段内,所有进程代码都会被执行,这个过程叫做并发执行,而在这个过程中,把大量的进程不断放到CPU拿出CPU的动作,叫做进程切换
并发执行
多个进程在一个CPU下采用进程切换的方式,在一段时间内,让多个进程都得以推进,称之为并发,进程在CPU被来回切换,我们要做到每个进程在下次来的时候还能从上次结束的位置继续进行,这里就需要我们进程对上下文进行保存和恢复,这部分信息存在于哪里呢?答案是寄存器,CPU寄存器里面保存的是进程的上下文,因为寄存器是最快的存储结构,能够大大提高效率
(二)S-浅度睡眠状态
处于 S 状态的进程正在等待某个事件的发生或资源的可用,此时进程会暂时停止执行,进入睡眠状态,它可以被某些信号中断唤醒,进程在等待期间,仍然占用着一些系统资源,如内存空间等,用于维持其进程上下文和相关的数据结构,以便在事件发生或资源可用时能够快速恢复执行
例如一些IO操作,进程需要从磁盘读取数据、等待网络数据的接收或者等待用户在终端输入数据等情况,由于 I/O 操作相对较慢,进程会进入可中断睡眠状态,直到 I/O 操作完成,我们可以通过kill命令杀死进程


这里的sleep是因为我们使用了sleep函数,如果我们在程序当中不使用sleep函数,我们多次查看这里还是S状态,原因是我们在先前提到过,Linux下一切皆文件,屏幕打印也是一个文件IO的过程,所以这个进程大部分时间都在等待文件的IO,所以大部分时间都是S状态

下图可见这个进程是可以被杀死的

(三)D-磁盘休眠状态(深度睡眠)
深度睡眠状态对应的也是上面的阻塞状态,分工与浅度睡眠状态不同,相当与是将阻塞状态细分为两种状态,处于 D 状态的进程不会被信号中断唤醒,即使是像 kill 这样的强制终止信号也无法使其醒来,它只能在等待的事件完成后才会被唤醒,这是为了确保进程在执行一些关键操作时不会被意外打断,保证操作的完整性和稳定性
这里就不演示D状态了,只是如果有一个进程一直占用CPU的话,对计算机来说是一种负担
(四)Z-僵尸状态
僵尸状态其实也是死亡态的一种,只不过它并没有完全对系统资源进行回收,是当进程退出并且父进程没有读取到子进程退出的代码时产生的一种进程,叫做僵尸进程,僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码,所以只要子进程退出,父进程还在运行,且父进程没有读取子进程状态,子进程就进入了僵尸状态


我们可以通过while :; do ps aux | grep 16621; sleep 1; done来每秒循环查看16621进程的进程状态

僵尸进程一直不退出,就要一直被PCB维护,相当于不回收的一段空间,如果一直不回收,最终会导致内存泄漏
3、孤儿进程
上面提到的是子进程退出,父进程仍在运行且不读取子进程退出的状态,那么子进程就会成为一个僵尸进程,这里的孤儿进程是父进程先退出,子进程后退出,父进程退出后到子进程退出前这段时间子进程被称为孤儿进程,但没有父进程就不能进行资源释放了,这时,该进程会被系统领养也就是1号进程称为它的养父,它的父进程变为1号进程,我们称作孤儿进程被1号进程领养,最后退出时被1号进程回收

这里我们可以通过while :; do ps axj | grep process2; sleep 1; done来在右侧每隔一秒循环打印程序process2中的各个进程,我们发现在五秒后子进程的父进程变为1号进程

二、进程优先级
1、基本概念
大部分计算机只有一个CPU,而进程是有很多个的,这样就会产生多个进程去抢夺一个CPU的情况,这时就需要有优先级的存在去判定谁先来到CPU
CPU资源分配的先后顺序就是进程优先级,优先级高的先执行,通过ps -l可以查看:UID-执行者身份,也就是用户名,PID-该进程ID,PPID-该进程父进程ID,PRI-该进程优先级,NI-该进程nice值,其中PRI和NI就是和进程优先级密切相关的两个量

2、优先级的计算
PRI就是进程优先级,值越小,优先级越高,一般我们创建的进程PRI都是80,NI值可以修正优先级,PRI(NEW) = PRI(OLD) + NI,NI范围是-20~19,也就是我们所创建的进程的优先级一共有40级,需要注意的是,PRI(OLD)是一直不变的量,从创建出一个进程开始他就是这个量,只能通过后天NI值的改变修正影响PRI
三、Linux的调度
简单串联一下今天的内容,并且将Linux对于运行队列的调度通过一个简单的模型来解释
我们通过一个结构体来解释进程运行逻辑,首先我们需要有一个指针数组running,其中数组的每个元素序号都代表着优先级大小,其中,0~99这一百个位置是给其他种类的进程用的,我们所创建的进程只用100-139这四十个位置,刚好对应上前面的优先级范围,也是四十级,优先级范围是60-99,最终就会转化为100-139,每个元素就是一个指针,指向该优先级的运行队列
运行开始,先从优先级高的开始运行,再进行进程切换的过程中,优先级相同的进程运行完后,将running指针给到waiting,waiting可以看做是一个指针都指向空的指针数组,例如,running[100]所指向的进程都执行过时间片时长了,waiting[100] = running[100] ,然后running[100] = NULL,然后继续往下执行,直到所有的进程都执行过时间片时长一次,然后再将run和wait的值互换,此时,run指向waiting,wait指向running,内部是通过这个二阶指针决定哪个是运行队列哪个是等待队列的,waiting成为了运行队列,running成为了等待队列,然后按照同样的方法进行值的交换
在以上的过程中,需要有一个量来精确得知running或waiting啥时候为空,这就是isempty,这个bitmap是需要我们自己定义的,我们定义一个char*[5]类型的bitmap,刚好可以存储40个比特位,0-39对应running中的100-139,然后其中有数据该比特位就为1,没有数据就为0,在我们进行数据交互的时候改变bitmap的值,当这40个比特位都为0,即isempty为0,此时运行队列为空
struct runqueue
{bitmap isempty;//位图task_struct** run;//指向runningtask_struct** wait;//指向waitingtask_struct* running[140];//维护一个运行队列task_struct* waiting[140];//维护一个等待队列
}

我们是可以通过前后指针来访问该进程的全部内容的,在task_struct的双向链表中,其实指向前后的两个指针是放在一个结构体的,我们把这个结构体叫node
struct node
{struct node* next;struct node* prev;
}
struct task_struct
{//其他属性//...struct node* start;struct node link;
}
start指向link,link中有指向前后进程的指针,该进程就可以写作(task_struct*)(start - &(task_struct*)0->link),下面是该写法的解释:
- (task_struct*)0
这是将整数 0 强制转换为 task_struct* 类型的指针,也就是让这个指针指向地址 0,这里只是为了构建一个概念上的 task_struct 结构体,实际上并不会真正访问地址 0,只是利用它来获取结构体成员相对于结构体起始地址的偏移量 - ((task_struct*)0)->link
这一步是通过前面得到的指向地址 0 的 task_struct 指针来访问 link 成员,由于指针指向的是地址 0,所以 ((task_struct*)0)->link 得到的就是 link 成员相对于结构体起始地址的偏移量,因为在内存中,结构体成员的地址是相对于结构体起始地址的偏移,而这里起始地址为 0,所以 ((task_struct*)0)->link 的值就是 link 成员在 task_struct 结构体中的偏移字节数 - &((task_struct*)0)->link
对 ((task_struct*)0)->link 取地址,得到的就是 link 成员相对于结构体起始地址的偏移量的地址,实际上就是偏移量的值本身,例如,如果 link 成员在 task_struct 结构体中是从第 20 个字节开始的,那么 &((task_struct*)0)->link 的值就是 20 - start - &((task_struct*)0)->link
start 是 link 成员的实际内存地址。用 start 减去 link 成员相对于结构体起始地址的偏移量,就可以得到包含 link 成员的 task_struct 结构体的起始地址,这是基于内存地址的计算原理,因为结构体成员的地址等于结构体起始地址加上该成员相对于起始地址的偏移量,反过来,结构体起始地址就等于成员地址减去偏移量 - (task_struct*)
最后将计算得到的地址强制转换为 task_struct* 类型的指针,这样就得到了指向包含 link 成员的 task_struct 结构体的指针
今日分享就到这里了~

相关文章:
【Linux】进程状态和优先级
个人主页~ 进程状态和优先级 一、进程状态1、操作系统进程状态(一)运行态(二)阻塞态(三)挂起态 2、Linux进程状态(一)R-运行状态并发执行 (二)S-浅度睡眠状态…...
携程Java开发面试题及参考答案 (200道-上)
说说四层模型、七层模型。 七层模型(OSI 参考模型) 七层模型,即 OSI(Open System Interconnection)参考模型,是一种概念模型,用于描述网络通信的架构。它将计算机网络从下到上分为七层,各层的功能和作用如下: 物理层:物理层是计算机网络的最底层,主要负责传输比特流…...
Docker 部署教程jenkins
Docker 部署 jenkins 教程 Jenkins 官方网站 Jenkins 是一个开源的自动化服务器,主要用于持续集成(CI)和持续交付(CD)过程。它帮助开发人员自动化构建、测试和部署应用程序,显著提高软件开发的效率和质量…...
深入理解开放寻址法中的三种探测序列
一、引言 开放寻址法是解决散列表中冲突的一种重要方法,当发生冲突(即两个不同的键通过散列函数计算得到相同的散列值)时,它会在散列表中寻找下一个可用的存储位置。而探测序列就是用于确定在发生冲突后,依次尝试哪些…...
图像噪声处理技术:让图像更清晰的艺术
在这个数字化时代,图像作为信息传递的重要载体,其质量直接影响着我们的视觉体验和信息解读。然而,在图像采集、传输或处理过程中,难免会遇到各种噪声干扰,如高斯噪声、椒盐噪声等,这些噪声会降低图像的清晰…...
linux运行级别
运行级别:指linux系统在启动和运行过程中所处的不同的状态。 运行级别之间的切换:init (级别数) 示例: linux的运行级别一共有7种,分别是: 运行级别0:停机状态 运行级别1:单用户模式/救援模式…...
深入剖析Electron的原理
Electron是一个强大的跨平台桌面应用开发框架,它允许开发者使用HTML、CSS和JavaScript来构建各种桌面应用程序。了解Electron的原理对于开发者至关重要,这样在设计应用时能更合理,遇到问题也能更准确地分析和解决。下面将从多个方面深入剖析E…...
C++ 游戏开发:完整指南
目录 什么是游戏开发? 为什么选择 C 进行游戏开发? C 游戏开发:完整指南 1. 理解游戏开发的基础 2. 学习游戏引擎 3. 精通 C 进行游戏开发 4. 学习数学在游戏开发中的应用 5. 探索图形编程 6. 专注于游戏开发的某一领域 7. 通过游戏项目进行实…...
WebForms SortedList 深度解析
WebForms SortedList 深度解析 引言 在Web开发领域,对于数据结构的理解与应用至关重要。其中,SortedList类在WebForms中是一个常用的数据结构,它能够帮助开发者高效地管理有序数据集合。本文将深入解析SortedList类在WebForms中的应用,包括其基本概念、常用方法、性能特点…...
【hot100】刷题记录(12)-回文链表
题目描述: 给你一个单链表的头节点 head ,请你判断该链表是否为 回文链表 。如果是,返回 true ;否则,返回 false 。 示例 1: 输入:head [1,2,2,1] 输出:true示例 2: …...
深入理解 Unix Shell 管道 Pipes:基础和高级用法 xargs tee awk sed等(中英双语)
深入理解 Unix Shell 管道(|) 1. 什么是管道(Pipe)? 管道(|)是 Unix/Linux Shell 中最强大的功能之一,它允许将一个命令的输出作为另一个命令的输入,从而实现数据流的处…...
[MySQL]事务的理论、属性与常见操作
目录 一、事物的理论 1.什么是事务 2.事务的属性(ACID) 3.再谈事务的本质 4.为什么要有事务 二、事务的操作 1.事务的支持版本 2.事务的提交模式 介绍 自动提交模式 手动提交模式 3.事务的操作 4.事务的操作演示 验证事务的回滚 事务异常…...
RS485接口EMC
A.滤波设计要点 L1为共模电感,共模电感能够衰减共模干扰,对单板内部的干扰以及外部的干扰都能抑制,能提高产品的抗干扰能力,同时也能减小通过485信号线对外的辐射,共模电感阻抗选择范围为120Ω/100MHz ~2200Ω/100MHz…...
快速上手mybatis教程
基础知识 MyBatis 是一款优秀的持久层框架,其核心组件主要包括以下部分: SqlSession 作用:SqlSession 是 MyBatis 的核心接口,负责与数据库进行通信,执行 SQL 语句,并返回查询结果。它是 MyBatis 的一次会…...
本地部署DeepSeek-R1保姆级教程
近期,我国一款开源模型 DeepSeek-R1以低成本和高性能震撼了全球科技界。该模型的开源性使开发者能够在本地环境中部署和运行,提供了更高的灵活性和控制力。如果你也想在本地部署 DeepSeek-R1,可以参考以下完整的教程,涵盖Mac 版本…...
blender 相机参数
目录 设置相机参数: 3. 设置相机参数示例 4. 相机透视与正交 5. 额外的高级设置 设置相机参数: 设置渲染器: 外参转换函数 转换测试代码: 获取blender渲染外参: 设置相机参数: 3. 设置相机参数示…...
在GPIO控制器中,配置通用输入,读取IO口电平时,上拉和下拉起到什么作用
上下拉电阻作用 在通用输入的时候,也就是在读某个IO的电平的时候 一定要让IO口先保持一个电平状态,这样才能检测到不同电平状态。 如何保持电平状态? 1. 可以通过芯片内部的上下拉电阻,由于是弱上下拉一般不用 2. 硬件外界一个…...
Maven工程核心概念GAVP详解:从命名规范到项目协作的基石
Maven工程核心概念GAVP详解:从命名规范到项目协作的基石 一、GAVP是什么? 在Maven工程中,GAVP是四个核心属性的缩写:GroupId、ArtifactId、Version、Packaging。这组属性为项目在Maven仓库中提供了唯一标识,类似于“项…...
如何利用DeepSeek打造医疗领域专属AI助手?从微调到部署全流程解析
如何利用DeepSeek开源模型打造医疗领域专属AI助手?从微调到部署全流程解析 医疗人工智能正迎来爆发式增长,但在实际应用中,通用大模型往往存在医学知识不精准、诊断逻辑不严谨等问题。本文将手把手带您实现医疗垂直领域大模型的定制化训练&a…...
Redis|前言
文章目录 什么是 Redis?Redis 主流功能与应用 什么是 Redis? Redis,Remote Dictionary Server(远程字典服务器)。Redis 是完全开源的,使用 ANSIC 语言编写,遵守 BSD 协议,是一个高性…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...
永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器
一、原理介绍 传统滑模观测器采用如下结构: 传统SMO中LPF会带来相位延迟和幅值衰减,并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF),可以去除高次谐波,并且不用相位补偿就可以获得一个误差较小的转子位…...
零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》
🧠 LangChain 中 TextSplitter 的使用详解:从基础到进阶(附代码) 一、前言 在处理大规模文本数据时,特别是在构建知识库或进行大模型训练与推理时,文本切分(Text Splitting) 是一个…...
Python环境安装与虚拟环境配置详解
本文档旨在为Python开发者提供一站式的环境安装与虚拟环境配置指南,适用于Windows、macOS和Linux系统。无论你是初学者还是有经验的开发者,都能在此找到适合自己的环境搭建方法和常见问题的解决方案。 快速开始 一分钟快速安装与虚拟环境配置 # macOS/…...
