块设备与 I/O 调度:从寻道时间到多队列
块设备(磁盘/SSD)以固定大小的块为读写单位,其机械或电气特性决定了访问代价模型;I/O 调度器通过合并与排序请求来优化这个代价,其设计随介质从 HDD 演进到 NVMe 而彻底改写。
一句话定义
块设备(磁盘/SSD)以固定大小的块为读写单位,其机械或电气特性决定了访问代价模型;I/O 调度器通过合并与排序请求来优化这个代价,其设计随介质从 HDD 演进到 NVMe 而彻底改写。
为什么重要
存储是冯·诺依曼体系中最慢的一环(kp-002 的数量级表)。同一个数据库查询,随机 I/O 与顺序 I/O 在 HDD 上差百倍——理解块设备的代价模型,才能理解数据库页大小、日志结构文件系统(kp-023)、LSM 树(kp-025)这些"为什么偏偏这样设计"。
前置知识
kp-002(DMA 与中断);kp-026(请求如何提交到设备)。
核心概念
- 扇区/块/簇:扇区是介质最小读写单元(512B 或 4KB),块是文件系统与内核的分配/传输单位,簇是 FAT 系的叫法。
- HDD 代价模型:服务时间 ≈ 寻道 + 旋转延迟 + 传输,前两项主导且与访问模式强相关。
- I/O 调度器:请求队列的管理者:合并相邻、排序寻道、公平配额。
- 调度器谱系:noop → deadline(防饥饿)→ CFQ(按进程公平)→ BFQ → mq-deadline/none(多队列时代)。
- 多队列(blk-mq):NVMe 每核独立队列,消除单锁队列的争用瓶颈。
原理与机制
HDD 代价量化(7200 转典型值):寻道 4~9ms,旋转延迟平均 4.2ms(半圈),传输 1MB 约 1ms。则:
顺序读 1MB: 寻道+旋转(8ms) + 传输(1ms) ≈ 9ms -> ~110 MB/s
随机读 1MB: 250次 x 4KB, 每次 8ms ≈ 2000ms -> ~0.5 MB/s
同一块盘, 随机访问吞吐跌落 200 倍
I/O 调度器存在的意义就是把随机流"整形"成尽量顺序的流:相邻扇区请求合并成一个大请求;电梯算法(SCAN)让磁头单向扫过盘面沿途服务,避免来回摆动;deadline 为每个请求设超时防止老请求饿死。SSD 没有机械寻道,随机与顺序的差距缩小到几倍,调度排序失去意义——因此 NVMe 时代默认调度器多为 none(透传),仅保留合并;blk-mq 多队列则把"每设备一个加锁队列"改为每核队列,匹配 NVMe 数十万 IOPS 的并行度。
公式或模型
HDD 服务时间模型:T = T_seek + T_rot + T_xfer,随机小 I/O 中 T_seek + T_rot 占 90% 以上——一切"凑顺序"的技术(日志、预读、LSM)都在压缩这两项的占比。
直观类比
HDD 调度像电梯算法本尊:只上行时接人、只下行时接人,绝不来回蹿。SSD 多队列像把单收发室改成每层楼一个快递柜:没有中央排队,各取各的。
实例或案例
lsblk -o NAME,ROTA,SIZE,TYPE # ROTA=1 机械盘, 0 固态
cat /sys/block/sda/queue/scheduler # 查看与切换调度器: none mq-deadline bfq
iostat -x 1 # r/s, w/s, await(单请求毫秒), util(设备繁忙度)
hdparm -t /dev/sda # 顺序读吞吐基准(只读测试,谨慎使用)
fio --name=r4k --rw=randread --bs=4k --size=1g --runtime=10 --time_based # 标准基准工具
实验:iostat -x 下先 cat 一个大文件(await 低),再随机 dd 跳读(await 飙升),直观感受访问模式的影响。
常见误区
- 用顺序吞吐评价 SSD:4K 随机 IOPS 才是数据库与虚拟机的关键指标。
- 认为块大小 = 扇区大小:块是软件层分配单位(常 4KB),扇区是介质单位(512B/4KB),两者可不同。
- 认为 SSD 不需要任何调度:合并写请求、限流防抖(尤其配合 cgroup)仍有价值,只是排序失效。
与其他知识点的关系
请求队列本身就是 kp-013 生产者-消费者的实例;预读与合并依赖 kp-027 页缓存;NVMe 并行队列与 kp-028 的 io_uring 相互成就。
延伸阅读
《操作系统概念》第 10 章大容量存储结构;内核文档 Documentation/block。
自测题
- HDD 随机 4K I/O 为什么比顺序慢两个数量级?
答:每次随机访问都要重新付寻道与旋转延迟(约 8ms),顺序访问只付一次后连续传输,故随机吞吐被机械延迟封顶。
- elevator 算法解决什么问题、引入什么新问题?
答:SCAN 单向扫盘合并请求减少寻道;新问题是磁头端请求可能被反复跳过而饥饿,需 deadline 等超时机制补偿。
- NVMe 为什么弃用传统调度器?
答:无机械寻道使排序无收益;数十万 IOPS 下单队列加锁成为瓶颈,blk-mq 每核队列加 none 透传成为更优解。