处理器运行模式
1.1.1 结尾提到一条贯穿全课的规律:操作系统的每一项管理能力,背后都有一个硬件机制在支撑。 这一节讲的就是最根本的那一个。
问题很具体:操作系统凭什么能阻止一个用户程序去关中断、去改页表、去直接读磁盘?如果靠软件逐条检查,那检查代码本身也在 CPU 上跑,用户程序同样可以绕过。 唯一可行的办法是把这件事交给硬件。
机制
两种运行模式
CPU 划分出两种运行状态:
用户态(目态)——权限低,只能执行非特权指令,不能直接访问关键硬件和内核数据。应用程序运行在这个状态。
内核态(核心态、管态)——权限高,可以执行全部指令,能管理硬件和系统资源。操作系统内核运行在这个状态。
当前处于哪种状态,由程序状态字寄存器(PSW)中的一个二进制位标志。 CPU 在执行每一条指令时都会检查这一位——这就是”硬件支撑”的确切位置。
特权指令与非特权指令
特权指令是只允许在内核态执行的指令,典型的有:关中断、置时钟、清内存、修改存储器管理寄存器、送程序状态字到 PSW、I/O 指令。
它们的共同点是:一旦被用户程序执行,就能破坏整个系统。 关了中断,时钟中断进不来,调度就永远轮不到别人,这个程序可以独占 CPU 到关机;改了存储器管理寄存器,就能访问任意进程的内存。
非特权指令是普通的算术运算、访存等指令,两种状态下都能执行。
疑问点:特权指令错由谁检查,开销是否很大
之前说的”特权指令错”可能是运行了内核态的指令,那是谁在检查?是操作系统吗? 那会是一笔很大的开销吗?
特权指令错是怎样被发现的
这一点最容易被误解,也是理解本节的关键。
操作系统并没有逐条扫描用户程序去找特权指令——那既做不到(程序可以在运行时动态生成指令),也太慢。
实际发生的是:CPU 执行到一条特权指令时,硬件检查 PSW 中的模式位,发现当前是用户态,于是拒绝执行并触发一个异常(“特权指令错”),控制权随即转入内核。
换句话说,检查是”执行时由硬件做的”,不是”事先由软件做的”。 操作系统所做的,只是在系统启动时把规则配置好(配置中断向量表、页表权限等),之后就交给硬件自动执行。
因此这项检查几乎没有额外开销。 它不是一段需要执行的代码,而是与指令译码同时进行的一次硬件比较——CPU 本来就要译码判断这是什么指令,顺便看一眼模式位即可。“开销大”的担心,来自把它想象成软件检查;一旦认清是硬件做的,这个担心就消失了。
顺带可以回答一个常见的实践疑问:在早期的微机接口实验中能直接使用关中断指令,并不是因为那段程序”运行在内核态”,而是因为那种环境根本没有操作系统在管理特权级——实模式下 CPU 不作特权检查,所有指令都能执行。特权级机制要起作用,前提是有人把规则配置好并让 CPU 运行在保护模式下。
疑问点:受控入口如何被"标记"
CPU 检测某处是受控入口——它怎么检测,怎么”标记”受控入口,是操作系统启动时挂载上去的吗?
用户态如何进入内核态:三种受控入口
用户程序不能随便跳到内核的任意位置——否则它只要跳到某个跳过权限检查的地方,保护就形同虚设。
能进入内核的入口必须是操作系统预先配置给 CPU 的受控入口,共三种触发方式:
| 入口 | 触发原因 | 例子 |
|---|---|---|
| 系统调用 | 用户程序主动请求操作系统服务 | read()、write()、fork() |
| 中断 | 外部事件打断 CPU | 时钟中断、I/O 完成中断 |
| 异常 | 当前指令执行出现问题 | 缺页、除零、非法指令、特权指令错 |
这些入口的地址由操作系统在启动时写入硬件的中断向量表(如 x86 的中断描述符表 IDT),之后 CPU 只承认这张表里的入口。用户程序无法伪造”我直接跳进内核某个函数”这种事——它连跳过去的地址都没有合法途径写进去。
三者的最短判据:系统调用是主动请求,中断来自外部,异常来自当前指令。
内核态如何返回用户态
回到用户态靠的是一条特权指令——通常是”中断返回”类指令,它在恢复现场的同时把 PSW 中的模式位改回用户态。
注意方向的不对称性:进入内核态是”被动”的(要么被中断/异常打断,要么执行陷入指令),返回用户态是”主动”的(内核执行一条特权指令主动交还控制权)。这个不对称正是保护得以成立的原因——如果用户程序能主动切到内核态,一切保护都没有意义。
操作系统内核包含什么
内核是操作系统中最基本、最核心的部分,运行在内核态,包含四部分:
① 时钟管理——提供时间标准,时钟中断是进程切换的基础(时间片用完靠它来触发)。
② 中断机制——负责响应和处理各类中断,是本节所有受控入口的实现基础。
③ 原语——处于操作系统最底层、最接近硬件的部分。它的特点是:运行具有原子性(操作只能一气呵成,不可中断)、运行时间较短、调用频繁。典型的有设备驱动、CPU 切换等。
④ 系统控制的数据结构及处理——进程管理、存储器管理、设备管理所用的各种数据结构(如 PCB、页表、设备控制表)及对它们的操作。
边界
疑问点:用户态与内核态是否只是一个标志字段
用户态和内核态指的只是一个标志(字段)吗?谁在管理它?有硬件参与吗?
用户态与内核态不是操作系统维护的一个变量
这是最容易犯的理解错误。
如果模式位只是操作系统在内存里维护的一个布尔变量,那么用户程序只要把它改成”内核态”就能提权——保护立刻崩塌。
实际上模式位在 PSW 寄存器里,而”修改 PSW”本身就是一条特权指令。 于是形成一个闭环:想改模式位,先得处于内核态;想处于内核态,只能通过受控入口进去。 用户程序从任何一个方向都进不来。
理解这个闭环,就理解了操作系统保护机制的全部原理。
模式切换不等于进程切换
这一条与 2.2.4 的结论一致,此处从运行模式的角度再说一遍:
模式切换只说明 CPU 从用户态进入了内核态(或反之),进程可能一点没变。
| 场景 | 是否进入内核态 | 是否一定切换进程 |
|---|---|---|
| 系统调用 | 是 | 不一定 |
| 中断 | 是 | 不一定 |
| 异常 | 是 | 不一定 |
| 时间片用完 | 是 | 通常会 |
| 普通函数调用 | 否 | 否 |
一句话:所有进程切换都要经过内核态,但进入内核态不一定切换进程。
一个进程调用 getpid(),进内核、取个数、回用户态,全程都是它自己在跑——发生了两次模式切换,零次进程切换。
调度发生在返回用户态之前
这是一个容易记反的时序细节。
内核在准备返回用户态之前,会检查是否需要重新调度。 若当前进程仍可运行且无需让出 CPU,就返回同一个进程;若时间片已用完、或有更合适的进程就绪,内核会先完成进程切换,再返回(另一个进程的)用户态。
准确说法是”返回用户态前检查调度”,而不是”返回用户态后开始调度”——后者在逻辑上不可能,因为一旦回到用户态,控制权已经不在内核手里了。
调度发生在内核态,常见时机是:中断处理结束时、系统调用返回用户态前、进程阻塞或被唤醒后。
特权指令与”只能在用户态执行的指令”
绝大多数指令要么是特权指令(只能内核态),要么两种模式都能执行。
但有一条例外:陷入指令(访管指令)是唯一一个只能在用户态执行、而不可在内核态执行的指令。
理由很直白:陷入指令的作用是”从用户态进入内核态”。已经在内核态了还执行它,语义上没有意义。 这一条是选择题的常见落点。
对照速查
| 用户态(目态) | 内核态(核心态、管态) | |
|---|---|---|
| 权限 | 低 | 高 |
| 能执行 | 仅非特权指令 | 全部指令 |
| 谁在跑 | 应用程序 | 操作系统内核 |
| 标志位置 | PSW 中的一位 | 同左 |
| 特权指令举例 | 为什么必须特权 |
|---|---|
| 关中断 | 关了就能独占 CPU 到关机 |
| 置时钟 | 篡改时间基准 |
| 修改存储器管理寄存器 | 能访问任意进程的内存 |
| 送程序状态字到 PSW | 能直接改模式位(保护闭环的关键) |
| I/O 指令 | 能绕过一切设备保护 |
| 三种受控入口 | 触发者 | 判据 |
|---|---|---|
| 系统调用 | 用户程序主动 | 主动请求 |
| 中断 | 外部设备 | 来自外部 |
| 异常 | 当前指令 | 来自当前指令 |
| 内核的四部分 | 说明 |
|---|---|
| 时钟管理 | 提供时间标准,是进程切换的基础 |
| 中断机制 | 所有受控入口的实现基础 |
| 原语 | 最底层、原子性、时间短、调用频繁 |
| 系统控制的数据结构及处理 | PCB、页表、设备控制表等 |
考点
- 模式位在 PSW 中,由硬件在执行每条指令时检查——不是软件事先扫描
- 保护闭环:改模式位需内核态,进内核态只能走受控入口
- 三种受控入口:系统调用(主动)、中断(外部)、异常(当前指令)
- 入口地址由操作系统启动时配置进中断向量表,用户程序无法伪造
- 所有进程切换都要经过内核态,但进入内核态不一定切换进程
- 调度在返回用户态之前进行,不是之后
- 原语的四个特点:最底层、原子性、时间短、调用频繁
- 陷入指令是唯一只能在用户态执行的指令
链接
- 🏠 返回总览:操作系统第 1 章:计算机系统概述总览
- ⬅️ 上一节:1.2 操作系统发展历程
- ➡️ 下一节:1.3.2 中断和异常的概念
- 🔗 模式切换与进程切换的完整对照,见 2.2.4 进程切换
- 📜 原始提问档案:第 1 章 原始提问档案(本地资料)
- 📖 名词库:第 1 章名词库