2、I/O调度器原理:CFQ、Deadline、NOOP、Kyber、BFQ调度器的工作机制与适用场景
Linux 块设备层中,I/O 调度器(I/O Scheduler)是位于通用块层与底层驱动之间的关键组件。其核心职责是对来自文件系统或直接 I/O 的请求进行排队、合并与排序,以优化磁盘寻道时间、降低延迟或保障公平性。不同的调度器在设计哲学上存在显著差异,理解其工作机制是进行存储性能调优的前提。
2.1 I/O调度器在块设备栈中的位置
下图展示了 I/O 调度器在 Linux 存储栈中的位置。它接收来自通用块层的 bio 请求,经过调度与合并后,生成 request 结构体并传递给底层驱动。
2.2 五大调度器工作机制详解
2.2.1 CFQ (Completely Fair Queuing)
CFQ 是 Linux 内核中长期以来的默认调度器,其设计目标是 公平性。它为每个进程分配独立的 I/O 时间片,并在时间片内处理该进程的请求队列。CFQ 通过计算进程的 I/O 优先级和权重,确保所有进程都能获得合理的 I/O 带宽。
- 工作机制: 维护多个内部队列(每个进程一个),通过时间片轮转调度。支持进程 I/O 优先级(ionice)。
- 优点: 多进程环境下公平性好,能有效防止单个进程饿死其他进程。
- 缺点: 延迟较高,不适合随机 I/O 密集场景;在高速 SSD 上调度开销占比过大。
- 适用场景: 传统的机械硬盘(HDD)环境,尤其是桌面系统或通用服务器。
2.2.2 Deadline
Deadline 调度器以 延迟保障 为核心目标。它维护两个队列:一个按扇区排序(提高吞吐),另一个按请求过期时间排序(保障延迟)。每个请求被赋予一个截止时间(读请求通常为 500ms,写请求为 5s)。
- 工作机制: 正常情况下按扇区顺序分发请求;当有请求即将超时时,立即切换到“截止时间”队列,优先处理超时请求。
- 优点: 读请求延迟低,写请求不会无限阻塞;在数据库等延迟敏感场景表现优秀。
- 缺点: 公平性不如 CFQ,写请求可能被读请求大幅抢占。
- 适用场景: 数据库服务器、实时应用、混合读写负载。
2.2.3 NOOP (No Operation)
NOOP 是最简单的调度器,它几乎不做任何排序操作,仅执行基本的请求合并。请求按照到达顺序直接进入 FIFO 队列。
- 工作机制: 仅进行前后请求的合并,不进行重排序。本质是一个简单的 FIFO 队列。
- 优点: 调度开销极低,CPU 占用少。
- 缺点: 对机械硬盘无任何寻道优化,性能极差。
- 适用场景: 纯 SSD 或 NVMe 设备(无机械寻道时间),以及硬件 RAID 卡或智能控制器(硬件已做排序)。
2.2.4 Kyber
Kyber 是专为 快速设备(如 NVMe SSD) 设计的高性能调度器。它采用基于令牌桶的算法,动态控制提交到驱动层的 I/O 深度,以最小化延迟。
- 工作机制: 维护读/写两个令牌桶。根据当前延迟反馈动态调整令牌发放速率。当延迟升高时,减少令牌发放以降低队列深度。
- 优点: 延迟可控,对延迟尖峰有很好的抑制作用;调度开销低。
- 缺点: 吞吐量可能不如 NOOP 或 mq-deadline;配置参数较少,调优空间有限。
- 适用场景: 低延迟要求的 NVMe SSD、云原生存储节点。
2.2.5 BFQ (Budget Fair Queuing)
BFQ 是 CFQ 的现代替代品,专注于提供 更细粒度的公平性。它不仅分配时间片,还分配 I/O 带宽预算(以扇区数计量),并考虑进程的 I/O 模式。
- 工作机制: 为每个进程维护一个预算(budget),预算用完后切换到下一个进程。支持低延迟模式,可对交互式进程进行优先响应。
- 优点: 公平性极佳,交互式体验好;能有效防止 I/O 抖动。
- 缺点: 调度开销较大,在高速 SSD 上可能成为瓶颈。
- 适用场景: 桌面系统、多媒体编辑、需要稳定 I/O 延迟的嵌入式系统。
2.3 调度器特性对比表
| 调度器 | 核心目标 | 调度开销 | 延迟表现 | 公平性 | 适用设备 |
|---|---|---|---|---|---|
| CFQ | 公平性 | 高 | 中等 | 优秀 | HDD |
| Deadline | 延迟保障 | 中 | 低(读) | 一般 | HDD / SSD |
| NOOP | 低开销 | 极低 | 取决于硬件 | 无 | SSD / NVMe / RAID |
| Kyber | 延迟控制 | 低 | 低且稳定 | 中等 | NVMe SSD |
| BFQ | 细粒度公平 | 高 | 中等 | 极优 | HDD / 低速SSD |
2.4 调度器选择决策流程
在实际调优中,可参考以下决策树快速选择调度器:
2.5 查看与切换调度器
在 Linux 系统中,可以通过 /sys/block/<device>/queue/scheduler 文件查看和修改当前调度器。
# 查看当前调度器(方括号内为当前生效的调度器)
cat /sys/block/sda/queue/scheduler
# 输出示例:[mq-deadline] kyber bfq none
# 临时切换调度器(重启后失效)
echo kyber > /sys/block/nvme0n1/queue/scheduler
# 永久修改(通过 udev 规则)
# /etc/udev/rules.d/60-iosched.rules
# ACTION=="add|change", KERNEL=="nvme*", ATTR{queue/scheduler}="kyber"
2.6 多队列块层(blk-mq)与调度器
随着 NVMe 等高速设备的普及,传统单队列调度器(如 CFQ、Deadline)在锁竞争和扩展性上出现瓶颈。Linux 内核引入了 blk-mq(多队列块层) 架构。blk-mq 为每个 CPU 核心分配独立的软件队列,并映射到硬件队列,大幅降低了锁开销。
- 传统调度器(单队列): CFQ、Deadline、NOOP。适用于单队列设备或兼容模式。
- 多队列调度器(blk-mq): mq-deadline、Kyber、BFQ、none。专为 blk-mq 设计,支持多队列并行分发。
下表对比了传统调度器与多队列调度器的关键差异:
| 特性 | 传统调度器(单队列) | 多队列调度器(blk-mq) |
|---|---|---|
| 锁粒度 | 全局队列锁(高竞争) | 每 CPU 软件队列锁(低竞争) |
| 扩展性 | 差(CPU 核数 > 8 时瓶颈明显) | 优秀(可线性扩展至数百核) |
| 典型代表 | CFQ, Deadline, NOOP | mq-deadline, Kyber, BFQ, none |
| 适用硬件 | HDD, 低速 SSD | NVMe, 高速 SSD, 多队列硬件 |
lsblk -D 查看设备是否支持多队列。