graph TB
A["用户进程"] -->|"read/write"| B["VFS"]
B --> C["文件系统\next4/btrfs"]
C --> D["块 I/O 层"]
D --> E["I/O 调度器\nCFQ/Deadline"]
E --> F["块设备驱动"]
F --> G["磁盘"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,color:#333
style F fill:#FFF9C4,stroke:#F9A825,color:#333
style G fill:#C7CEEA,stroke:#9FA8DA,color:#333
graph TB
A["bio"] --> B["bi_io_vec 数组"]
B --> C["bio_vec 1\npage X, 4KB"]
B --> D["bio_vec 2\npage Y, 4KB"]
B --> E["bio_vec 3\npage Z, 4KB"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFDAB9,stroke:#FFAB76,color:#333
style E fill:#FFDAB9,stroke:#FFAB76,color:#333
关键点:
一个 bio 可以包含多个 bio_vec(分散/聚集 I/O)
每个 bio_vec 引用一个 page(不复制数据)
page cache 天然支持这种模式
1.3 bio 的”三段式”
graph LR
A["生成 bio\n(FS 调用 submit_bio)"] --> B["加入调度队列\n(电梯算法合并排序)"]
B --> C["派发到驱动\n(make_request_fn)"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#B5EAD7,stroke:#80CBC4,color:#333
graph TB
A["bio 1: sector 100"] --> D["调度器"]
B["bio 2: sector 50"] --> D
C["bio 3: sector 200"] --> D
D --> E["排序后:50, 100, 200\n减少寻道"]
style A fill:#FFB3C6,stroke:#F48FB1,color:#333
style B fill:#FFB3C6,stroke:#F48FB1,color:#333
style C fill:#FFB3C6,stroke:#F48FB1,color:#333
style D fill:#FFDAB9,stroke:#FFAB76,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,color:#333
3.2 4 大调度器对比
调度器
算法
适用
特点
noop
简单 FIFO
SSD、NVMe
无开销
deadline
截止时间优先
数据库
防饥饿
cfq
完全公平队列
桌面
多任务公平(已废弃)
bfq
预算公平队列
桌面、低延迟
cfq 替代品
3.3 noop
graph LR
A["请求"] --> B["FIFO 队列"]
B --> C["直接派发"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#FFDAB9,stroke:#FFAB76,color:#333
style C fill:#B5EAD7,stroke:#80CBC4,color:#333
优点:极简、零开销 缺点:不排序,磁头来回寻道 适用:SSD(无需排序,磁盘本身很快)
3.4 Deadline
graph TB
A["Deadline 调度器"] --> B["读 FIFO (红黑树)"]
A --> C["写 FIFO (红黑树)"]
B --> D["读 deadline\n默认 500ms"]
C --> E["写 deadline\n默认 5s"]
D --> F{"读超时?"}
F -->|"是"| G["优先派发读"]
F -->|"否"| H["正常排序"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
style E fill:#FFB3C6,stroke:#F48FB1,color:#333
style F fill:#FFF9C4,stroke:#F9A825,color:#333
style G fill:#B5EAD7,stroke:#80CBC4,color:#333
style H fill:#B5EAD7,stroke:#80CBC4,color:#333
优点:保证读延迟(写饥饿时可让读优先) 缺点:吞吐量不如 CFQ 适用:数据库(读敏感)
3.5 CFQ(已废弃)/ BFQ
graph TB
A["BFQ 调度器"] --> B["每进程一个队列"]
B --> C["时间片轮转"]
C --> D["公平调度"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#B5EAD7,stroke:#80CBC4,color:#333
graph TB
A["read(fd)"] --> B{"page cache\n有数据?"}
B -->|"是"| C["直接返回"]
B -->|"否"| D["磁盘读\n加入 page cache"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#FFF9C4,stroke:#F9A825,color:#333
style C fill:#B5EAD7,stroke:#80CBC4,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
graph TB
A["bio 1: 4KB @ sector 100"] --> D["调度器合并"]
B["bio 2: 4KB @ sector 101"] --> D
C["bio 3: 4KB @ sector 102"] --> D
D --> E["合并为\nbio A: 12KB @ sector 100"]
style A fill:#FFB3C6,stroke:#F48FB1,color:#333
style B fill:#FFB3C6,stroke:#F48FB1,color:#333
style C fill:#FFB3C6,stroke:#F48FB1,color:#333
style D fill:#FFDAB9,stroke:#FFAB76,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,color:#333
合并策略:
前向合并:相邻 bio 合并
后向合并:和正在处理的合并
跨 bio 合并:多个 bio 合并
6.2 电梯算法
graph TB
A["请求队列"] --> B{"方向\n向上?"}
B -->|"是"| C["向上处理"]
B -->|"否"| D["向下处理"]
C --> E{"顶?"}
E -->|"是"| F["换方向"]
E -->|"否"| C
D --> G{"底?"}
G -->|"是"| F
G -->|"否"| D
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#FFF9C4,stroke:#F9A825,color:#333
style C fill:#B5EAD7,stroke:#80CBC4,color:#333
style D fill:#B5EAD7,stroke:#80CBC4,color:#333
style E fill:#FFF9C4,stroke:#F9A825,color:#333
style F fill:#FFDAB9,stroke:#FFAB76,color:#333
style G fill:#FFF9C4,stroke:#F9A825,color:#333
# 输出(关注 bi/bo): # procs ---memory--- ---swap-- ---io--- -system-- ... # r b swpd free buff cache si so bi bo # 0 0 0 2g 500m 4g 0 0 20 100
7.3 iotop
1 2 3 4 5 6 7
# 看哪个进程在 I/O sudo iotop
# 输出: # Total DISK READ: 0.00 B/s | Total DISK WRITE: 10.00 K/s # PID PRIO USER DISK READ DISK WRITE COMMAND # 1234 be/4 root 0.00 B/s 10.00 K/s mysqld
八、面试高频考点
8.1 必背题
题目
答案要点
bio 是什么?
块 I/O 请求描述符
请求队列作用?
排序、合并 I/O
4 大 I/O 调度器?
noop / deadline / cfq / bfq
page cache 是什么?
文件内容内存缓存
buffer cache 呢?
块缓存(已弱化)
O_DIRECT 何时用?
自管理缓存 / 大文件顺序 I/O
何时用 noop?
SSD(无寻道)
何时用 deadline?
数据库(读敏感)
8.2 高频追问
追问
关键点
I/O 合并如何工作?
前向/后向合并相邻 bio
电梯算法?
单向扫描到底再换向
多队列设备?
NVMe:每个 CPU 一个队列
bio vs request?
bio = 逻辑 I/O,request = 物理 I/O
写回策略?
writeback 后台 + fsync 强制
直通 I/O vs 普通 I/O?
绕 vs 不绕 page cache
九、配套实验
9.1 实验 1:查看 I/O 调度器
1 2 3 4 5
# 当前调度器 cat /sys/block/sda/queue/scheduler
# 各设备的调度器 for d in /sys/block/*/queue/scheduler; doecho"$d: $(cat $d)"; done
graph TB
A["块 I/O 层核心"] --> B["bio\nI/O 请求"]
A --> C["请求队列\n合并排序"]
A --> D["I/O 调度器\nnoop/deadline/bfq"]
A --> E["page cache\n内容缓存"]
A --> F["O_DIRECT\n直通 I/O"]
style A fill:#C7CEEA,stroke:#9FA8DA,color:#333
style B fill:#E8D5F5,stroke:#CE93D8,color:#333
style C fill:#FFDAB9,stroke:#FFAB76,color:#333
style D fill:#FFB3C6,stroke:#F48FB1,color:#333
style E fill:#B5EAD7,stroke:#80CBC4,color:#333
style F fill:#FFF9C4,stroke:#F9A825,color:#333