前言之前我们已经谈到了说操作系统要实现CPU的时分共享也就是多进程的同时运行就必须解决几个关键问题1.性能方面如何让进程运行得尽可能快而又不引入过多开销2.控制权如何在进程运行时操作系统仍能回收CPU、管理资源防止进程越权有两种极端的方案第一种时纯模拟执行让OS逐条模拟指令使其控制权达到100%但这样一来CPU的利用就极少性能很差另一种是直接让进程在CPU上跑性能完全可以达到最优但是操作系统会完全失控所以为了解决这个问题我们引入一种新的平衡两者的方案受限直接执行LDE6.1基本技巧受限直接执行在提到LDE之前我们先来看看第二种方案——直接执行的核心逻辑OS为进程先做准备工作比如在进程列表中创建条目、为进程分配内存、根据argc/argv初始化内存、清楚寄存器执行call main()命令跳转到程序入口程序直接在CPU上运行执行main()函数知道从main()中返回OS收尾释放进程内存将进程从列表中删除这个流程的优势非常明显——快因为程序直接操作CPU没有中间层开销。但你会发现两个问题这也是我们接下来要说到的受限机制要解决的核心如何限制程序的操作不能让它直接I/O、修改其他进程内存如何让OS重新获得CPU控制权比如进程死循环时实现进程切换6.2 问题一受限制的操作事实上这种“受限”的实现依赖于硬件提供的两种CPU执行模式这是所有限制的基础第一种就是用户模式也是进程运行时的默认模式· 权限限制不能执行 “特权指令”比如发起 I/O、修改页表、关闭中断、访问内核内存· 违规后果如果进程尝试执行特权指令CPU 会直接触发 “异常Exception”OS 会接管并终止该进程避免影响系统。第二种是内核模式OS运行的模式权限无任何限制可执行所有指令、访问所有硬件和内存、管理所有资源。那么为了正常保证程序运行一个进程要如何执行特权操作呢——当然是通过系统调用与陷阱机制来的。你也看到了用户进程如果想要执行特权操作像什么读取文件啊、创建进程啊这些都是不可以直接进行操作的必须通过操作系统提供的唯一合法通道——系统调用接下来我们就来看看这个完整的线路是什么步骤 1进程发起系统调用进程通过调用 C 库函数如read()、open()发起请求库函数底层会执行一条陷阱指令Trap Instruction。书里补充了一个关键细节库函数会先按照与内核约定的规则将系统调用号、参数放入指定寄存器或栈中比如 x86 架构的特定寄存器再执行陷阱指令 —— 这是为了让内核能正确识别 “进程要做什么”。步骤 2硬件触发陷阱切换到内核模式陷阱指令执行后硬件会自动完成 3 件核心工作书里强调这是硬件强制行为无法被进程篡改保存当前进程的上下文通用寄存器、程序计数器 PC、状态标志到该进程的 “内核栈Kernel Stack”—— 确保之后能恢复进程执行。将 CPU 模式从 “用户模式” 切换到 “内核模式”—— 解锁特权操作权限。根据 “陷阱表Trap Table”跳转到对应的内核处理函数系统调用例程—— 进程无法指定跳转地址只能按内核预设的路径执行。步骤 3内核执行特权操作内核找到对应的系统调用处理函数先做权限检查比如读文件前检查是否有访问权限然后执行特权操作如向磁盘发送 I/O 请求、分配内存。书里特别提醒内核执行时使用的是进程的内核栈而非用户栈 —— 这是为了隔离用户态和内核态的数据避免进程篡改内核执行逻辑。步骤 4从陷阱返回用户态特权操作完成后内核执行 “从陷阱返回” 指令硬件再次自动完成 3 件事从进程的内核栈中恢复之前保存的上下文寄存器、PC。将 CPU 模式从 “内核模式” 切换回 “用户模式”—— 重新限制进程权限。跳回进程中 “陷阱指令的下一条指令”让进程继续执行。这里面我们涉及到了一个概念——陷阱表内核在启动时会初始化陷阱表记录每一种异常时间系统调用、除零错误、非法指令对应的内核处理函数地址。然后内核通过特权指令将陷阱表的地址告诉硬件硬件会永久保存这个地址直到系统重启——用户进程无法修改陷阱表也无法指定陷阱除法后的跳转地址。他的核心价值就是强制进程通过内核预设的“合法入口”进入内核确保所有特权操作都经过内核的检查和控制另外设置陷阱表本身也是特权操作如果进程在用户模式下尝试执行会触发异常被内核终止这也进一步确保了陷阱表的安全性。6.3问题2在进程之间切换刚才我们解决了受限操作的问题但受限直接执行还面临一个核心难题操作系统怎么在进程运行时重新夺回CPU控制权从而实现进程切换毕竟进程直接在CPU上运行时操作系统时不不运行的——如果OS不跑怎么指挥切换呢我们举一个例子吧根据我们之前所学的知识当进程A在CPU上执行的时候OS的代码没有运行他现在应该还是一个闲置的状态。但是你会知道进程A如果陷入了一个死循环那OS就永远没机会执行了更没办法切换到进程B。这该怎么办呢其实书中介绍了操作系统发展过程中两种核心思路1. 早期方案协作式调度依赖进程自觉核心逻辑OS 相信所有进程都是 “懂事的”—— 运行一段时间后会主动放弃 CPU让 OS 有机会切换其他进程。进程如何主动放弃 CPU方式 1执行系统调用如 read ()、open ()时自然会陷入 OSOS 处理完调用后就可以选择切换进程方式 2显式调用yield()系统调用 —— 这个调用啥也不做只负责把控制权交还给 OS方式 3进程出错如除零、非法内存访问—— 会触发陷阱OS 接管 CPU 后可选择切换进程。书里明确指出的缺点致命问题如果进程 “不配合”比如恶意程序、有 bug 的程序进入无限循环从不执行系统调用OS 会永远无法夺回 CPU只能重启机器 —— 这在现代系统中完全不可接受。2. 现代方案非协作式调度OS 强制夺权核心逻辑不依赖进程自觉而是通过硬件时钟中断强制让 OS 重新获得 CPU 控制权 —— 这是现代操作系统的标准方案。关键硬件支持时钟中断Timer Interrupt什么是时钟中断OS 启动时会通过 “特权指令” 设置一个硬件定时器时钟设备让它每隔固定时间比如 10ms就触发一次 “中断”中断的效果无论当前进程在执行什么指令CPU 都会立刻暂停它自动切换到内核模式然后跳转到 OS 预先设置的 “时钟中断处理程序”属于 OS 代码核心价值哪怕进程在死循环时钟中断也能强制 “打断” 它让 OS 重新运行 —— 这就解决了 “OS 不运行无法切换” 的矛盾。书里强调的细节设置时钟中断是 “特权操作”用户进程不能修改定时器否则会破坏 OS 的控制权中断时的硬件行为和系统调用的陷阱类似CPU 会自动保存当前进程的上下文寄存器、PC到进程的内核栈再切换到内核模式 —— 确保之后能恢复进程执行。OS在通过时钟中断夺回CPU后接下来要做的就是切换进程核心就是“保存旧进程状态加载新进程状态”我们将其称之为“上下文切换”。具体的定义OS停止当前运行的进程并启动另一个就绪进程的过程。本质是“进程状态的保存与恢复”第一步硬件自动保存时钟中断时当时钟中断触发CPU 会自动把进程 A 的用户态上下文通用寄存器、程序计数器 PC、状态标志保存到进程 A 的 “内核栈” 中然后 CPU 切换到内核模式跳转到 OS 的时钟中断处理程序。第二步OS 手动保存与加载软件层面OS 在中断处理程序中会进一步保存进程 A 的 “内核态上下文”比如 OS 执行中断处理时用到的寄存器到进程 A 的 “进程控制块PCB” 中 ——PCB 是 OS 记录进程状态的数据结构每个进程都有一个OS 的调度程序Scheduler根据策略选择下一个要运行的进程比如进程 BOS 从进程 B 的 PCB 中恢复进程 B 的 “内核态上下文” 到 CPU 寄存器OS 执行 “从陷阱返回” 指令硬件会自动从进程 B 的内核栈中恢复 “用户态上下文”CPU 切换回用户模式跳转到进程 B 的 PC 指针指向的指令 —— 进程 B 开始运行切换完成。总结至此受限直接执行的整套机制就完整了它既保留了直接执行的高性能又通过用户态 /内核态、系统调用与陷阱、时钟中断与上下文切换把权限控制和 CPU 调度牢牢收回到操作系统手中。可以说LDE 是现代操作系统实现多进程并发最基础、最核心的底层方案既让进程 “跑得飞快”又让操作系统 “管得住、切得动”在性能与安全、效率与控制之间给出了一个优雅而实用的平衡解。理解了受限直接执行也就真正摸到了操作系统进程管理的本质。