Appearance
中断、异常与系统调用
CPU 不会永远顺序执行某个用户程序。设备完成 I/O、时钟到期、访问缺失页面、执行除零指令,或程序主动请求内核服务时,处理器都需要暂时转移到受控入口处理事件。面试中要区分:外部中断是异步硬件事件,异常由当前指令同步触发,系统调用是程序主动发起的受控特权切换。
三类事件
| 类型 | 来源与时机 | 例子 | 处理后通常如何继续 |
|---|---|---|---|
| 外部中断(hardware interrupt / IRQ) | CPU 外部设备异步发出,通常在指令边界响应 | 网卡收包、磁盘完成、定时器 | 返回被打断的任务,或触发调度切换 |
| 异常(exception) | 当前指令执行时同步产生 | 缺页、除零、非法指令、断点 | 可修复则重试指令;不可修复则报告/终止任务 |
| 系统调用(system call) | 用户程序主动执行受控入口指令 | read、write、open | 内核完成服务后返回调用点的下一条指令 |
“中断”在不同教材中有广义和狭义用法:广义上可统称这些控制流转移;严格讨论硬件时,interrupt 往往专指外部 IRQ。不要把 Unix 的**信号(signal)**当成 CPU 硬件中断:信号是内核向进程递送的异步软件通知。
从事件到处理程序
以 x86 为例,处理器根据向量号查询 IDT(中断描述符表)中的入口。进入内核时,硬件会保存部分状态;内核入口代码再保存其余可能被破坏的寄存器,建立适合 C/Rust 等内核代码运行的上下文。处理完毕后,通过架构规定的返回指令恢复现场。
这里有两个容易混淆的边界:
- 进入内核态不等于上下文切换:一个
getpid系统调用可能进入并离开内核,仍继续原线程; - 上下文切换不等于复制进程资源:切换主要保存/恢复寄存器、栈、调度状态;切到其他进程时还切换地址空间。详见 进程、线程与调度。
外部中断:设备如何通知 CPU
设备通过中断控制器(现代 x86 通常是 APIC 体系)向 CPU 报告事件。CPU 响应后,内核应尽快确认/应答设备并记录事件;对耗时工作通常拆为两段:
text
硬中断上半部:确认设备、搬运少量必要状态、安排后续工作,尽快返回
↓
软中断 / tasklet / workqueue / 内核线程:处理协议栈、批量数据或可能耗时的工作术语和具体实现随内核版本、设备驱动及架构变化,但工程原则稳定:硬中断上下文应短小,不能执行可能睡眠的操作。因此硬中断路径通常不能直接使用会阻塞的 mutex、执行磁盘 I/O 或等待条件变量;需要保护极短共享状态时才考虑自旋锁,较重工作应延后。
频繁中断也会产生中断风暴和 CPU 抖动。网卡常通过 NAPI 等轮询/中断混合机制在高负载下降低每包中断成本;服务器排障可关注 softirq、IRQ 亲和性、队列和 CPU 利用率,而非只看应用线程。
同步异常:Fault、Trap 与 Abort
不同 ISA 的精确命名略有差异,以下是常见的理解框架:
| 类别 | 含义 | 例子 | 返回位置 |
|---|---|---|---|
| Fault(故障) | 当前指令尚可修复 | 合法地址的缺页、某些调试 fault | 修复后通常重试当前指令 |
| Trap(陷阱) | 有意或可报告的同步事件 | 断点、单步调试;历史上的软件中断入口 | 通常在下一条指令处继续或交给调试器 |
| Abort(终止) | 严重且通常不可恢复 | 机器检查、严重硬件错误 | 不返回原指令,终止任务或进入内核故障路径 |
缺页为什么不是“程序一定出错”
访问一个虚拟页时,若页表项暂未驻留物理页,CPU 触发 page fault。内核检查该地址是否属于合法映射:
- 合法的按需分配、文件映射或 swap 页面:分配/回收页框或从后备存储载入,更新页表,然后重试原指令;
- 地址不存在、权限不允许或无法满足映射:向进程报告错误,例如
SIGSEGV或SIGBUS。
所以 page fault 是“地址转换/权限路径需要内核介入”的事件;只有其中不能修复的情形才表现为程序崩溃。页表与缺页链路见 虚拟内存与页面置换。
系统调用:主动进入内核
系统调用是用户程序请求内核服务的 ABI,不是“直接调用内核 C 函数”。调用方按 ABI 把系统调用号和参数放入约定寄存器/内存,然后执行架构定义的受控入口指令;内核校验参数、权限和用户地址后执行服务,最后返回结果或错误码。
- 32 位 x86 Linux 历史上常通过
int 0x80软件中断门进入; - 64 位 x86 Linux 通常使用专用的
syscall/sysret快速路径; - vDSO 可以把部分只读时间查询等能力映射到用户空间,避免每次都陷入内核;
- 库函数不一定等于一次系统调用:
printf可能先写用户态缓冲,malloc主要是运行库分配器,见 虚拟内存与页面置换。
syscall 与 int 0x80 的进入机制和性能取舍见 进程、线程与调度。
中断、异常、信号与调度的关系
text
网卡收包 ───────→ 外部中断 → 内核网络处理 → 唤醒等待 socket 的线程
时钟到期 ───────→ 外部中断 → 更新时钟/调度器 → 可能抢占当前线程
访问未驻留页面 ─→ Fault → 载入/映射页面 → 重试该内存指令
read() ─────────→ 系统调用 → 内核检查 socket → 有数据则返回;否则线程可睡眠
kill(pid, SIGTERM) → 内核信号递送 → 目标进程在合适时机处理默认动作/处理函数事件本身不必然导致任务切换:硬中断处理结束可直接恢复原任务;系统调用也可能立即返回。只有当前任务阻塞、时间片耗尽、被抢占或调度器发现更合适的可运行任务时,才会发生线程/进程上下文切换。
面试速答
外部中断来自网卡、磁盘、时钟等硬件,是异步事件;异常由当前指令同步触发,例如缺页、除零和非法指令;系统调用则是用户程序主动通过受控 ABI 请求内核服务。CPU 根据向量进入内核处理程序,保存现场,必要时切到内核栈;处理完后要么返回原线程,要么由调度器切换任务。缺页是可修复 fault:合法页载入并更新页表后重试原指令;非法访问才会产生段错误。硬中断路径必须短且不能睡眠,耗时工作通常延后到 softirq 或工作队列。
常见误区
- 把“用户态 → 内核态切换”说成必然发生进程切换;
- 把所有 page fault 说成段错误;
- 把 Unix signal、软件中断和外部硬件 IRQ 当成同一个概念;
- 说
int 0x80经过外部中断控制器:它是 CPU 执行的同步软件中断,不经过 PIC/APIC; - 在硬中断上下文中等待锁、睡眠或执行长时间工作;
- 把
malloc、printf等库函数机械等同为“一次系统调用”。