1本章要解决的问题
如何在磁盘上存储和访问数据?现代硬盘怎样存储数据?接口是什么?数据在盘上怎样布局、怎样被访问?磁盘调度如何提升性能?
How do modern hard-disk drives store data? What is the interface? How is the data actually laid out and accessed? How does disk scheduling improve performance?
上一章讲了操作系统和一般 I/O 设备打交道的通用机制(轮询、中断、DMA、驱动)。这一章专门研究其中最重要的一种设备:硬盘驱动器(hard disk drive, HDD)。几十年来它一直是持久化存储的主力,后面的文件系统(FFS、日志、LFS……)几乎每一个设计决策都建立在"磁盘怎样工作"的认识之上。
本章的核心收获只有一句话,却贯穿整个持久化部分:一次磁盘 I/O 的时间 = 寻道 + 旋转 + 传输,前两项是机械运动,慢得可怕;所以顺序访问比随机访问快几百倍,而磁盘调度就是想办法少做机械运动。
2核心概念速览
扇区数组(array of sectors)
磁盘对外就是 0 到 n−1 编号的 512 字节扇区;只有单个扇区的写是原子的。
盘片 · 表面 · 磁道
盘片两面都是表面;每个表面上有成千上万条同心圆磁道,每条磁道上排着扇区。
寻道(seek)
磁臂把磁头移到目标磁道:加速、滑行、减速、稳定,毫秒级,最贵的操作之一。
旋转延迟(rotational delay)
磁头到位后等目标扇区转过来。平均半圈:15000 RPM 时是 2ms。
传输(transfer)
扇区从磁头下经过时真正读写数据。对小请求来说它只占很少时间。
TI/O 与 RI/O
TI/O = Tseek + Trotation + Ttransfer;RI/O = 传输大小 / TI/O。
磁盘调度(disk scheduling)
磁盘请求的"作业长度"可以估算,所以能做近似 SJF:SSTF、SCAN/C-SCAN、SPTF。
磁道偏斜 · 缓存
track skew 让跨磁道顺序读不错过扇区;磁道缓冲(track buffer)缓存整条磁道,write-back 更快但有风险。
3接口:一个扇区数组
现代磁盘的基本接口很简单:驱动器由大量扇区(sector)组成,每个 512 字节,可读可写。在有 n 个扇区的磁盘上,扇区编号从 0 到 n−1,这就是驱动器的地址空间。多扇区操作当然可以(很多文件系统一次读写 4KB 甚至更多),但厂商只保证单个 512 字节的写是原子的:要么完整写入,要么完全没写。如果写 4KB 的过程中突然断电,可能只有其中一部分扇区写上了,这叫撕裂写(torn write)。
此外还有一些接口里没写、但大家都默认的"不成文的约定"(unwritten contract):地址空间里相邻的两个块,访问起来比相距很远的两个块快;连续访问一段块(顺序读写)是最快的方式,通常比任何随机访问模式快得多。读完本章你会明白这些约定从何而来。
4基本几何结构
盘片(platter)是一张坚硬的圆盘(比如铝),表面镀一层很薄的磁性层,通过改变磁性来持久存储比特,断电后数据依然保留。一个磁盘可以有一张或多张盘片,每张盘片有两面,每一面叫一个表面(surface)。所有盘片固定在主轴(spindle)上,电机带动主轴以恒定速率旋转,速率用 RPM(每分钟转数)衡量,常见 7200–15000 RPM。10000 RPM 的磁盘转一圈大约 6ms。
每个表面上的数据按同心圆组织,一圈叫一条磁道(track);一个表面有成千上万条磁道,一根头发丝的宽度里就能挤下几百条。读写靠磁头(disk head)完成,每个表面一个;所有磁头装在同一个磁臂(disk arm)上,磁臂在表面上移动,把磁头定位到想要的磁道。
5一个简单的磁盘:单磁道与旋转延迟
从最简单的模型开始,一次加一条磁道。假设磁盘只有一条磁道,12 个扇区,编号 0–11,每个 512 字节(Figure 37.1)。加上磁头和磁臂(Figure 37.2),磁头此刻停在扇区 6 上方,盘片逆时针旋转——所以扇区按 6、7、8……11、0、1 的顺序依次经过磁头。
现在来了一个读扇区 0 的请求。在单磁道磁盘上,驱动器什么也不用移动,只需等待扇区 0 转到磁头下面。这段等待在现代磁盘里经常发生,而且占 I/O 服务时间的很大比重,因此专门叫旋转延迟(rotational delay)。如果转一整圈要 R,从 6 等到 0 大约要 R/2。最坏的请求是扇区 5:刚刚错过,几乎要等一整圈。
6多磁道:寻道
再现实一点:三条磁道(Figure 37.3 左)。外道是扇区 0–11,中道 12–23,内道 24–35;磁头现在在最内道。来了一个读扇区 11 的请求,驱动器必须先把磁臂移到正确的磁道(最外道),这叫寻道(seek)。寻道和旋转一起,是磁盘最昂贵的操作。
寻道本身分好几个阶段:加速(磁臂开始移动)→ 滑行(全速移动)→ 减速 → 稳定(settling)——磁头必须精确地停在正确的磁道上(差一点都不行),稳定时间往往就要 0.5–2ms。
寻道完成后,磁头已经在正确磁道上,但盘片在这段时间里也在转(Figure 37.3 右,约转过 3 个扇区),此时扇区 9 正要经过磁头,只需再忍受一小段旋转延迟。当扇区 11 经过磁头时,进入最后一个阶段:传输(transfer),数据被读出或写入。完整的 I/O 时间由此得出:先寻道,再等旋转,最后传输。
7一些其他细节:偏斜、多区与缓存
磁道偏斜(track skew)
很多驱动器会让相邻磁道的扇区编号错开一些,保证跨磁道的顺序读也能顺畅进行。原因是:从一条磁道切换到相邻磁道时,磁头需要时间重新定位(即使只移动一道)。如果没有偏斜,磁头刚到下一条磁道,想要的下一个块已经从它下面转过去了,只能再等将近一整圈。
在后面的模拟器里载入 10,11,12,13 这组请求,对比偏斜 0 和偏斜 2:总时间从 585 降到 285。
多区(multi-zoned)
外圈磁道比内圈长,自然能放更多扇区。多区磁盘把表面分成若干区(zone),一个区是一组连续的磁道;同一区内每条磁道扇区数相同,外侧的区每道扇区更多。由于转速恒定,外圈每秒经过磁头的扇区更多,所以外圈的顺序带宽更高。
磁道缓冲:write-back 与 write-through
现代驱动器都有一块缓存(cache),历史原因也叫磁道缓冲(track buffer),通常 8 或 16MB。读一个扇区时,驱动器可能顺手把整条磁道都读进缓存,之后对同一磁道的请求就能立刻返回。写的时候驱动器有个选择:数据放进缓存就报告"写完了",叫 write-back 缓存(又叫 immediate reporting);真正写到盘面上才报告,叫 write-through。write-back 让磁盘"看起来"更快,但有风险:如果文件系统或应用依赖写入顺序来保证正确性(第 42 章的日志就是这样),write-back 可能打乱顺序、断电时造成问题。
8I/O 时间:算一算
化学课上"让单位互相约掉,答案自己冒出来"的方法叫量纲分析(dimensional analysis),在系统分析里同样好用。例 1:10000 RPM 的磁盘转一圈多少毫秒?把想要的单位写在左边,再把已知条件依次乘上,让单位约掉:
例 2:传输速率 100MB/s,传 512KB 要多少毫秒?
有了磁盘的抽象模型,就可以用一点数学来分析性能。I/O 时间是三部分之和:
T_I/O = T_seek + T_rotation + T_transfer (37.1)
R_I/O = Size_Transfer / T_I/O (37.2)
比较驱动器时,I/O 速率 RI/O 更好用。原书拿两块 Seagate 磁盘做对比:高性能 SCSI 盘 Cheetah 15K.5 和面向容量的 SATA 盘 Barracuda(Figure 37.5):
| Cheetah 15K.5 | Barracuda | |
|---|---|---|
| 容量 | 300 GB | 1 TB |
| 转速 | 15,000 RPM | 7,200 RPM |
| 平均寻道 | 4 ms | 9 ms |
| 最大传输率 | 125 MB/s | 105 MB/s |
| 盘片数 | 4 | 4 |
| 缓存 | 16 MB | 16/32 MB |
| 接口 | SCSI | SATA |
这两块盘正好代表磁盘市场的两极:"高性能"市场追求转得快、寻道短、传得快;"容量"市场最在乎每字节的成本,盘慢一些,但尽量塞下更多比特。考虑两种负载:随机负载(random)向磁盘随机位置发出小的(如 4KB)读请求,数据库等应用很常见;顺序负载(sequential)连续读大量扇区。用下面的计算器复现原书的计算(参数可改):
随机 vs 顺序:两块磁盘的 TI/O 与 RI/O
Cheetah 15K.5
Barracuda
负载
逐项看原书的 Cheetah 随机负载:平均寻道直接取厂商给的 4ms(全程寻道大约要 2–3 倍长);15000 RPM = 250 转/秒,一圈 4ms,平均等半圈 2ms;4KB 以 125MB/s 传输只要约 30µs(1ms 可是 1000µs!),小到可以忽略。于是 TI/O ≈ 6ms,RI/O ≈ 4KB / 6ms ≈ 0.66MB/s。Barracuda 的 TI/O ≈ 13.2ms,慢一倍多,RI/O ≈ 0.31MB/s。顺序负载假设只寻道、旋转一次,然后传 100MB:TI/O 约为 800ms 和 950ms,I/O 速率几乎就是峰值传输率 125 和 105MB/s(Figure 37.6):
| Cheetah | Barracuda | |
|---|---|---|
| RI/O 随机 | 0.66 MB/s | 0.31 MB/s |
| RI/O 顺序 | 125 MB/s | 105 MB/s |
说明:原书数字是四舍五入的近似(把 T 取成约 6ms、13ms,把 4KB 当成约 0.004MB)。计算器按 1MB = 1024KB 精确计算,得到 0.65 和 0.30MB/s、顺序 124 和 104MB/s,结论完全一样。
这张表说明了两件事。第一,也是最重要的:随机和顺序负载之间有巨大的性能差距,Cheetah 约 200 倍,Barracuda 超过 300 倍。由此得到计算史上最显而易见的设计建议:
顺序地使用磁盘(Use disks sequentially)。只要可能,就按顺序传输数据;做不到顺序,至少要用大块传输,越大越好。如果 I/O 是零零碎碎的随机小块,性能会急剧下降,用户会受苦,你也会受苦——因为你知道这都是你那些随手的随机 I/O 造成的。
第二点更微妙:"高性能"盘和"容量"盘的性能差距也很大。所以人们愿意为前者付高价,而对后者则尽量买便宜的。
9旁注:为什么平均寻道是全程的 1/3
书和论文里常说"平均寻道时间约为全程寻道的三分之一"。这来自平均寻道距离的计算(不是时间)。把磁道编号为 0 到 N,任意两道 x、y 之间的寻道距离是 |x − y|。所有可能寻道的距离之和用积分近似:
∫₀ᴺ ∫₀ᴺ |x − y| dy dx
内层拆开绝对值:∫₀ˣ (x − y) dy + ∫ₓᴺ (y − x) dy = x² − Nx + N²/2
外层:∫₀ᴺ (x² − Nx + N²/2) dx = N³/3 − N³/2 + N³/2 = N³/3
除以寻道总数 N²:平均距离 = N/3
用穷举验证 N/3
10磁盘调度
I/O 代价这么高,操作系统历来会插手决定磁盘请求的顺序。给定一组 I/O 请求,磁盘调度程序(disk scheduler)检查它们并决定下一个服务哪个。和进程调度不同,进程的运行时间通常不可知,而磁盘请求要花多久却能估算得相当准:估计寻道和可能的旋转延迟即可。于是磁盘调度器可以贪心地先做最短的那个——遵循 SJF(最短作业优先)的原则。
SSTF:最短寻道时间优先
SSTF(shortest-seek-time-first,也叫 SSF)按磁道排序请求,先服务最近磁道上的请求。原书的例子(Figure 37.7):磁头在内道,有扇区 21(中道)和 2(外道)两个请求,SSTF 先发 21,完成后再发 2。
SSTF 不是万能的。第一,操作系统看不到驱动器的几何结构,只看到一个块数组。这好解决:OS 可以改用最近块优先(nearest-block-first, NBF),下一个服务块地址最近的请求。第二个问题更根本:饥饿(starvation)。如果磁头所在的内道源源不断地来请求,纯 SSTF 会完全忽略其他磁道上的请求。
如何处理磁盘饥饿?怎样实现类似 SSTF 的调度,同时避免饥饿?
How can we implement SSTF-like scheduling but avoid starvation?
电梯算法:SCAN 与 C-SCAN
答案早就有了。SCAN 让磁头在磁盘上来回扫,按磁道顺序服务请求。从外到内(或从内到外)扫一遍叫一个扫描(sweep)。如果某个请求所在的磁道这一趟已经扫过了,它不会被立即处理,而是排到下一趟(反方向)。变种有:
- F-SCAN:扫描期间冻结要服务的队列,扫描中新到的请求放进另一个队列稍后服务。这样,远处的请求不会因为不断到来的近处新请求而饿死。
- C-SCAN(Circular SCAN):只从外向内扫,扫到头就回到最外道重新开始。这对内外磁道更公平:来回扫的 SCAN 偏爱中间磁道——服务完最外道后,要经过中间两次才会再回到最外道。
SCAN 也叫电梯算法(elevator algorithm),因为它像电梯一样要么向上要么向下,而不是看哪层近就去哪层。想象你从 10 楼下到 1 楼,有人在 3 楼进来按了 4,电梯却因为 4 楼"更近"而掉头上去——多气人!电梯算法在现实中避免了打架,在磁盘上则避免了饥饿。
可惜 SCAN 及其变种(甚至 SSTF)都没有尽可能地贴近 SJF:它们忽略了旋转。
如何计入磁盘旋转的开销?怎样实现同时考虑寻道和旋转、更接近 SJF 的算法?
How can we implement an algorithm that more closely approximates SJF by taking both seek and rotation into account?
SPTF:最短定位时间优先
SPTF(shortest positioning time first,也叫 SATF, shortest access time first)就是答案。看原书的例子(Figure 37.8):磁头在内道扇区 30 上方,下一个该服务中道的 16,还是外道的 8?
答案是"看情况"(it depends)。取决于寻道和旋转的相对快慢:如果寻道比旋转慢得多,SSTF 及其变种就够了;但如果寻道比旋转快不少,那就应该多寻一段道去服务外道的 8,而不是就近去中道服务 16——16 刚刚转过磁头,得等几乎一整圈。在下面的模拟器里用"原书 Figure 37.8"两个预设按钮对比:寻道速率 S=4 时 SPTF 先做 8,总时间 315;SSTF 先做 16,总时间 435。把 S 改成 1,两者都会先做 16。
几乎任何问题都可以用"看情况"回答,就像作者的同事 Miron Livny 常说的。但请谨慎使用:回答太多次"看情况",人们就不再问你问题了。比如有人问"去吃午饭吗?"你答:"看情况,你也去吗?"更重要的是知道为什么要看情况、看什么情况。
在现代驱动器上,寻道和旋转大致相当(当然取决于具体请求),所以 SPTF 有用、能提升性能。但它在 OS 里更难实现:OS 通常不知道磁道边界在哪,也不知道磁头此刻转到了哪个角度。所以 SPTF 通常在驱动器内部实现。
其他调度问题
- 在哪里调度?老系统里 OS 包办一切:从待处理请求中选最好的发给磁盘,完成后再选下一个。现代磁盘能同时容纳多个未完成的请求,内部有精巧的调度器(掌握磁头精确位置等所有细节,可以准确实现 SPTF)。于是 OS 调度器通常挑出它认为最好的若干个(比如 16 个)一起发下去,由磁盘按 SPTF 顺序服务。
- I/O 合并(I/O merging):比如依次来了读块 33、8、34 的请求(Figure 37.8),调度器应把 33 和 34 合并成一个两块的请求,再对合并后的请求排序。合并在 OS 层尤其重要,能减少发给磁盘的请求数、降低开销。
- 该等多久再发请求?直觉是只要有一个 I/O 就立刻发给磁盘,这叫work-conserving(保持工作):有请求时磁盘绝不空闲。但预期调度(anticipatory disk scheduling)的研究表明,有时稍等一下更好(non-work-conserving):也许马上就会来一个"更好"的请求(比如同一进程的下一个相邻块),整体效率反而更高。难点是决定何时等、等多久;Linux 内核里有相关实现。
11模拟器:仿 disk.py 看寻道、旋转与传输
下面的模拟器复现了原书作业工具 disk.py 的模型:3 条磁道(外道 0–11、中道 12–23、内道 24–35),每道 12 个扇区、每扇区 30°,磁道间距 40 个距离单位。默认寻道速率 -S 1(每时间单位移动 1 个距离单位,所以换一道要 40)、旋转速率 -R 1(每时间单位转 1°,所以传输一个扇区要 30、转一圈要 360)。时间 0 时磁头在外道、正对扇区 6 的中间。输入请求序列,逐步观察每个请求的寻道 旋转 传输。
disk.py:请求序列、调度策略与三段时间
动画里盘片逆时针转,磁头固定在右侧(3 点钟方向)沿半径方向移动(真实磁臂是绕轴摆动的,这里简化成直线)。浅色扇区是已服务的,橙色是还没服务的请求,深橙色是正在传输的扇区。
12先预测,再验证
-a 0、-a 6、-a 30、-a 7,30,8、-a 10,11,12,13 的寻道、旋转、传输时间。提示:起点是扇区 6 的正中间,每扇区 30。看答案
-a 0:Seek 0、Rotate 165(6 的后半 15 + 7…11 共 5×30)、Transfer 30、Total 195。-a 6:Seek 0、Rotate 345(刚错过 6 的开头,要等 360−15)、Transfer 30、Total 375。-a 30:Seek 80(两道 × 40)、Rotate 265、Transfer 30、Total 375。30 和 6 角度对齐,寻道 80 之后还得等到同一时刻 345。-a 7,30,8:7 → (0, 15, 30, 45);30 → (80, 220, 30, 330);8 → (80, 310, 30, 420)。合计 Seek 160、Rotate 545、Transfer 90、Total 795。-a 10,11,12,13:10 → (0, 105, 30, 135);11 → (0, 0, 30, 30);12 → (40, 320, 30, 390);13 → (0, 0, 30, 30)。合计 Seek 40、Rotate 425、Transfer 120、Total 585。
-a 7,30,8,把寻道速率改成 -S 2、4、8、10、40、0.1。先预测:寻道快 2 倍,总时间会少一半吗?看答案
S=2:寻道合计 80,但 Total 仍是 795——省下的寻道时间全变成了旋转等待(30 和 8 都"早到了但扇区还没来")。S=4、8、10、40:Total 都是 435,因为从 30 到 8 寻道足够快,赶在 8 转过来之前就位,省掉了一整圈;之后再快也没用,瓶颈变成旋转。S=0.1:寻道 800 一次,Total 2235。结论:寻道和旋转是耦合的,只优化其中一项,收益可能是零,也可能是跳变。
-a 7,30,8,把旋转速率改成 -R 0.1、0.5、0.01。时间怎么变?看答案
转得越慢,旋转和传输都等比例变长:R=0.5 Total 1590,R=0.1 Total 4350,R=0.01 Total 43500。有意思的是 R=0.1 时第三个请求 8 只需旋转 220:寻道 80 这段时间盘几乎没转,等待模式完全不同。
-a 7,30,8 用 SSTF 和 SATF 各是什么顺序、多长时间?再找一组 SATF 明显优于 SSTF 的请求。看答案
SSTF:7 和 8 都在当前磁道(距离 0),按队列顺序先 7 再 8,最后 30。7 → (0, 15, 30, 45);8 → (0, 0, 30, 30);30 → (80, 190, 30, 300);合计 Seek 80、Rotate 205、Transfer 90、Total 375(FIFO 是 795)。SATF 选出同样的顺序,也是 375。
SATF 胜出的例子:-a 0,20(默认参数)。SSTF 先做同道的 0(要等 165),Total 435;SATF 发现 20 寻道 40 后只需再等 5,先做 20,Total 195。或者用原书 Figure 37.8 的预设:S=4 时 SSTF 435 对 SPTF 315。一般地,当寻道相对旋转很快、而近处磁道上的请求刚刚错过时,SATF 更好。
-a 10,11,12,13 哪里表现差?加磁道偏斜能改善吗?默认寻道速率下偏斜设多少最好?S=2、S=4 时呢?能写出公式吗?看答案
问题出在 11 → 12 跨磁道:寻道 40,而读完 11 时 12 的起点正好在磁头下,寻道完它已转过去,要等 320。偏斜试验结果(Total):S=1 时 skew 0/1/2/3 = 585/615/285/315,最佳 2;S=2 或 4 时 = 585/255/285/315,最佳 1。
公式:换一道的寻道时间是 40/S,这段时间盘转过 (40/S)·R 度,每扇区 30°,所以偏斜至少要 skew = ⌈(40 / S) · R / 30⌉ 个扇区。偏斜再大也不好:多等的每个扇区都是浪费。
看答案
1MB 的传输要 8ms,TI/O ≈ 4 + 2 + 8 = 14ms,RI/O ≈ 71MB/s,是 4KB 随机读的一百多倍。这就是"做不到顺序,至少用大块传输,越大越好":寻道和旋转是每个请求的固定成本,请求越大,固定成本被摊得越薄。
13动手实验:顺序读 vs 随机读
labs/37-hdd/seqrand.c 在当前目录写一个 128MB 的测试文件,然后用 O_DIRECT(绕过页缓存,每次读都真的到达设备)分别测:1MB 一次的顺序读、4KB 一次的顺序读、4KB 一次的随机读。测完自动删除文件。
cd labs/37-hdd && make && ./seqrand
示例输出(WSL2 · ext4,底层是 SSD 上的虚拟磁盘;你的机器上数字会不同):
测试文件 128 MB = 32768 个 4KB 块(O_DIRECT,绕过页缓存)
顺序读 1MB × 128 : 57.1 ms 2243.6 MB/s
顺序读 4KB × 4000 : 481.2 ms 32.5 MB/s 平均每次 120.3 us
随机读 4KB × 4000 : 648.9 ms 24.1 MB/s 平均每次 162.2 us
顺序(1MB) / 随机(4KB) 带宽比 ≈ 93 倍
怎么解读:
- 1MB 顺序读比 4KB 随机读快约 90 倍——即使在没有机械部件的 SSD 上,"大块、顺序"依然是王道,因为每个请求都有固定开销(系统调用、虚拟化层、设备命令处理)。
- 4KB 随机读只比 4KB 顺序读慢一点:SSD 没有磁头,也就没有寻道和旋转延迟。在真正的 HDD 上,随机 4KB 每次要付出 5–15ms,只有 0.3–1MB/s(本章 Figure 37.6),与顺序读相差几百倍。
- 为什么要
O_DIRECT:刚写完的文件还在页缓存里,不绕过缓存的话读到的是内存,测不到设备。O_DIRECT要求缓冲区、偏移、长度按块对齐,所以用posix_memalign。SSD 的内部结构在第 44 章详细讨论。
14自测题
-a 7,30,8 的总时间:15常见误区与本章小结
- "寻道快了,I/O 就一定快"——不一定。寻道和旋转耦合:早到的磁头只能等扇区转过来(作业 2)。
- "传输率 125MB/s,所以磁盘就有 125MB/s"——只有顺序大块传输才接近这个数;随机 4KB 只有不到 1MB/s。
- "平均寻道时间 = 全程寻道时间的一半"——平均寻道距离约为全程的 1/3。
- "lseek() 会让磁头寻道"——lseek 只改内存里的文件偏移(第 39 章),真正的磁盘寻道发生在后续读写落到不同磁道时。
- "磁盘调度只由 OS 完成"——现代磁盘内部有自己的(通常是 SPTF)调度器,OS 只负责挑一批、合并请求。
- "write-back 缓存只是更快,没有代价"——它可能在断电时丢失或打乱写入顺序,危及依赖写顺序的日志等机制。
本章要点
- 磁盘接口是扇区数组,只有单扇区写原子;"相邻快、顺序最快"是不成文的约定。
- TI/O = Tseek + Trotation + Ttransfer;小随机 I/O 几乎全是机械定位时间,顺序与随机相差 200–300 倍。
- track skew、多区、磁道缓冲是真实磁盘的重要细节;write-back 快但有风险。
- 磁盘调度近似 SJF:SSTF/NBF 会饿死请求 → SCAN/F-SCAN/C-SCAN 解决饥饿 → SPTF 同时考虑寻道与旋转,通常在驱动器内部实现;再加上 I/O 合并与预期调度。
和后续章节的联系:单块磁盘不够大、不够快、也不够可靠,下一章 RAID 把多块磁盘组合成一个"更好的磁盘",分析时直接沿用本章的 S(顺序带宽)和 R(随机带宽)。第 41 章 FFS 会利用"相邻块访问快"来布局文件;第 43 章 LFS 则把所有写都变成顺序写,把这一章的"顺序地使用磁盘"推向极致。