ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Re:Linux 系统篇(十五):进程篇(四):进程切换深度解析 —— CPU 寄存器、进程上下文与切换全流程万字详解

Re:Linux 系统篇(十五):进程篇(四):进程切换深度解析 —— CPU 寄存器、进程上下文与切换全流程万字详解 观众老爷们大家好 这里是邪修KING的独家频道本文属于系列Linux系统篇 ——操作指令一起学Linux的小伙伴可订阅专栏 Linux系统篇 上一篇我们讲完了僵尸与孤儿进程把进程从创建到退出的生命周期补全了。很多人都知道 Linux 是多任务系统“同时” 跑几百个进程但很少有人想透一件事 CPU 就那么几个核心一个进程跑着跑着被切走过会儿又切回来为什么能精准从上次停下的地方继续执行中间的临时数据、运算结果不会丢吗 答案就是**进程上下文**。本篇我们从 CPU 寄存器讲起一步步拆解上下文的组成、切换的完整流程、硬件与软件的配合彻底搞懂多任务切换的底层原理。一、死循环进程如何运行深入理解时间片1.1 现代操作系统的公平调度单核 CPU 同一时刻只能执行一个进程但我们感觉浏览器、编辑器、终端能 “同时” 运行靠的就是时间片轮转。操作系统把 CPU 时间切成很小的时间片比如几毫秒每个进程跑一个时间片时间到了就强制切走换下一个进程。因为切换速度极快人眼感知不到宏观上就像所有进程在同时运行。1.2 喝咖啡的类比你早上同时做三件事写代码、泡咖啡、回消息。写代码写 30 秒停手去搅两下咖啡停手回一条消息停手回来接着写代码每件事都不是一口气做完的而是碎片化交替执行。因为切换很快你感觉三件事都在推进。进程调度就是同样的逻辑CPU 在几十个进程间快速切换每个进程跑一小段宏观并行微观串行。1.3 时间片轮转机制的好处公平每个进程都能分到 CPU 时间不会一个进程卡死霸占 CPU其他进程饿死响应快交互进程比如点击鼠标不用等前面大进程跑完很快就能轮到吞吐高CPU 尽量不闲着整体效率高 时间片不是越小越好切换是有开销的。时间片太小切换太频繁大部分时间都花在切换上真正干活的时间少整体效率反而低。时间片太大大进程占 CPU 太久交互响应慢。Linux 桌面版默认时间片一般是几毫秒到几十毫秒是响应和吞吐的平衡。二、CPU 寄存器与临时数据2.1 寄存器的诞生背景与核心作用CPU 是运算器但运算不能没有地方放中间结果。内存太慢了CPU 运算速度是内存的上百倍如果每次取数存数都跑内存CPU 大部分时间都在等性能浪费严重。于是 CPU 内部集成了寄存器容量极小速度极快和 CPU 同速用来存放当前正在运算的临时数据、指令地址、栈指针等程序运行时当前用到的变量、中间结果、下一步执行哪条指令都存在寄存器里2.2 常见的核心寄存器分类以 x86 架构为例核心寄存器分几类表格寄存器类型代表作用程序计数器PC / EIP存放下一条要执行的指令的内存地址。CPU 照着它取指令执行是程序的 “导航指针”栈指针寄存器SP / ESP指向当前进程的内核栈 / 用户栈栈顶。函数调用、局部变量、返回地址都靠栈指针定位通用寄存器eax/ebx/ecx/edx 等存放运算的中间值、参数、返回值。加减乘除、逻辑运算都用它们状态寄存器EFLAGS存放运算标志位进位、零、符号等、中断开关等状态2.3 核心结论区分 “空间” 与 “内容”这是理解上下文切换最关键的一点寄存器是 “盒子 / 空间”CPU 里就这么几十个寄存器硬件上是固定的所有进程共用这一套寄存器寄存器里的数据是 “内容”每个进程运行时往寄存器里放自己的数据盒子只有一套但每个进程都有自己的 “行李”。切换进程的时候不能把上一个进程的数据直接扔了得先存起来再放下一个进程的。三、进程上下文与切换机制3.1 什么是进程上下文Process Context进程运行时CPU 所有寄存器的值 PCB 里的进程状态、内存映射、打开文件等信息合起来就是进程上下文。通俗说就是进程的「运行现场快照」记录了进程跑到哪了、中间结果是什么、栈在哪、打开了哪些文件。上下文分两类硬件上下文CPU 寄存器的值是切换的核心软件上下文PCB 里的进程状态、内存信息、信号状态等3.2 切换的 “当兵留籍” 故事用军营训练类比一秒懂切换逻辑训练场 CPU每个士兵 一个进程士兵的个人装备、武器 进程上下文储物柜 PCB训练规则每个人训练 10 分钟时间到立刻换人。士兵 A 时间到把自己的装备全部收进自己的储物柜放好士兵 B 过来打开自己的储物柜把装备拿出来穿戴好士兵 B 开始训练从他上次停下的科目继续整个过程A 的装备不会丢B 接着自己上次的进度。进程切换就是完全一样的逻辑时间片到进程 A 把寄存器里的所有数据存回自己的 PCB储物柜调度器选进程 B把 B 的 PCB 里存的上下文恢复到 CPU 寄存器里CPU 从 B 的程序计数器指向的位置继续执行3.3 一次完整的切换流程一次完整的进程切换分四步第一步触发调度时钟中断触发内核发现当前进程时间片用完了。或者进程主动等待 IO比如读磁盘主动让出 CPU。第二步保存当前进程上下文内核把当前 CPU 所有寄存器的值全部保存到当前进程的 task_struct 的上下文字段里。包括程序计数器 PC、栈指针 SP、所有通用寄存器、状态寄存器一个都不能少。保存完这个进程的现场就完整封存了。第三步调度器选择下一个进程调度算法从运行队列里选出下一个该运行的进程。第四步恢复新进程上下文把选中进程的 PCB 里存的上下文全部加载到 CPU 寄存器里。程序计数器 PC 变成新进程的指令地址栈指针变成新进程的栈通用寄存器里是新进程的中间数据。加载完成那一刻CPU 就开始执行新进程的指令就像它从没被打断过一样。3.4 硬件上下文的具体保存与恢复实现上下文切换不是纯软件做的是硬件 软件配合中断硬件自动保存时钟中断发生时CPU 硬件会自动把当前的 PC、状态寄存器等关键寄存器压入当前进程的内核栈。这一步是硬件自动完成的不用内核代码写。内核软件补充保存内核接着把剩下的通用寄存器、浮点寄存器等保存到 PCB 的 thread_struct 里。恢复的时候反过来内核先从 PCB 恢复通用寄存器中断返回时硬件自动从栈里恢复 PC、状态寄存器跳转到新进程继续执行 为什么要硬件软件配合硬件自动做最关键的现场保护保证中断瞬间不会乱软件做剩下的灵活适配不同架构。四、内核源码视角上下文存在哪里4.1 任务状态段与 task_structx86 架构有 TSSTask State Segment任务状态段的机制用来存任务状态。Linux 没有用硬件的多任务 TSS 切换而是每个 CPU 只有一个 TSS内核自己把进程上下文存在task_struct里的thread_struct字段中。struct task_struct { // ... pid、状态、内存等信息 struct thread_struct thread; // 存放硬件上下文 // ... };thread_struct里就是完整的寄存器快照切换的时候直接和 CPU 寄存器交互。4.2 内核中的 current 指针内核里有一个全局指针current永远指向当前正在 CPU 上运行的进程的 task_struct。内核随时知道 “现在是谁在跑”保存上下文的时候直接往 current 指向的 PCB 里存就行。切换完成后current 指针也跟着指向新的进程。五、衍生思考5.1 全新进程 vs 已调度过的进程同样是上 CPU 运行两种进程的上下文不一样全新进程第一次上 CPU上下文是初始化的值。PC 指向程序入口地址栈指针指向栈顶通用寄存器都是初始值。相当于新兵第一次上训练场装备都是新的。已调度过的进程上下文是上次被切走时保存的现场。PC 指向上次执行到的那条指令寄存器里是上次的中间结果。恢复后接着跑就像没被打断过。5.2 分时操作系统与实时操作系统表格类型调度方式特点适用场景分时操作系统时间片轮转公平优先响应快多任务体验好但不保证高优先级任务绝对优先桌面、服务器Linux 属于这一类实时操作系统优先级抢占高优先级任务立刻抢占 CPU延迟极低确定性强但可能饿死低优先级任务工业控制、航天、汽车电子Linux 是典型的分时系统兼顾公平和响应适合服务器和桌面场景。六、上下文切换的开销很多人以为切换是免费的其实切换开销不小这也是为什么不是进程越多越好。开销主要来自两方面直接开销保存恢复寄存器、执行调度代码占用 CPU 时间间接开销CPU 缓存Cache失效。上一个进程的数据还在缓存里新进程过来缓存都是旧的要重新从内存读数据速度慢很多。进程越切换缓存命中率越低性能下降越明显。所以时间片不能太小切换不能太频繁否则大部分时间都耗在切换和缓存失效上真正干活的时间少。全文总结时间片轮转多任务的基础CPU 分成时间片进程轮流使用宏观并行微观串行。寄存器CPU 内部高速存储单元存放当前运行的临时数据所有进程共享一套硬件寄存器。进程上下文进程运行的完整现场核心是 CPU 寄存器的值存在 PCB 的 thread_struct 里。切换流程触发中断 → 保存当前进程上下文到 PCB → 调度选新进程 → 恢复新进程上下文到 CPU → 继续执行。软硬配合硬件自动保存关键寄存器内核软件补充保存协同完成上下文切换。切换开销不仅有直接的保存恢复时间还有缓存失效的间接开销切换频率要平衡。下篇预告调度器怎么从几百个进程里快速选出下一个早期 O (n) 调度有什么问题Linux 经典的 O (1) 调度算法是怎么做到无论多少进程选下一个都是固定时间下一篇我们深入调度算法拆解活跃队列、过期队列、位图优化的设计精髓。
返回列表