Re: Linux系统篇(十八)进程篇·三:深度硬核!全面起底 Linux 进程状态变化与内核链表动态解绑

news2026/5/21 22:28:51
◆ 博主名称 晓此方-CSDN博客大家好欢迎来到晓此方的博客。⭐️Linux系列个人专栏 【主题曲】Linux⭐️此方的GitHub github_此方⭐️Re系列专栏我们思考 (Rethink) · 我们重建 (Rebuild) · 我们记录 (Record)文章目录概要序論一、操作系统的“进程状态”二、Linux中的进程状态2.1进程的状态有哪些2.2调度队列FIFO算法进程运行状态2.3设备树进程的阻塞状态2.3.1什么是设备树2.3.2什么是阻塞状态2.4交换分区与进程挂起2.4.1交换分区的定义2.4.2挂起与优先挂起2.5进程的状态变化三、理解内核链表的相关话题3.1内核链表不是传统链表3.2为什么内核链表要这么设计3.2.1更方便的偏移量计算——offset宏3.2.2彻底理清“流动”的本质多链表动态解绑与重组3.2.2.1物理上它们“住在一起”3.2.2.2逻辑上它们属于“完全不同的绳子”3.2.3.3“流动”是如何发生的3.2.2.4核心总结四、进程状态的查看4.1回顾Linux的进程状态类型4.2运行R与睡眠S状态4.3停止T与追踪停止状态t4.4深度睡眠D4.4.1对比一般睡眠状态4.4.2深度睡眠的意义4.4.3测试方法概要序論Hello大家好我是此方上文我们初步探讨了[进程]这个话题了解了进程的概念和进程的父子关系今天将继续深入了解进程的各种状态好的现在我们开始吧。一、操作系统的“进程状态”如下图精辟的总结了课本上提到的进程状态确实有些复杂。不过我想要说的是这张图讲解的是操作系统的进程状态的总理论而我们今天要讲的Linux进程状态和它的关系是特殊性与普遍性的关系。二、Linux中的进程状态2.1进程的状态有哪些为了弄明白正在运行的进程是什么意思我们需要知道进程的不同状态。一个进程可以有几个状态在Linux内核里进程有时候也叫做任务。下面的状态在kernel源代码里定义/* * The task state array is a strange bitmap of reasons to sleep. * Thus running is zero, and you can test for combinations of * others with simple bit tests. */staticconstchar*consttask_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运行状态running并不意味着进程一定在运行中它表明进程要么是在运行中要么在运行队列里。S睡眠状态sleeping意味着进程在等待事件完成这里的睡眠有时候也叫做可中断睡眠interruptible sleep。D磁盘休眠状态Disk sleep有时候也叫不可中断睡眠状态uninterruptible sleep在这个状态的进程通常会等待 IO 的结束。T停止状态stopped可以通过发送 SIGSTOP 信号给进程来停止T进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。X死亡状态dead这个状态只是一个返回状态你不会在任务列表里看到这个状态。一句话都听不懂没有关系拆开来我们一个一个讲2.2调度队列FIFO算法进程运行状态在谈论进程的运行状态之前我需要先讲一讲进程的调度队列我们上一篇中讲到了进程是由PCB代码与数据构成的进程的PCB里面有一个指针指向下一个PCB于是就形成了全局进程链表。我们这里讲的队列和全局链表非常相似为什么这个它即是链表又是队列我们后面会讲。先抛出一个结论:“进程在调度队列里面进程处于运行状态”。通过进程的调度队列我们也得到一个名词“进程的FIFO算法”: 进程按照队列的规则先进先出依次被CPU调用执行。2.3设备树进程的阻塞状态2.3.1什么是设备树我们的计算机它的摄像头麦克风键盘鼠标显示器以及磁盘网卡等等一系列的硬件他们都是操作系统通过硬件驱动调用的然而硬件驱动调用硬件不是直接访问硬件本身实际上其内部也经厉了“先描述再组织的过程操作系统将他们的数据封装成为一个结构体然后将他们组织在一起于是就形成了设备树。我后来发现这一段的讲解极为不准确设备树和等待队列没有任何关系。我在后文3.2.2有纠错。2.3.2什么是阻塞状态设备树上的每一个结点都有一个等待队列输出一个结论进程在等待队列中的时候就是阻塞状态。那么什么是阻塞当一个进程正在运行但它发起的某个请求通常是 I/O 请求暂时无法得到满足或者必须等待某个事件如磁盘数据读取、网卡数据到达发生时操作系统会剥夺该进程的 CPU 使用权将其从运行队列Run Queue中移出。然后该进程就会被链入对应设备的等待队列当中。等待设备就绪。什么是就绪又一个问题。这很简单我们键盘键入硬盘读取这些都是设备就绪。2.4交换分区与进程挂起挂起首先你要知道这是一种非常极端的情况一般不会发生。什么时候会发生呢你的操作系统认识到内存资源严重不足的时候我们所讲的内存资源是“物理内存”而不是“虚拟内存”放在虚拟地址空间中讲2.4.1交换分区的定义交换分区我们首先需要了解的概念。它是磁盘中的一个概念在磁盘中有这么一块临时区域用来临时存放从内存中交换过来的代码。2.4.2挂起与优先挂起那么当程序挂起就是操作系统将程序的代码和数据从内存中提取出来挂在这个交换分区当中。以此来腾出空间给其他进程使用。缓减内存压力。这个过程中PCB不会动。那么谁会被优先带走呢答案是正在阻塞的进程。他们不着急使用CPU资源所以先挂起。在极端中的极端情况有可能运行队列中靠后的一些进程的代码和数据也会被挂起来。当然这很少见。2.5进程的状态变化从运行到阻塞从阻塞到挂起从运行到挂起以及他们的所有的反方向。在之前我们全部讲完了。现在给一张总结图三、理解内核链表的相关话题3.1内核链表不是传统链表我们Linux中的一个个PCB他们被链接起来形成一个链表这没有错但是现在我要讲的可能会颠覆你的认知。Linux内核使用的链表不是传统的链表传统的链表我们可以画一个潦草的示意图一个带头循环双向链表每一个结点里面都会有两个struct Node*指针指向上一个/下一个结点。那么Linux的内核链表呢试想一下把前后指针独立封装一下让它和你的数据解耦会发生什么这就是Linux内核链表的想法。structlist_head{structlist_head*next,*prev;};structtask_struct{intx,y,z;structlist_headlinks;...};也即是说现在这个链表本身依赖的不是指针而是这样一个连接器结构体它来帮助前后PCB建立联系。3.2为什么内核链表要这么设计3.2.1更方便的偏移量计算——offset宏现在如果你手里只有一个指向links成员的指针想要拿到整个task_struct的首地址你就必须知道links在这个结构体里“往后挪了多少距离”。结构体首地址 成员地址 − 偏移量 结构体首地址 成员地址 - 偏移量结构体首地址成员地址−偏移量有一种非常通用的计算方式o f f s e t ( ( ( s t r u c t t a s k _ s t r u c t ∗ ) 0 ) → l i n k s ) offset \(((struct\ task\_struct\ *)0)\to links)offset(((structtask_struct∗)0)→links)这种计算方式实在是太妙了。首先人为设定起点将 0 强制转换为结构体指针相当于在内存地址 0 的位置“虚拟”出一个结构体。算位置访问成员 links 并取地址编译器会根据内存对齐规则算出它相对于起点即地址 0的距离。空位即偏移因为起点是 0所以得到的成员地址在数值上就精准等于它在结构体里的偏移量。C自带一个宏可以直接获取这个偏移量上面的讲解就是它的底层。首先包含头文件#include stddef.h第一个参数填结构体类型第二个填成员名。size_t offsetoffsetof(structtask_struct,links);3.2.2彻底理清“流动”的本质多链表动态解绑与重组这一段内容会再一次颠覆你的认知。在探讨进程如何在各种队列中“流动”之前我们需要先纠正一个极易混淆的误区⚠️重要纠错设备树≠ \neq等待队列我上文把“设备树”和“等待队列”混为一谈认为进程阻塞是挂在设备树上。这是极其不准确的设备树和等待队列没有半毛钱关系。设备树 (Device Tree)是系统启动时内核用来清点、初始化硬件的静态“点名册”不参与运行时进程的调度。等待队列 (Wait Queue)是驱动程序内部维护的运行时动态链表专门用来组织正在等待该设备的进程。3.2.2.1物理上它们“住在一起”在物理内存中一个进程有且仅有一个task_struct进程控制块 PCB实例。这个结构体非常庞大包含了一个进程的所有核心信息PID、状态、优先级等。它就像一个巨大的“多功能插线板”身上长满了各种不同的接口 ——这些接口就是内嵌在其中的struct list_head成员变量。3.2.2.2逻辑上它们属于“完全不同的绳子”虽然所有的插口都长在同一个 PCB 上但每个插口连接的都是不同的、相互独立的、没有交集的独立链表。这就是著名的“用绳子穿过实体”的设计全局链表Tasks List像一根红色的绳子横向穿过每个 PCB 的tasks插口。只要进程存活这条线就永远不解绑内核通过它来遍历系统中的所有进程。运行队列Runqueue像一根橙色的绳子只穿过那些状态为“就绪Ready”或“运行Running”的 PCB 的run_list插口。调度器Scheduler只在这条线上挑选进程去 CPU 上执行。某一条等待队列Wait Queue设备千千万等待队列的蓝线也有千千万。每个硬件设备网卡、键盘、磁盘等都有自己专属的一条等待队列线。3.2.3.3“流动”是如何发生的当一个进程从“运行态”变成“阻塞态”例如等待读取磁盘文件时内核绝对不会去搬动或复制这个巨大的 PCB它在幕后只干了两件事解绑断开旧线调用list_del把该 PCB 负责“运行队列”的run_list钩子从橙线上取下来。重连挂上新线调用list_add把该 PCB 负责“等待队列”的wait_list钩子挂到该磁盘设备特有的那条蓝色等待队列线上。3.2.2.4核心总结实体唯一内存中只有task_struct一个实体静静地呆着。状态改变 关系解绑与重组进程所谓的“状态流动”物理本质上只是不同颜色的绳子链表指针在 PCB 的不同插口上进行解绑和重新勾连的过程。高效率的秘密这种设计让进程状态切换变成了极其高效的指针操作O ( 1 ) O(1)O(1)复杂度不需要任何内存拷贝完美体现了 Linux 内核“扁平化且高效”的管理哲学。四、进程状态的查看4.1回顾Linux的进程状态类型把上面你看不懂的那张表搬过来现在你至少能读懂一部分了接下来我们来查看一下进程的状态/* * The task state array is a strange bitmap of reasons to sleep. * Thus running is zero, and you can test for combinations of * others with simple bit tests. */staticconstchar*consttask_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 */};进程状态的本质就是一个在PCB中的整型是一个状态数组的下标。4.2运行R与睡眠S状态Linux的Sleep就先当于操作系统的阻塞状态#includeiostream#includeunistd.hintmain(){std::coutstd::unitbuf;intcount0;pid_t pidgetpid();while(true){std::cout我是后台进程, PID: pid, 循环次数: countstd::endl;sleep(1);}return0;}为什么我们查到的进程状态都是S?因为我们的进程是一个循环打印假设执行一次是1毫秒在这毫秒中99%的时间是在等待显示屏就绪1%的时间是拿来打印于是运气不好我们查不出来。#includeiostream#includeunistd.hintmain(){// 打印当前的 PID方便你后续去查状态或杀进程std::cout进程已启动, PID: getpid()请在另一个终端观察其状态...std::endl;// 纯 CPU 计算的死循环没有任何 I/O 打印也没有 sleepwhile(true){}return0;}不让他IO查出来全部都是R如果进程在前台那么状态前面就有一个号否则没有。加上表示当前进程在后台启动[whbbite-alicloud lesson13]$ ./myprocess4.3停止T与追踪停止状态tt表示进程处于“正在被追踪而停止”的状态。虽然它也属于停止状态但它与大写 T 有细微的语义区别大写 T (Stopped)通常是用户主动发送信号如 CtrlZ让进程停下的。小写 t (Tracing stop)专门用于 Debugger调试器环境。当进程被系统追踪比如你在用 gdb 调试时进程在等待调试器发送下一个指令c/s期间状态会显示为小写的 t。关于19号信号稍微补充一下信号还没有学在 Linux 信号机制中19号信号是SIGSTOP。SIGSTOP的唯一作用就是强制停止挂起一个进程。当进程接收到这个信号时它的状态会立即转变为T(Stopped)。你按下ctrl Z也是传递这个信号给进程。一种很有趣的场景含scanf等阻塞式输入函数的进程被放到后台运行。行为分析后台进程无法直接从终端读取用户输入。当进程尝试执行scanf等终端输入系统调用时内核会向该进程发送SIGTTIN信号强制将其暂停Stopped, T。解决办法用户需要通过fg命令将该进程调回前台才能正常输入并继续执行。4.4深度睡眠D4.4.1对比一般睡眠状态浅睡眠S比如有一个scanf在等待阻塞。然后我们直接按ctrlc可以直接kill它。或者执行指令kill -9 PID杀死这个是信号的知识后面会讲但是深度睡眠D状态怎么都不会杀死。如何杀掉D进程1.等他自己醒来。2.物理方法重启计算机/断电4.4.2深度睡眠的意义场景一个进程正在向磁盘写入数据但是磁盘写入数据的速度超级慢相对于进程而言于是进程就要去等待磁盘进入S状态。等待过程中可能发生一种非常极端的内存不足情况这个时候OS就要开始杀没有用的进程。这个时候可能会误杀这个正在“摆烂”的进程。于是这个时候磁盘还没有读完数据但是进程已经被干掉了磁盘拿着数据不知道该怎么办于是就只能把数据丢弃了。这个时候用户还不知道于是就丢失了数据。如果这个数据是医院里几千个病人的病历或者是银行的转账信息这是非常危险的。为了防止操作系统发生上述情况。于是一种新的状态被设计出来。D状态不可中断。4.4.3测试方法构建一个块级 IO通过以下命令模拟高强度的磁盘写入操作ddif/dev/zeroof~/test.txtbs4096count100000dd: 磁盘拷贝/转换命令。if/dev/zero: 输入文件数据来源源源不断地产生零字节。of~/test.txt: 输出文件拷贝到的目标文件路径。bs4096: 每次拷贝的数据块大小Block Size此处为 4096 字节4KB。count100000: 一共拷贝的数据块数量这里一共拷贝 100000 次。好的本期内容就到这里如果对你有帮助还不要忘记点赞三联支持。我是此方我们下期再见。bye!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2632874.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…