第 1 章:Deadline 调度器概述与设计目标
1.1 Deadline 调度器的起源与目标
Deadline 调度器(Deadline I/O Scheduler)是 Linux 内核中用于块设备的请求调度算法之一。它最初设计的目标是解决传统 CFQ(Completely Fair Queuing) 调度器在高负载情况下出现的延迟不可控问题。CFQ 更强调公平性,但在对实时性和延迟敏感的应用场景下(如数据库、虚拟机磁盘 I/O),其随机延迟可能过大。Deadline 调度器通过引入 请求过期机制(deadline),保证每个请求不会被无限期延迟,从而在提供较高吞吐量的同时确保 I/O 延迟上限。
其核心设计思想包括以下几点:
延迟保证:每个请求都有一个最大等待时间(deadline),超时的请求会优先调度。
读写分离:读请求通常延迟敏感,写请求可以稍微延迟,Deadline 调度器维护独立的队列以区分两类请求。
顺序访问优化:对于顺序磁盘访问,Deadline 通过 扫描算法 尽量减少磁头寻道时间,提高顺序吞吐性能。
Deadline 调度器主要适用于 SSD/HDD 均可,但对旋转盘(HDD)尤其有效,因为磁头移动代价较高。它在 Linux 内核中是 内核默认提供的调度器之一,可以通过命令:
cat /sys/block/sda/queue/scheduler
查看可用调度器,并通过:
echo deadline > /sys/block/sda/queue/scheduler
切换当前设备使用 Deadline 调度器。
1.2 Deadline 调度器的数据结构
Deadline 调度器核心依赖三个主要的数据结构:
sort_list(按扇区排序的队列)
fifo_list(按到达顺序的队列)
rbtree 或链表结构
这种双队列设计保证了调度器在同时优化延迟和顺序访问的能力。通过 sort_list 提供快速顺序调度,通过 fifo_list 提供延迟保证。
第 2 章:Deadline 调度器请求插入机制
2.1 请求到达与插入流程
当一个 I/O 请求到达 Deadline 调度器时,它会经过以下流程:
区分读写类型
计算请求 Deadline
read_expire(默认 500ms)
write_expire(默认 5s)
插入 sort_list
插入 fifo_list
按到达顺序插入尾部链表,便于过期检查。
此操作 O(1),保证低开销。
通过上述流程,每个请求同时存在 顺序队列和超时队列,实现延迟控制和顺序扫描优化双目标。
2.2 插入优化与内核实现细节
Deadline 调度器的实现还包含一些优化细节:
Deadline 调度器内部对读写请求分开处理,分别插入各自的红黑树和 FIFO 队列。 这些设计保证了 Deadline 调度器在面对随机 I/O 或顺序 I/O 的混合负载时,既能保证延迟上界,又能充分利用磁盘顺序吞吐能力。
第 3 章:Deadline 请求调度策略
3.1 调度器核心策略
Deadline 调度器的核心调度策略是 “按 deadline 优先 + 扫描顺序”,具体规则如下:
超时请求优先
检查 FIFO 队列头部的请求是否已过期。
过期请求优先调度,即使它在扇区顺序上不是最优。
顺序扫描选择
读写混合处理
这种策略兼顾了实时性和顺序优化,特别适用于数据库、Web 服务等对 I/O 延迟敏感的场景。
3.2 代码实现与选择逻辑
Deadline 调度器在内核中的核心选择函数通常为:
static struct request *deadline_dispatch(struct request_queue *q, int force){ struct deadline_data *dd = q->elevator->elevator_data; struct request *rq; // 1. 检查读请求过期 rq = dd_first_expired(&dd->r_fifo); if (rq) return dd_dispatch_request(dd, rq); // 2. 检查写请求过期 rq = dd_first_expired(&dd->w_fifo); if (rq) return dd_dispatch_request(dd, rq); // 3. 顺序扫描调度读请求 rq = dd_pick_next_request(&dd->r_rb_root, dd->last_sector); if (rq) return dd_dispatch_request(dd, rq); // 4. 顺序扫描调度写请求 rq = dd_pick_next_request(&dd->w_rb_root, dd->last_sector); if (rq) return dd_dispatch_request(dd, rq); return NULL;}
调度逻辑:
FIFO 队列用于过期检查
Sort 队列用于顺序调度
读请求优先
这使得 Deadline 调度器在高负载情况下依然能保持 低延迟和高吞吐。
第 4 章:Deadline 调度器的读写分离优化
4.1 读请求的延迟优化
Deadline 调度器特别针对读请求设计了低延迟策略:
短过期时间
优先队列机制
批量顺序扫描
4.2 写请求的滞后与合并策略
写请求设计上可容忍更长延迟,Deadline 调度器通过以下手段优化写吞吐:
写请求滞后窗口(write_back_window)
写请求在短时间内不会立即调度,允许读请求优先。
避免“读饥饿”,提高响应性能。
批量合并
最大过期时间保证
这种设计保证了 读延迟低、写吞吐高,尤其适合数据库和虚拟化场景。
第 5 章:Deadline 调度器在多队列环境下的优化
5.1 blk-mq 多队列适配
Linux 4.x 及以上内核引入了 blk-mq(多队列块层),Deadline 调度器也适配了多队列机制:
每个硬件队列独立调度
每个队列都有独立的 Deadline 数据结构。
避免单队列锁竞争,提高多核系统下吞吐。
全局延迟保证
高并发优化
5.2 Deadline 与 CFQ / NOOP 比较
Deadline 调度器与其他调度器的区别:
Deadline 在高 I/O 压力下能保证读延迟,同时顺序扫描优化也保证了 HDD 顺序吞吐,是高性能存储的首选方案。
第 6 章:Deadline 调度器性能调优与应用实践
6.1 参数调优
Deadline 调度器核心参数可以通过 sysfs 调整:
read_expire / write_expire
fifo_batch / front_merges
调优方法示例:
echo 200 > /sys/block/sda/queue/iosched/read_expireecho 1000 > /sys/block/sda/queue/iosched/write_expireecho 16 > /sys/block/sda/queue/iosched/fifo_batch
通过实际 I/O 测试调整参数,满足应用的低延迟和高吞吐需求。
6.2 实践应用场景
数据库服务器
虚拟化环境
混合负载场景
通过参数调优和合理队列设计,Deadline 调度器能够在不同硬件和工作负载下,提供稳定、可预测的 I/O 性能