CPU 运行模式与特权指令

← 操作系统知识地图 | ← 主页

一个程序崩溃不该拖垮整个系统——CPU 靠”双模式”把普通程序和操作系统隔离开。


CPU 上同时跑着你的浏览器、音乐播放器、操作系统内核。如果浏览器里一行 STOP_CPU 就能把整台机器停掉,那谁还敢多开程序?

所以这一章本质上在解决一个问题:怎么防止一个程序搞坏整个系统?


要解决什么问题

早期的单任务系统没有保护——程序直接操作硬件,写飞一个指针就能把内存里别人的数据踩烂。多道程序并发之后,这个问题更致命:

  • 一个程序把时钟改了,所有依赖时钟的程序全部乱套
  • 一个程序直接写磁盘,绕过文件系统,数据安全形同虚设
  • 一个程序执行停机指令,整个系统挂了

解决思路很朴素:给指令分等级。有些指令太危险,只有操作系统才能动。


怎么解决的:用户态与内核态

CPU 在硬件里加了一个标志位——程序状态字(PSW)里的模式位。这个位决定了 CPU 当前处于什么”身份”:

PSW 本身是 CPU 内部的一个特殊寄存器,属于计算机组成原理的范畴,详见 寄存器 — PSW 章节

  • 用户态(模式位 = 1):普通程序跑在这里,只能执行安全指令
  • 内核态(模式位 = 0):操作系统内核跑在这里,什么指令都能执行

指令分三档

分类指令谁可以用
🟢 普通指令存数、寄存器清零、跳转(JMP / JZ / JNZ)用户态
🟡 陷入指令syscall / int 0x80用户态 → 内核态
🔴 特权指令置时钟、停机(HLT)、屏蔽中断(CLI / STI)只有内核态

判断标准很简单:能影响全局状态、能绕过 OS 管控的,就是特权指令。让用户程序随便停机、关中断,系统就没法管了。

用户程序怎么请求特权服务

用户程序不能直接执行特权指令,但它可以”敲门”——执行陷入指令syscall)(访管制令)。CPU 硬件做三件事:

  1. 保存现场(PC、PSW、通用寄存器压栈)
  2. 模式位切到内核态
  3. 跳到 OS 预设的中断处理程序入口

处理完,OS 执行”返回用户态”指令,CPU 恢复现场,程序继续跑,全程无感。

这就像一个办事窗口:你不能进去翻档案(不能直接执行特权指令),但你可以在窗口提交申请(系统调用),工作人员进库房帮你拿(内核代劳)。


态切换的具体机制和三种中断类型(访管中断、时钟中断、缺页中断)详见 中断与任务协作


再往前一步:从双模式到多级特权

用户态/内核态两级够用吗?对于简单的嵌入式系统够了。但复杂场景下,两层不够细腻。

  • Intel x86:Ring 0 ~ Ring 3 共四级。Ring 0 是内核,Ring 1/2 留给设备驱动(实际很少用),Ring 3 是用户程序。虚拟机监控器(Hypervisor)跑在 Ring -1,比内核权限还高。
  • ARM:EL0(用户程序)→ EL1(内核)→ EL2(Hypervisor)→ EL3(安全监控,TrustZone)。Cortex-M 因为面向嵌入式,只保留了 Handler 模式(类似内核态)和 Thread 模式(类似用户态),简化了特权级设计。

对照 FreeRTOS 看:Cortex-M 上 FreeRTOS 的任务跑在 Thread 模式(非特权),中断和 SVC 调用跑在 Handler 模式(特权)。中断与任务协作 里展开了中断和任务的边界设计。

多级特权的本质没变:每一层都只能管自己该管的事,越权就要往上交


大内核 vs 微内核

特权级决定了怎么隔离,但还没回答另一个问题:内核里到底放什么? 这是操作系统结构设计的核心分歧。

要解决什么

内核态有最高权限,但代价是出了 bug 整个系统挂掉。所以内核里放多少东西,本质上是在性能可靠性之间做取舍。

三种设计

大内核(Monolithic)微内核(Microkernel)外核(Exokernel)
内核里放什么全部——文件系统、网络栈、驱动、调度、内存管理只放必需的——调度、IPC、地址空间管理几乎不放——只做硬件资源的安全复用
其他服务在哪内核态用户态,独立进程用户态,应用直接操作硬件
服务间通信函数调用,快IPC,有开销不需要——内核不提供服务
一个服务崩了整个内核可能挂只挂那个进程只挂那个应用
典型系统Linux、Unix、WindowsQNX、MINIX、L4MIT Exokernel、Nemesis

可以这样理解:大内核是”什么活都自己干”的老板,微内核是”只抓核心、其余外包”的指挥中心,外核是”只当保安、不管业务”——它只负责拦住不该进的人,别的什么都不管。

外核的思路最极端:内核不提供任何抽象。传统 OS 把磁盘抽象成文件、把内存抽象成虚拟地址空间——外核觉得这些都是多余的,应用最清楚自己需要什么。外核只做一件事:安全地把硬件资源(磁盘块、物理页框、CPU 时间片)分配给应用,应用自己决定怎么用。好处是极致灵活和性能,代价是应用要自己实现文件系统、内存管理——开发极其复杂。至今没有商业 OS 采用纯外核。

Linux 为什么选大内核

Linux 内核里塞了文件系统、网络栈、成千上万的驱动——是标准的大内核。但它不是纯血大内核,通过内核模块.ko 文件)把驱动动态加载到内核空间,不用重新编译内核就能扩展功能。严格说 Linux 是大内核 + 模块化,取了微内核”可扩展”的好处,但没有微内核的 IPC 开销。

嵌入式里的实际情况

微内核理论更优雅(隔离好、更安全),但嵌入式里大内核和 RTOS 占绝对主流:

  • Linux(大内核):跑在 Cortex-A 上,要的是生态和驱动
  • FreeRTOS:不分用户态/内核态,所有任务和内核跑在同一特权级,连微内核都不是——它是单体实时内核,一切以”快”和”确定”为优先
  • QNX(微内核):少数嵌入式里跑微内核的例子,用在汽车、医疗等对安全要求极高的场景

所以很多时候不是”大内核 vs 微内核谁更好”的问题,而是你的硬件有没有 MMU、你的场景能不能接受 IPC 延迟。


如果你正在跟随梳理, 返回 MOC←