让慢设备不拖死快 CPU。三种交互方式(轮询/中断/DMA)= 谁来盯着设备;再串总线、外设接口、中断流程。
| 轮询 Polling | 中断 Interrupt | DMA | |
|---|---|---|---|
| 谁主动 | CPU 反复查状态寄存器 | 设备就绪后「打断」CPU | DMA 控制器搬数据,CPU 只管起停 |
| CPU 代价 | 忙等,全程占用 | 只在响应时打断,可干别的 | 几乎不参与,最后收中断 |
| 延迟 | 取决于轮询频率 | 快,但有中断开销 | 搬完才通知 |
| 适用 | 设备极快、或极低频 | 事件驱动、通用 | 大批量块传输(磁盘/网卡) |
对比:核心权衡是「CPU 空转 vs 响应开销」。轮询适合设备永远瞬间就绪(省上下文切换);中断适合偶发事件;DMA 把「设备→内存」这块最累的活外包给 DMA 控制器。
轮询: CPU ──查状态──▶ 设备 …… CPU 空转等
中断: 设备 ──就绪,发中断──▶ CPU 打断当前任务去处理
DMA: 设备 ──DMA控制器──▶ 内存(数据直搬),完成后再中断 CPU
总线是 CPU、内存、外设间的公共通道,分三类信号:
总线仲裁:多个设备抢总线时由仲裁器按优先级/轮转决定谁能用——否则两设备同时驱动会总线冲突。
注意:早期单总线共享,慢设备拖慢快设备(木桶效应);现代用多级总线/点对点(PCIe 点对点串行),高速设备走专属通道。「总线」如今更像历史概念——PCIe 用交换式点对点取代共享总线,但地址/数据/控制的抽象仍在。
CPU 与外设交互的最小接口是设备寄存器(状态/数据/命令寄存器)。两种编址:
| 方式 | 做法 | 特点 |
|---|---|---|
| 端口映射 I/O | 独立 I/O 地址空间,专用 in/out 指令 | x86 传统 |
| 内存映射 I/O(MMIO) | 外设寄存器映射进内存地址空间,用普通访存指令 | RISC 主流,统一 |
对比:MMIO 把「访问外设」变「读写某段内存地址」,无需专门指令、能走 Cache 机制——但外设寄存器通常禁用 Cache(设备状态自己会变,缓存了读旧值),正是第 7 章「MMIO 强制写直达」的原因。
设备就绪 → 发中断请求(IRQ)→ CPU 在当前指令边界响应:
① 设备拉高中断请求线
② CPU 完成当前指令,检查是否有中断且未屏蔽
③ 关中断,保存现场(PC、寄存器压入内核栈)
④ 根据中断向量/原因,跳转到中断服务程序(ISR)
⑤ ISR 处理(读状态、取数据、清中断)
⑥ 恢复现场,开中断,返回被中断的程序
对比:中断/异常/系统调用用同一套「打断+保存现场+向量跳转」,来源不同:中断来自外部设备(异步);异常来自指令执行出错(同步:除零、缺页、段错误);系统调用是程序主动请求(
int 0x80/syscall)。第 8 章缺页即「异常」。
read()/recv() 系统调用,内核走中断+DMA;asyncio、NIO 的非阻塞 I/O 本质是把等待从「阻塞线程」改成「事件通知」。