I/O 控制方式与缓冲
基于通用操作系统概念 · 核于 2026-08
速查
- I/O 控制演进:程序查询(忙等待,CPU 全占)→ 中断驱动(CPU 起止参与,每单位一中断)→ DMA(整块传完才一中断,CPU 几乎零干预)→ 通道(可编程 I/O 处理器)。
- 程序查询:CPU
while(忙)死循环轮询状态寄存器——最浪费 CPU,现代 OS 不用。 - 中断驱动:CPU 发命令即返回,设备完成发中断,CPU 响应取数据——CPU 与设备并行,但每字节一中断,高频小数据开销大。
- DMA:CPU 把整块任务交给 DMA 控制器(DMAC),DMAC 直接在设备↔内存间搬运(周期窃取总线),整块完成才发一次中断——用于磁盘/网卡等大数据块。
- DMA 三要素:CPU 给 DMAC 设 源地址(设备/内存)、目的地址(内存/设备)、传输长度,DMAC 自主完成。
- 缓冲(Buffering):在内存开缓冲区暂存 I/O 数据,目的:①缓解 CPU 与设备速度失配;②减少 I/O 次数(攒够一块);③协调块设备与字符设备的粒度差。
- 单缓冲:设备→缓冲区→用户区,CPU 与设备可部分并行,但任一时刻只一方用缓冲。
- 双缓冲:两块缓冲交替——设备填 A 时 CPU 取 B,A 满换 B 填——CPU 与设备并行度更高,适合持续流式 I/O。
- 循环缓冲:多个缓冲组成环形队列,进一步平滑突发 I/O,常用于生产者-消费者(网卡收包)。
- 缓冲 vs 缓存(Cache):缓冲目的是减少 I/O 次数/速度匹配(必经缓冲);缓存目的是复用最近数据(命中即不读设备),如页缓存。
- Spooling(假脱机,Simultaneous Peripheral Operations On-Line):用磁盘 + 队列把独占设备虚拟化成共享设备——多进程"同时"提交打印,实际排队在磁盘,逐个输出。代表:打印机、邮件发送。
- Spooling 三要素:①高速磁盘(虚设备载体);②输入/输出井(磁盘上的排队区);③Spooling 守护进程(管理队列、调度实际输出)。
- 设备分配:独占设备需分配(避免冲突)——按设备类(多道)/物理设备(单道)/通道分层分配,配 DCT/COCT/CHCT 等数据结构(考研)。
一、程序查询:最原始的忙等待
程序查询(Programmed I/O,轮询):CPU 主动、反复地读设备状态寄存器,直到设备"就绪"再读/写数据寄存器:
发命令(写控制寄存器):
loop:
if (状态寄存器 == 忙) goto loop // 死循环,CPU 全占用
数据 = 数据寄存器 // 就绪,取数据- 致命缺点:CPU 全程忙等待。设备完成一次 I/O 通常要 ms 级(磁盘寻道+旋转),而 CPU 一条指令只要 ns 级——CPU 在
while(忙)里空转百万条指令,100% 浪费。 - 期间 CPU 无法执行其他进程(即便在多道系统,轮询也独占 CPU),吞吐极低。
- 仅用于:极简单的嵌入式系统、或设备必定"立即就绪"的场景(如读内存映射寄存器)。现代通用 OS 不用。
二、中断驱动:CPU 与设备并行
中断驱动 I/O:CPU 发出命令后立即返回去执行别的进程;设备完成传输后发硬件中断,CPU 响应中断、执行中断处理程序取走数据:
CPU: 写控制寄存器(启动设备) → 进程阻塞,调度别的进程执行
设备: 传输完成 → 拉高中断信号线
CPU: 当前指令结束 → 响应中断 → 保存现场 → 跳中断处理程序
→ 中断处理: 数据 = 数据寄存器 → 唤醒等 I/O 的进程 → 恢复现场- 优点:CPU 与设备并行工作——I/O 期间 CPU 跑别的进程,利用率大增。这是现代 OS 的基础。
- 缺点(高频小数据):每传一个单位(字节/字)就发一次中断。传 1KB 数据要 1024 次中断,每次中断都有上下文切换开销(μs 级),中断处理时间可能超过数据传输本身,CPU 被中断淹没。
- 缓解:配合缓冲——攒够一块再传,减少中断次数;或升级到 DMA。
三、DMA:大批量数据零干预
DMA(Direct Memory Access,直接内存访问):为消除中断驱动的高频中断,引入专门的 DMA 控制器(DMAC)。CPU 把整块数据传输任务交给 DMAC,DMAC 直接在设备与内存之间搬运数据,不需要 CPU 逐字节参与,整块传完才发一次中断:
CPU: 向 DMAC 写入——源(设备)、目的(内存地址)、长度(N) → 启动 → CPU 去执行别的进程
DMAC: 接管总线,逐字节/字在设备↔内存搬运
(总线冲突时用"周期窃取"——DMAC 借一个总线周期传一字,CPU 稍停顿)
DMAC: N 字节传完 → 发一次中断通知 CPU
CPU: 响应中断 → 收尾(唤醒进程)- 核心优势:大批量数据 CPU 几乎零干预。CPU 只在开始(设置 DMAC)和结束(一次中断)参与,中间传几 KB/M 数据 CPU 全程不介入。
- 总线控制:DMA 期间 DMAC 成为主总线控制者,与 CPU 分时共享总线——当 DMAC 和 CPU 同时要总线,DMAC 用周期窃取(cycle stealing)或突发模式(burst)。
- 适用:块设备(磁盘、SSD、网卡)的大数据块传输。这是现代磁盘 I/O 的标配。
- 与中断驱动对比:同样传 4KB——中断驱动要 4096 次中断,DMA 只要 1 次。
- 进阶:通道(Channel):大型机(IBM S/390)用通道——比 DMAC 更强,是可编程的 I/O 处理器,能执行通道程序(一组 I/O 指令链),独立完成复杂的 I/O 序列,CPU 只管启动和收尾。通道是 DMA 的"超集"。
DMA vs 中断 vs 程序查询
| 维度 | 程序查询 | 中断驱动 | DMA |
|---|---|---|---|
| CPU 参与 | 全程忙等待 | 每单位参与 | 仅起止参与 |
| 中断频率 | 无 | 每单位一次 | 整块一次 |
| 传输单位 | 字/字节 | 字/字节 | 块 |
| CPU 利用率 | 极低 | 高 | 最高 |
| 数据通路 | 设备↔CPU↔内存 | 设备↔CPU↔内存 | 设备↔内存(绕过 CPU) |
| 适用 | 极简单 | 慢速字符设备 | 块设备大批量 |
四、缓冲:缓解速度失配
缓冲(Buffering):在内存开辟缓冲区暂存 I/O 数据。三个目的:①缓解 CPU 与设备速度失配(CPU 快、设备慢,缓冲做"蓄水池");②减少 I/O 次数(攒够一块再传,比逐字节高效);③协调块设备与字符设备的粒度差。
- 单缓冲(Single Buffer):OS 在内核设一块缓冲区。设备把数据填入缓冲区,CPU 从缓冲区取——任一时刻只有一方能用缓冲(互斥),但"设备填下一块"与"CPU 处理上一块"可部分并行。每块处理时间 ≈ max(设备填, CPU 处理)。
- 双缓冲(Double Buffer):设两块缓冲区 A、B 交替使用。设备填 A 时,CPU 同时从 B 取;A 满后切换——设备填 B、CPU 取 A。CPU 与设备并行度显著提升,适合连续流式 I/O(音视频、串口)。
- 循环缓冲(Circular Buffer):多个缓冲区组成环形队列,一个生产者写、一个消费者读,用头尾指针管理。进一步平滑突发 I/O,常用于网卡收包、管道、生产者-消费者。
- 缓冲 vs 缓存(Cache):容易混淆。缓冲目的是"减少 I/O 次数 + 速度匹配"——数据必经缓冲(如磁盘块先读进内核缓冲区再给用户)。缓存目的是"复用最近用过的数据"——命中就直接用、不再读设备(如 Linux 的 page cache,第二次读同一文件从内存直接取)。
- 代价:缓冲占用额外内存,且数据常需在"用户缓冲↔内核缓冲↔设备"间多次拷贝(双倍拷贝问题,
sendfile/mmap/io_uring旨在减少拷贝)。
五、Spooling:独占设备虚拟化
Spooling(Simultaneous Peripheral Operations On-Line,外部设备同时联机操作 / 假脱机):用高速磁盘 + 队列把独占设备改造成共享设备的技术。
- 问题背景:打印机是独占设备——一次只能服务一个进程。若进程 A 打印 100 页,进程 B 要等很久;且若 A 打印到一半崩溃,打印机就卡死。直接共享会冲突/混乱。
- Spooling 做法:所有进程的打印任务不直接发给打印机,而是先写到磁盘上的"输出井"(一个排队目录),每个任务排成队列。一个 Spooling 守护进程(打印后台)按队列顺序,一个一个取出送往实际打印机。
- 效果:每个进程"提交即返回"(瞬间完成,因为只是写磁盘),感觉自己在独占打印机——独占设备被虚拟化成了多进程共享的设备。
- 三要素:①高速磁盘(虚拟设备的载体,开输入井/输出井);②输入井/输出井(磁盘上的排队区);③Spooling 守护进程/后台(管理队列、调度实际设备输出)。
- 典型应用:打印机(lpr/lpd)、邮件发送队列、批处理作业的输入井。
- 本质:用空间换时间 + 异步——用磁盘空间缓冲,把"独占设备串行访问"转成"磁盘多进程并发写 + 设备串行输出"。
下一步
I/O 控制与缓冲讲完后,下一个核心是磁盘调度与 RAID——磁盘的物理结构如何决定访问时间、FCFS/SSTF/SCAN/C-SCAN/LOOK 算法如何优化寻道、RAID 0/1/5/10 如何兼顾性能与可靠。