本节摘要:门禁体系的主体工程。本节讲特权级如何划分两个世界、特权指令为何对普通程序关死大门、以及系统调用、中断、异常三条进出通道的完整礼仪。6.1 节的缺页异常旅程在此抵达终点。读完你应当能徒手画出一次系统调用的全链路,并说清"硬件收回控制权"的全部触发方式。
特权模型给处理器的当前状态标一个等级:用户态(低特权)跑应用,内核态(高特权)跑操作系统内核。等级不是装饰——它接在每条指令与每次访存的门口做检查:I/O 端口操作、页表指针改写、中断开关(如 x86 的 cli 与 sti)、控制寄存器读写,这些特权指令在用户态执行直接触发非法指令异常,压根到不了功能电路。等级还叠加在 6.4 节的页权限上:内核页标记为"仅特权可访问",用户态碰都不用碰就被拒绝。
各家对等级的切法不同但同构。x86 用四个环(ring 0 到 3),主流系统只用 0 与 3 两环;ARM 用异常级别 EL0 到 EL3(应用、内核、虚拟机监控器、安全监控各占一级);RISC-V 用 M、S、U 三级(机器、监管、用户)。层数差异反映的是各家对虚拟化与安全监控的态度——多出的层就是 7.3 与 7.4 节的主角们的座席。
**为什么硬件必须强行分层?**纯软件的分权挡不住 bug 与攻击:应用一旦能直接关中断或改页表,操作系统的全部管理假设瞬间作废。硬件分层是"操作系统可能被绕过"这一假设的物理兜底——所有多进程系统、所有安全模型,地基都是这条指令级的等级线。
用户态进入内核态只有受控的门。第一条是主动借道——系统调用。应用把调用号与参数放好,执行一条专用陷入指令:x86-64 的 syscall(编码 0F 05,前身的 32 位时代是 int 0x80,编码 CD 80),RISC-V 的 ecall(编码 0x00000073)。硬件随即完成一组固定动作:切换到内核态、跳到入口向量表里登记的固定地址、换用内核栈。接着内核按调用号查分发表、保存现场、执行服务、把返回值放回约定寄存器,最后执行返回指令(sysret 或 sret)切回用户态、恢复现场、从陷入指令的下一条继续。全程应用没有任何选择权——能选的只有调用号,其余全是硬件与内核的剧本。
第二条是被动接管——中断与异常。两者的区别要分清:中断是外部事件(网卡来包、时钟到点)异步打上门来,与当前指令无关;异常是当前指令的同步产物——缺页(6.1 节)、除零(4.2 节)、非法指令(包括用户态执行特权指令)、断点调试。另有陷阱一类是程序故意为之(调试断点)。无论哪一种,硬件的接管动作与系统调用同构:切特权级、按向量表跳转、保存现场。中断与异常的最大工程差别在于可预测性:中断随时可能来,入口延迟是实时系统的生命线(6.4 节的 MPU 模型为此而存在);异常与指令严格同步,位置可精确复现。

通道的验收标准是"现场完好":内核办完事,用户程序必须能从原地原状态继续。为此上下文保存有一套严格礼仪——硬件自动保存最少必需项(出错地址、异常原因、部分标志),其余寄存器由软件按约定保存:切换到内核栈(避免踩用户的栈)、压住全部通用寄存器、保存浮点状态(按需,省的是切换开销)。5.4 节的精确异常在这里接受检阅:乱序核心里同时可能有几十条在途指令,异常来时硬件必须把"之前的全部生效、之后的全部作废"整理干净,内核才能理直气壮地假设现场一致。虚拟化(7.3 节)对精确性要求更苛刻——监控器要截留异常另作安排,现场不精确就没有虚拟化。
第一个坑:低估系统调用的开销。一次往返数百个周期起步(模式切换、栈切换、缓存与 TLB 的局部性破坏都在计费),高频小调用是性能杀手——标准库用缓冲与批量合并调用,高频只读数据走免陷入通道(vDSO 类机制把时钟读取这类只读服务映射进用户态)。第二个坑:混淆异常的"同步"与中断的"异步"带来的调试差异。异常可单步复现(同样输入必在同一指令触发),中断不可(与外部时机耦合)——把中断处理里的 bug 拿到单步调试器里复现,本身就是方法论错误,要用日志与计数器。第三个坑:在内联汇编里乱开大门。应用代码里手工执行特权指令或裸调陷入编号,绕过标准库的系统调用封装,会在 libc 版本或平台差异前碎裂——陷入编号与寄存器约定是内核 ABI,属平台契约,别自行发明。
问:为什么系统调用不能像普通函数调用那样快?普通函数调用只动寄存器与用户栈,系统调用要跨特权级:切换 CPU 模式、切换到内核栈、硬件压入返回现场、按向量表跳转——每一项都是固定开销,数百周期起步。此外调用边界会打断分支预测与缓存的局部性(内核代码路径是另一套指令流),隐藏成本比明面更多。这就是"能不陷入就不陷入"的工程哲学的由来:批量合并、缓冲、只读数据映射进用户态,全是在给陷入次数做减法。
问:中断到来时如果 CPU 正在内核态执行,会发生什么?同样接管,但处理方式按被抢占代码的身份有讲究:若被打断的是低优先级内核代码,处理完直接返回;若打断的是更关键的中断处理自身,就涉及中断嵌套与优先级仲裁;若被抢占的是持锁临界区,内核还要标注"从中断返回时再处理"的待办。这些策略属于操作系统的调度章法,但它们的可行性地基都是 7.1 的通道礼仪——硬件保证每次接管现场完好,软件才敢谈优先级。
问:两个世界的数据怎么安全传递?靠约定寄存器与受检拷贝。调用号与参数放在约定寄存器传入,内核不信任任何指针——用户传入的缓冲区地址要逐个校验(确属用户态映射、长度合法、没有指向内核区),拷贝进出都用专用例程(顺手处理缺页)。历史上大量内核漏洞正是漏了这层校验——门禁通道也是攻击面的第一现场。