I/O 与磁盘 — 中断和 DMA 解放 CPU,RAID 用冗余换可靠
15 I/O 与磁盘 — 中断和 DMA 解放 CPU,RAID 用冗余换可靠
1. 这一章讲什么
进入第三部分:持久化。地基是两类硬件知识:CPU 怎么和慢设备打交道 (不学会这个,后面文件系统的「为什么」都立不住),和磁盘到底慢在哪 (机械运动的时间模型,是整个文件系统设计的第一推动力)。 外加 RAID——在文件系统登场之前,先用冗余磁盘阵列把 「又大又快又可靠」这三个愿望的结构讲清楚。
2. 顶层全景
CPU 与设备(三个协议升级):
轮询 PIO ──▶ 中断(慢设备):发请求、睡、中断叫醒;快设备反而该轮询
──▶ DMA:连「拷贝数据」也外包给 DMA 引擎
设备接口:三个寄存器(状态/命令/数据);驱动程序把千奇百怪的设备
统一成「一块可读的块数组」
│
磁盘的时间账:
T_I/O = 寻道 + 旋转延迟 + 传输
随机 4KB 读 ≈ 6–13ms(寻道+旋转是大头);顺序读 ≈ 带宽上限
⇒ 顺序比随机快几十倍 —— 「局部性」将是下一章的主题
│
RAID(三轴:容量 / 抗几盘坏 / 吞吐):
0 条带:性能满分,可靠零分
1 镜像:容量减半,读随机满带宽
4 奇偶集中:容量省,但奇偶盘是小写的瓶颈(R/2!)
5 旋转奇偶:把奇偶块摊开,小写升到 N/4·R ── 几乎取代了 4图说:三级递进——交互协议、单盘时间模型、多盘冗余结构。
3. 核心原理
3.1 主线例子:一次磁盘读的三代协议
设进程 1 要读磁盘(书里的时序图骨架)1:
**第一代:轮询(PIO)。协议四步:while 状态寄存器==忙,空转;
写数据到数据寄存器(一个 4KB 块要 CPU 一字一字搬,这叫
** programmed I/O);写命令寄存器;再 while 忙,空转等完成。
CPU 时间线:1 1 1 1 1 p p p p p p 1 1 1——p 是烧掉的轮询时间2。
第二代:中断。发完请求就把进程 1 睡掉,CPU 去跑进程 2;
磁盘完成后发硬件中断,中断处理程序叫醒进程 1。
时间线变成 1 1 1 1 1 2 2 2 2 2 1 1 1——磁盘干活时 CPU 有人用3。
但中断不是无条件更好:设备很快时,中断处理+上下文切换比轮询还贵;
网络洪峰时,每个包一个中断会把系统打进活锁(livelock)——
光处理中断,用户程序永远跑不了;对策是中断合并(设备稍等,
攒几个完成一起报)和「轮询一小会儿再中断」的混合策略4。
**第三代:DMA。**中断解决了「等」的浪费,没解决「搬」的浪费—— PIO 下拷贝 4KB 也要 CPU 逐字搬运。DMA 引擎接手: OS 告诉它「数据在内存哪、多长、去哪个设备」,然后就去忙别的; 拷贝完 DMA 发中断5。时间线里拷贝那段 c 从 CPU 行挪到了 DMA 行6。
设备怎么连进系统:总线分层次(内存总线最快最短;PCI 居中; SCSI/SATA/USB 等外设总线在最外圈——物理和成本决定的)。 OS 和设备说话有两套办法:专用 I/O 指令(x86 的 in/out,特权级), 或内存映射 I/O(设备寄存器映射成内存地址,普通 load/store 直达)。 设备驱动是那层让文件系统不用关心「背后是 SCSI 还是 SATA」的软件—— 内核里 device-neutral 性的来源7。
3.2 磁盘的时间模型:一次随机读要付三笔钱
机械盘的样子:一张或多张盘片绕主轴转(7,200–15,000 RPM; 10,000 RPM = 一转 6 毫秒);每张盘面一个磁头挂在机械臂上; 数据在磁道(同心圆)上的 512 字节扇区里8。
一次 I/O 的三段时间(书里的公式)9:
T_I/O = T_寻道 + T_旋转 + T_传输- 寻道:机械臂移到目标磁道——最贵的一项。加速、滑行、减速、 稳定(0.5–2ms,必须停准)。
- 旋转延迟:目标扇区转到底下。平均半转:15,000 RPM = 250 转/秒 = 一转 4ms → 平均 2ms10。
- 传输:扇区从磁头下流过的时间,通常可忽略。
主线例子的账(书里两款真实硬盘,4KB 随机读)11:
| 盘 | 寻道 | 旋转 | 传输 | T_I/O | 随机带宽 |
|---|---|---|---|---|---|
| Cheetah 15K.5(高性能,SCSI) | 4ms | 2ms | 0.03ms | ≈6ms | 0.66 MB/s |
| Barracuda(大容量,SATA) | 9ms | ≈4.2ms | — | ≈13.2ms | 0.31 MB/s |
对比顺序读:一次寻道+旋转后贴着读,传输率几乎等于峰值 (125/105 MB/s)。随机与顺序差出两个数量级——书里的实测: Cheetah 差约 200 倍,Barracuda 差 300 倍以上12。这就是「顺序 I/O」的军规 和下一章 FFS 全部设计的出发点。
两个会咬人的细节:厂商只保证单个 512 字节扇区原子写—— 大一点的写断电就是撕裂写(torn write)13;盘载缓存(8–16MB) 默认写回,会让「已写入」撒谎——这直接通向第 17 章的日志14。
3.3 RAID:三个愿望的三轴评估
RAID 对外透明地就是一块大盘(换上去不用改任何软件—— 透明性让 RAID 一夜部署成功15)。评估任何 RAID 设计用三轴: 容量(N 盘 × B 块里有多少有用)、可靠性(容忍几盘坏)、 性能(分顺序/随机两种负载;先记两个数:单盘顺序带宽 S、 随机带宽 R,S≫R)16。
RAID-0 条带:块轮流铺到各盘。容量 N·B(满分)、可靠 0 分、 性能满分(顺序 N·S,随机 N·R)——它是性能的上界17。
RAID-1 镜像:每块存两份(可分在镜像对上做条带 = RAID-10)。 容量砍半 (N·B)/2;稳抗 1 盘坏;随机读可分摊到全部盘 = N·R(意外之喜), 随机写两份物理写 = (N/2)·R18。多盘更新的一致更新问题 在这里第一次露面:写盘 0 完成、写盘 1 前断电,两份就不一致了—— 后面文件系统的崩溃一致性是同一个问题的放大版19。
RAID-4 奇偶:每条纹块配一个奇偶块,用 XOR 算——
不变式:每行(数据+奇偶)里 1 的个数是偶数;掉任何一块,
用其余块 XOR 回来20。容量 (N−1)·B,只花一块盘的冗余!
顺序读写都是 (N−1)·S。问题在随机小写:改一块数据必须同时改奇偶,
用减法公式 P新 = (C旧 ⊕ C新) ⊕ P旧(读旧数据+旧奇偶,写新数据+新奇偶,
每次逻辑写 = 4 次物理 I/O);更要命的是所有奇偶都住同一块盘——
奇偶盘成为串行瓶颈,随机写吞吐 = R/2,加盘也不涨(小写问题)21。
RAID-5 旋转奇偶:和 4 完全一样,只把奇偶块逐行轮换到不同盘。 小写依然 4 次物理 I/O,但不同请求的奇偶落在不同盘上, 并行回来了:随机写 = N/4·R22。三轴总表(书里的图 38.8, 照抄)23:
| RAID-0 | RAID-1 | RAID-4 | RAID-5 | |
|---|---|---|---|---|
| 容量 | N·B | (N·B)/2 | (N−1)·B | (N−1)·B |
| 抗坏 | 0 | 1(运气好 N/2) | 1 | 1 |
| 随机读 | N·R | N·R | (N−1)·R | N·R |
| 随机写 | N·R | (N/2)·R | R/2 | N/4·R |
| 顺序读写 | N·S | (N/2)·S | (N−1)·S | (N−1)·S |
图说:一行行读下来,每个级别都是在「容量、可靠、性能」三角里 换不同的顶点;RAID-5 全面好于 4,是它几乎消灭 4 的原因24。
4. 作者的判断与证据
书里给了证据的判断:
- 三代协议的时序图和「快设备该轮询」的反面教材都有展开34;
- 随机与顺序读相差两个数量级有实测背书(200 倍 / 300 倍)12;
- RAID 各级的吞吐公式全部从「物理 I/O 次数」推出,不是经验值2122。
作者的判断(立场):
- 「能透明部署的功能才有部署率」被作为工程通则提出15;
- 对 RAID-1 的态度微妙:容量贵,但随机读满带宽——「其实拿到一半带宽已经算好的了」是在为奇偶方案的下文铺垫。
5. 边界与局限
- 机械盘的账对理解设计仍然重要,但持久层主战场已是闪存 (下一章拆解 SSD);「寻道/旋转」在时间模型里的位置被 「擦除块/磨损」取代。
- RAID 一章假设 fail-stop 故障模型(盘非好即坏、坏了立刻知道)—— 静默损坏(latent sector error、位翻转)推到第 45 章,而现实中 静默错误才是主战场。
- RAID 不是备份:误删、火灾、勒索软件,RAID 一概不管—— 这个常识书里没展开,值得自己记住(补充,不在书里)。
6. 可带走的
- 慢设备三件套:中断(省等的浪费)、DMA(省搬的浪费)、 合并(省中断的浪费)。设计任何异步接口都先想这三板斧。
- 快设备用轮询、慢设备用中断、洪水用合并——「一刀切」是错的, 设备速度分布决定策略。
- T_I/O = 寻道 + 旋转 + 传输;评估任何存储方案的随机性能, 大头永远是前两项。
- 顺序比随机快几十倍——这是文件系统、数据库、应用层缓存 共同的第一原则。
- RAID 三轴评估法可复用到一切冗余设计:容量开销、容错数、 性能代价,一张表说清楚。
- 小写问题 = 热点(奇偶盘)问题的标准解法:把热点摊开 (旋转奇偶)。负载均衡的第一性思路就在这里。
7. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 系统架构与总线层次 | 36. I/O Devices | text/38-ch36-36-i-o-devices.txt:49(搜「System Architecture」) |
| 三个寄存器与轮询协议 | 36. I/O Devices | text/38-ch36-36-i-o-devices.txt:119(搜「Canonical Protocol」) |
| 中断与重叠的时序 | 36. I/O Devices | text/38-ch36-36-i-o-devices.txt:162(搜「Interrupts」) |
| 「中断不总是更好」与合并 | 36. I/O Devices | text/38-ch36-36-i-o-devices.txt:206(搜「NOT ALWAYS BETTER」)与:227(搜「coalescing」) |
| DMA | 36. I/O Devices | text/38-ch36-36-i-o-devices.txt:235(搜「DMA」) |
| 内存映射 I/O 与驱动 | 36. I/O Devices | text/38-ch36-36-i-o-devices.txt:282(搜「Methods Of Device」)与:320(搜「device driver」) |
| 磁盘几何与 512 字节扇区 | 37. Hard Disk Drives | text/39-ch37-37-hard-disk-drives.txt:52(搜「platter」) |
| 撕裂写 | 37. Hard Disk Drives | text/39-ch37-37-hard-disk-drives.txt:31(搜「torn write」) |
| 寻道、旋转、传输三段 | 37. Hard Disk Drives | text/39-ch37-37-hard-disk-drives.txt:118(搜「Seek Time」)与:171(搜「complete picture」) |
| T_I/O 公式与两款盘的账 | 37. Hard Disk Drives | text/39-ch37-37-hard-disk-drives.txt:284(搜「Doing The Math」)与:332(搜「Cheetah」) |
| 随机与顺序的实测差距 | 37. Hard Disk Drives | text/39-ch37-37-hard-disk-drives.txt:370(搜「factor of 200」) |
| 写回缓存的危险 | 37. Hard Disk Drives | text/39-ch37-37-hard-disk-drives.txt:220(搜「write back」) |
| RAID 透明性 | 38. Redundant Arrays of Inexpensive Disks(RAIDs) | text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:31(搜「TRANSPARENCY」) |
| 三轴评估法 | 38. Redundant Arrays of Inexpensive Disks(RAIDs) | text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:106(搜「Evaluate」) |
| RAID-0 条带 | 38. Redundant Arrays of Inexpensive Disks(RAIDs) | text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:134(搜「Striping」) |
| 一致更新问题 | 38. Redundant Arrays of Inexpensive Disks(RAIDs) | text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:350(搜「CONSISTENT-UPDATE」) |
| RAID-4 与减法奇偶公式 | 38. Redundant Arrays of Inexpensive Disks(RAIDs) | text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:416(搜「Parity」)与:543(搜「Pnew」) |
| 小写问题(R/2) | 38. Redundant Arrays of Inexpensive Disks(RAIDs) | text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:572(搜「small-write」) |
| RAID-5 旋转奇偶与总表 | 38. Redundant Arrays of Inexpensive Disks(RAIDs) | text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:594(搜「Rotating Parity」)与:622(搜「RAID-0」) |
Footnotes
-
出处:「36. I/O Devices」第 126–195 段(
text/38-ch36-36-i-o-devices.txt:129,搜「While (STATUS」)。 ↩ -
出处:「36. I/O Devices」第 136–148 段(
text/38-ch36-36-i-o-devices.txt:138,搜「polling」)。原文:反复读状态寄存器即轮询;数据由 CPU 逐字搬即 programmed I/O(PIO)。 ↩ -
出处:「36. I/O Devices」第 162–195 段(
text/38-ch36-36-i-o-devices.txt:174,搜「timeline」)。时间线图示:无中断时 CPU 轮询空转(p),有中断时进程 2 填补磁盘工作时间。 ↩ ↩2 -
出处:「36. I/O Devices」第 196–233 段(
text/38-ch36-36-i-o-devices.txt:206,搜「NOT ALWAYS BETTER」)。原文:快设备轮询更好;网络洪峰可致活锁;合并降低中断开销。 ↩ ↩2 -
出处:「36. I/O Devices」第 235–280 段(
text/38-ch36-36-i-o-devices.txt:255,搜「PIO OVERHEADS」)。原文:DMA 引擎在设备与内存间搬运数据,OS 只需编程一次,完成时收中断。 ↩ -
出处:「36. I/O Devices」第 260–280 段(
text/38-ch36-36-i-o-devices.txt:269,搜「revised timeline」)。 ↩ -
出处:「36. I/O Devices」第 282–360 段(
text/38-ch36-36-i-o-devices.txt:289,搜「COMMUNICATE」)与:320(搜「device-neutral」)。 ↩ -
出处:「37. Hard Disk Drives」第 50–93 段(
text/39-ch37-37-hard-disk-drives.txt:52,搜「platter」)。RPM、磁道、扇区、磁头、机械臂的定义均在该节。 ↩ -
出处:「37. Hard Disk Drives」第 284–289 段(
text/39-ch37-37-hard-disk-drives.txt:289,搜「Tseek」)。 ↩ -
出处:「37. Hard Disk Drives」第 104–117 段(
text/39-ch37-37-hard-disk-drives.txt:113,搜「rotational delay」)。平均旋转延迟为半转;文中例:15,000 RPM → 4ms/转 → 平均 2ms。 ↩ -
出处:「37. Hard Disk Drives」第 309–357 段(
text/39-ch37-37-hard-disk-drives.txt:330,搜「Cheetah」)。Cheetah:T=6ms、R≈0.66 MB/s;Barracuda:T≈13.2ms、R≈0.31 MB/s。 ↩ -
出处:「37. Hard Disk Drives」第 368–371 段(
text/39-ch37-37-hard-disk-drives.txt:370,搜「factor of 200」)。原文:Cheetah 随机与顺序差约 200 倍,Barracuda 差 300 倍以上。 ↩ ↩2 -
出处:「37. Hard Disk Drives」第 28–31 段(
text/39-ch37-37-hard-disk-drives.txt:31,搜「torn write」)。原文:驱动器只保证单个 512 字节扇区原子;大一点的写遇断电即撕裂写。 ↩ -
出处:「37. Hard Disk Drives」第 211–224 段(
text/39-ch37-37-hard-disk-drives.txt:220,搜「write back」)。写回缓存可让「完成」早于真实落盘,破坏顺序保证。 ↩ -
出处:「38. Redundant Arrays of Inexpensive Disks(RAIDs)」第 31–50 段(
text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:31,搜「TRANSPARENCY」)。 ↩ ↩2 -
出处:「38. Redundant Arrays of Inexpensive Disks(RAIDs)」第 106–132 段(
text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:106,搜「Evaluate」)。 ↩ -
出处:「38. Redundant Arrays of Inexpensive Disks(RAIDs)」第 134–219 段(
text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:134,搜「Striping」)。 ↩ -
出处:「38. Redundant Arrays of Inexpensive Disks(RAIDs)」第 298–414 段(
text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:308,搜「two physical copies」)。 ↩ -
出处:「38. Redundant Arrays of Inexpensive Disks(RAIDs)」第 350–370 段(
text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:350,搜「CONSISTENT-UPDATE」)。原文:写多盘的逻辑操作在断电下可能只完成一半。 ↩ -
出处:「38. Redundant Arrays of Inexpensive Disks(RAIDs)」第 416–475 段(
text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:443,搜「XOR(0,0,1,1)」)。 ↩ -
出处:「38. Redundant Arrays of Inexpensive Disks(RAIDs)」第 513–593 段(
text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:572,搜「bottleneck」)。原文:奇偶盘瓶颈使随机小写 = R/2,加盘无效。 ↩ ↩2 -
出处:「38. Redundant Arrays of Inexpensive Disks(RAIDs)」第 594–646 段(
text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:594,搜「Rotating Parity」)。随机写 = N/4·R,4 次物理 I/O 是奇偶方案的固有成本。 ↩ ↩2 -
出处:「38. Redundant Arrays of Inexpensive Disks(RAIDs)」第 622–638 段(
text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:622,搜「RAID-0」)。图 38.8 总表。 ↩ -
出处:「38. Redundant Arrays of Inexpensive Disks(RAIDs)」第 647–649 段(
text/40-ch38-38-redundant-arrays-of-inexpensive-disks-raids.txt:648,搜「replaced」)。 ↩