在 Linux 系统中,I/O 调度器是块设备层(Block Layer)和设备驱动层之间的关键模块,它直接影响系统的磁盘性能、延迟表现以及多进程的公平性。I/O 调度器的作用是管理 请求队列(Request Queue),决定何时以及以何种顺序将 I/O 请求提交到底层存储设备。本文将从原理、调度策略、内核实现机制、优化策略及源码解析等多角度对 Linux 三大经典 I/O 调度器:CFQ(Completely Fair Queuing)、Deadline、BFQ(Budget Fair Queuing) 进行系统性剖析。
第一章 I/O 调度器概述
Linux 内核的块层设计允许不同的 I/O 调度器实现不同的策略,用户可通过 /sys/block/<device>/queue/scheduler 选择调度器。I/O 调度器主要目标包括:
提高磁盘吞吐量:通过合理调度 I/O 请求,减少磁盘寻道次数,优化顺序访问。
降低 I/O 延迟:避免请求长时间等待,提高系统响应。
保证公平性:防止某个进程占用过多 I/O 时间,保障多进程环境的公平访问。
支持 QoS / 优先级调度:如实时任务优先处理。
1.1 块设备请求处理流程
Linux 的块层从用户态到设备驱动大致流程如下:
用户态应用 | v系统调用接口(如 read/write) | vVFS 层文件系统 | v块 I/O 子系统(Block Layer) | |-- 请求队列合并、排序、调度(I/O 调度器) v底层块设备驱动 | v存储设备
I/O 调度器主要位于 块层请求队列(struct request_queue) 和设备驱动之间,对请求的顺序进行排序和优化,并可进行合并(merge)、延迟(delay)、优先级控制等操作。
第二章 Completely Fair Queuing (CFQ)
CFQ 是 Linux 内核中最早的公平调度器之一,其核心理念是让每个进程公平地获得磁盘访问时间,类似 CPU 的完全公平调度器 CFS。
2.1 核心设计思想
2.2 队列与时间片机制
CFQ 的调度核心是时间片轮询队列:
调度流程示意:
+---------------------+| CFQ 调度器主循环 |+---------------------+ | v 选择下一个 cfq_queue | v检查队列是否空或超时 | | v v dispatch 请求 切换到下一个队列 | v 完成 I/O 后更新队列时间片
2.3 I/O 合并机制
2.4 优先级控制
CFQ 支持 8 个 I/O 优先级(0-7),映射到不同队列权重,允许用户通过 ionice 命令控制进程 I/O 权重。例如:
ionice -c 2 -n 0 dd if=/dev/zero of=/tmp/test bs=1M count=100
此命令将进程设置为 普通 I/O 优先级,确保其他高优先级进程不会被延迟过久。
2.5 内核实现
核心数据结构:
struct cfq_data { struct cfq_queue *active_queue; struct rb_root dispatch_trees[2]; // 读写红黑树 ...};struct cfq_queue { struct list_head fifo; unsigned int slice_end; unsigned int slice_dispatch; ...};
核心调度函数:
cfq_dispatch_request(struct cfq_data *cfqd) { // 按时间片选择活跃队列 // 检查请求是否超时 // dispatch 请求到块层}
2.6 CFQ 优缺点
优点:
高度公平,多进程混合 I/O 友好。
支持 I/O 优先级和 cgroup。
缺点:
对 SSD 无明显优势,时间片机制可能增加延迟。
实现复杂,调度器开销较大。
第三章 Deadline 调度器
Deadline 调度器关注 延迟保证,确保每个 I/O 请求在截止时间内被处理,避免饥饿。
3.1 核心设计思想
3.2 调度流程
+---------------------------+| Deadline 调度器主循环 |+---------------------------+ | v检查 FIFO 队列中是否有 deadline 超时请求 | 有超时 --> dispatch 无超时 --> 按扇区号排序队列调度 | v完成 I/O 更新队列
3.3 内核实现关键点
核心数据结构:
struct deadline_data { struct list_head sort_list; // 扇区排序队列 struct list_head fifo_list; // FIFO 队列 ...};
核心函数:
deadline_dispatch(struct request_queue *q) { // 优先调度超时请求 // 否则调度最接近当前扇区的请求}
3.4 优缺点
优点:
避免 I/O 饥饿。
高负载下性能稳定。
结构简单,实现容易。
缺点:
第四章 Budget Fair Queuing (BFQ)
BFQ 是 CFQ 的改进版,兼顾吞吐量和响应性,适合桌面和多媒体应用。
4.1 核心设计思想
4.2 调度流程
+----------------------+| BFQ 调度器主循环 |+----------------------+ | v选择活跃队列(有预算且非空) | vDispatch 请求 |更新队列预算,若用完 --> 切换队列
4.3 内核实现关键点
核心数据结构:
struct bfq_data { struct bfq_queue *active_queue; ...};struct bfq_queue { unsigned int budget; // I/O 预算 unsigned long last_dispatch; ...};
核心调度函数:
bfq_dispatch_request(struct bfq_data *bfqd) { // 按预算选择队列 // 发出请求 // 更新预算}
4.4 优缺点
优点:
高度公平。
响应性好,特别适合桌面和多媒体。
顺序访问优化,提高吞吐量。
缺点:
实现复杂。
对 SSD 优化有限。
极端随机负载吞吐量略低。
第五章 调度器对比与选择
第六章 内核源码解析
6.1 CFQ
6.2 Deadline
6.3 BFQ
第七章 实际应用与优化策略
SSD 环境
HDD 高负载
数据库 / 高性能服务器
桌面多媒体系统
7.1 参数优化示例
echo 500 > /sys/block/sda/queue/iosched/read_expireecho 500 > /sys/block/sda/queue/iosched/write_expire
ionice -c 2 -n 0 <process>
echo bfq > /sys/block/sda/queue/scheduler
第八章 总结
Linux 的 I/O 调度器各有特点:
CFQ:公平性高,适合多进程混合 I/O,但 SSD 适用性一般。
Deadline:延迟保证强,防止 I/O 饥饿,适合高负载数据库或随机 I/O。
BFQ:预算驱动,高度公平,桌面和多媒体场景响应性最佳。
实际选择应根据 设备类型、应用负载和性能目标 决定。未来内核中,随着 NVMe 和 SSD 的普及,调度器的优化重点将从减少寻道转向延迟控制与队列优化。