07 输入输出系统

📚 本文是 计算机组成原理 的第 7 篇,相关系列见 基础与理论

硬件速览里埋了两个伏笔:"键盘按一下,CPU 怎么知道?"(中断)和"搬大块数据还要劳烦 CPU 吗?"(DMA)。本章一次回收——这也是计组的收官篇:CPU 和外设的三种相处方式,从"死等"到"有事叫我"到"快递员直接送货"。

OS 视角的 I/O 软件栈(缓冲、驱动、设备分配)在 I/O 管理,本篇聚焦硬件层机制

一、接口与端口:CPU 怎么"摸到"设备

外设通过接口(I/O Controller)挂到总线,接口里有一排寄存器,即端口(port):数据端口、状态端口、控制端口。

CPU 访问端口有两种编址:

维度 统一编址(存储器映射 MMIO) 独立编址(专用 I/O 指令)
地址空间 端口占用主存地址空间的一段 端口独立于主存,另有小地址空间
访问方式 普通访存指令(mov 直接读写设备) 专门指令(x86 的 IN/OUT
优点 指令丰富(所有访存指令都能操作设备)、无需额外控制线 地址空间不占主存、指令短、程序意图清晰(看到 IN/OUT 就知道在摸设备)
缺点 主存空间被"吃"一块;地址译码要区分内存和设备 需专门的 I/O 指令和控制线(多一根 IO/M̅ 信号)
代表 ARM、RISC-V(MMIO 为主) x86(同时支持两种)

💡 GPU 显存(显卡)就是 MMIO 的活例子:cudaMalloc 返回的"显存指针",其实是主存地址空间里映射的一段——CPU 用普通访存指令就能摸到它。

二、程序查询方式:最笨也最直白

loop:  读状态端口 → 忙?
       忙 → 跳回 loop 死等
       闲 → 读/写数据端口
  • CPU 100% 轮询等待,设备慢时全在空转
  • 优点:接口简单、无额外硬件
  • ⚠️ 效率极低——"CPU 等一行字符输入"就是这种模式的绝罚。面试里"忙等待(busy waiting)"的反面教材

三、中断方式:有事叫我

全流程(408 必背)

设备准备好 → 接口发中断请求(INTR 线)→ CPU 每条指令执行结束前查一次请求 → 允许且开中断则响应

  1. 关中断(保护现场期间不再被打断)
  2. 保存断点:PC、PSW 压栈(有的硬件自动存,有的靠中断程序)
  3. 引出中断服务程序:送中断向量(中断类型号 × 向量长度 → 向量表找入口),PC ← 服务程序入口
  4. 保存现场:通用寄存器压栈(软件干)
  5. 执行中断服务程序本体(如从键盘缓冲区取字符)
  6. 恢复现场开中断中断返回(IRET:弹回断点)

⚠️ 1、2 步是中断隐指令——由硬件自动完成,不在任何指令集里,却真实存在。408 选择题爱考"哪一步属于隐指令":关中断、保存断点、引出服务程序这三件。

中断分类一张图

中断(CPU 外部事件,异步)
├── 外中断(狭义的"中断")
│    ├── 可屏蔽(INTR 线:键盘、打印机…PSW 的 IF 位可挡)
│    └── 不可屏蔽(NMI 线:掉电、内存校验错…必须响应)
└── 内中断(异常,CPU 执行指令时自己产生的,同步)
     ├── 故障 Fault(缺页、除 0——可修复,修复后重执行)
     ├── 自陷 Trap(系统调用、断点——**故意**的,[[01-操作系统概述|OS 概述]]的用户态入口)
     └── 终止 Abort(硬件错误——程序已救不了)

⚠️ 核心区分:异常(内中断)与当前指令同步,中断(外中断)与指令流异步。缺页是异常不是中断——408 最爱出这道题。

中断嵌套与屏蔽字

  • 运行服务程序时又来了更急的请求 → 中断嵌套:高优先级打断低优先级(条件:开中断状态下响应)
  • 屏蔽字技术:每个中断源有一个屏蔽字,1 = 屏蔽(不许打断我)。把"自己 + 比自己低的"都置 1,就实现了动态调整优先级
  • 手算例题:中断 A~D,响应优先级 A>B>C>D。若想让处理优先级变成 B>D>A>C,问各屏蔽字?
    • 规则:处理优先级高的,屏蔽"自己及所有处理优先级比自己低的"(1=屏蔽),不屏蔽比自己高的(0=放行)
    • B 最高:屏蔽 B、D、A、C → 1111
    • D 次之:屏蔽 D、A、C,放行 B → 0111(按 B,D,A,C 位序)
    • A 再次:屏蔽 A、C,放行 B、D → 0011
    • C 最低:只屏蔽自己 → 0001
  • ⚠️ 响应优先级(谁先被 CPU 看)由硬件排队器固定;处理优先级(谁能打断谁)由屏蔽字软件设——两套体系别混

多重中断 vs 单重中断

单重中断:服务期间关中断,拒绝一切打扰;多重中断:服务期间开中断,允许更高级的插队。现代 OS 全是多重。

四、DMA 方式:大块数据别劳烦 CPU

中断解决了"有事叫我",但一字一中断搬运 1MB 数据 = 100 万次中断——还是太亏。DMA(Direct Memory Access)让 DMA 控制器直接掌管总线,在设备 ↔ 主存之间整块搬运,CPU 只在开头出力。

DMA 的三种传送方式(争总线的方式)

方式 怎么用总线 特点
停止 CPU 访存 DMA 传输期间 CPU 完全让路 控制最简单;CPU 有一段"纯空窗"
周期挪用(周期窃取) DMA 一个周期一个周期地"偷"总线 最常用;CPU 与 DMA 交替,DMA 优先
交替访问 时间上轮流,各走各拍 需要总线周期比存储周期长一倍,硬件复杂

DMA 流程(写盘为例)

  1. 预处理(CPU 干):设置 DMA 控制器的传送参数——主存起始地址、设备地址、传送字数,然后启动设备,CPU 转去干别的
  2. 数据传送(DMA 干):块级搬运,需要总线就申请(周期挪用),完全不经过 CPU 寄存器
  3. 后处理(CPU 干):传完后 DMA 发中断,CPU 校验数据、决定后续——⚠️ DMA 结束仍要一次中断,只是不再"一字一中断"

DMA vs 中断——必背对比表

维度 中断方式 DMA 方式
数据流向 设备 → CPU 寄存器 → 主存(过路 CPU 设备 ↔ 主存(绕开 CPU
打断时机 指令执行周期结束后响应 总线周期(存取周期)结束后即可让路
响应速度 指令粒度(较慢) 总线周期粒度(更快)
适用 低速设备(键盘、鼠标) 高速块设备(磁盘、网卡、SSD)
硬件 接口 + 中断逻辑 DMA 控制器(含地址/计数寄存器)
中断次数 每单位数据一次 整块传完一次(后处理)

💡 收伏笔:总线与接口说的"IRQ/DMA 通道"就是本篇的机制实体——每个设备申请一条 IRQ 线(中断源)或 DMA 通道。而"CPU 让出总线给 DMA"的本质是总线仲裁06-总线:DMA 控制器也是总线主设备,链式/独立请求里的一员)。

五、通道与 IO 处理机(了解)

  • 通道:能执行通道程序的专用处理器,进一步把"组块搬运的调度逻辑"也从 CPU 挪走——CPU 只发一条"启动通道"指令
  • 层级:程序查询 < 中断 < DMA < 通道,CPU 介入程度逐级下降
  • 大型机(IBM z 系列)用通道;个人电脑用 DMA + 多核中断分发就够了

六、盲点自测

  1. MMIO 和独立编址的本质区别?GPU 显存属于哪种?(是否占用主存地址空间;MMIO)
  2. 中断隐指令包含哪三件事?(关中断、保存断点、引出中断服务程序——硬件自动做)
  3. 缺页是中断还是异常?(异常/内中断,同步于指令)
  4. 响应优先级和处理优先级各由谁决定?(硬件排队器固定 / 屏蔽字软件设)
  5. DMA 三种方式里最常用的是?(周期挪用/窃取)
  6. DMA 结束后 CPU 为什么还要被打断一次?(后处理:校验、收尾)
  7. 为什么磁盘用 DMA、键盘用中断?(数据粒度:块设备整块搬,字符设备一字节一事件)

七、动手玩

# 看你机器的中断分配(macOS 用 dmesg 不直观,Linux 更清晰)
cat /proc/interrupts          # Linux:每个 IRQ 号挂在哪个 CPU、是什么设备
# 注意观察:中断被"摊"到多个核上——多核中断分发的现实版

参考资料

  • 王道《计算机组成原理考研复习指导》第 7 章
  • CSAPP 第 8 章《异常控制流》——异常/中断/信号/进程上下文切换的统一视角
  • 08-IO管理——OS 怎么在硬件之上组织 I/O(缓冲、驱动、假脱机)

⬅️ 总线 🏠 00-基础与理论 ➡️ 数据结构总览