当前位置:首页>Linux>Linux I/O 调度器解析:CFQ、Deadline 与 BFQ 实现原理

Linux I/O 调度器解析:CFQ、Deadline 与 BFQ 实现原理

  • 2026-09-10 20:13:32
Linux I/O 调度器解析:CFQ、Deadline 与 BFQ 实现原理

    在 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 调度器主要目标包括:

  1. 提高磁盘吞吐量:通过合理调度 I/O 请求,减少磁盘寻道次数,优化顺序访问。

  2. 降低 I/O 延迟:避免请求长时间等待,提高系统响应。

  3. 保证公平性:防止某个进程占用过多 I/O 时间,保障多进程环境的公平访问。

  4. 支持 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 核心设计思想

  • 为每个发起 I/O 的进程或 cgroup 创建一个 独立的队列(cfq_queue)。

  • CFQ 使用 时间片(I/O slice) 来轮询队列。

  • 支持 读写分离 和 优先级队列。

2.2 队列与时间片机制

CFQ 的调度核心是时间片轮询队列:

  • 每个进程队列分配一个时间片(通常 4~8ms)。

  • 超过时间片后,当前队列挂起,调度器切换到下一个队列。

  • 队列中的请求按扇区号排序,以减少寻道。

调度流程示意:

+---------------------+| CFQ 调度器主循环     |+---------------------+        |        v   选择下一个 cfq_queue        |        v检查队列是否空或超时   |             |   v             v dispatch 请求  切换到下一个队列        |        v  完成 I/O 后更新队列时间片

2.3 I/O 合并机制

  • CFQ 会尝试 合并相邻扇区的请求,减少请求数量。

  • 内核调用 blk_try_merge_requests() 来执行合并。

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 核心设计思想

  • 每个请求维护一个 deadline(到期时间)。

  • 调度时优先处理 deadline 即将到期的请求。

  • 维护两个队列:

    1. FIFO 队列(按到达时间排序)

    2. 排序队列(按扇区号排序,优化寻道)

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 饥饿。

  • 高负载下性能稳定。

  • 结构简单,实现容易。

缺点:

  • 公平性不如 CFQ。

  • 对桌面交互响应优化有限。


第四章 Budget Fair Queuing (BFQ)

BFQ 是 CFQ 的改进版,兼顾吞吐量和响应性,适合桌面和多媒体应用。

4.1 核心设计思想

  • 为每个进程分配 I/O budget(允许发出的字节数)。

  • 根据预算调度队列,而不是时间片。

  • 支持 顺序访问优化 和 后台写延迟控制。

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 优化有限。

  • 极端随机负载吞吐量略低。


第五章 调度器对比与选择

特性
CFQ
Deadline
BFQ
公平性
高
中
高
吞吐量优化
一般
高
高
延迟优化
一般
高(读优先)
高(预算 + 读优先)
实现复杂度
高
中
高
适用场景
多进程混合 I/O
高负载随机 I/O
桌面、多媒体、交互负载
SSD 优化
一般
一般
一般

第六章 内核源码解析

6.1 CFQ

  • 文件路径:block/cfq-iosched.c

  • 核心结构:cfq_data, cfq_queue

  • 调度函数:cfq_dispatch_request()

  • 关键机制:

    • 时间片轮询

    • 队列合并

    • 优先级 cgroup 支持

6.2 Deadline

  • 文件路径:block/deadline-iosched.c

  • 核心结构:deadline_data

  • 调度函数:deadline_dispatch()

  • 关键机制:

    • FIFO + 扇区排序队列

    • deadline 超时优先

    • 写延迟批处理优化

6.3 BFQ

  • 文件路径:block/bfq-iosched.c

  • 核心结构:bfq_data, bfq_queue

  • 调度函数:bfq_dispatch_request()

  • 关键机制:

    • 预算驱动队列调度

    • 顺序访问优化

    • 读优先 & 多媒体友好


第七章 实际应用与优化策略

  1. SSD 环境

    • SSD 无寻道问题,建议使用 noop 或 deadline,减少调度器开销。

  2. HDD 高负载

    • CFQ 或 BFQ 提供公平性与吞吐优化。

  3. 数据库 / 高性能服务器

    • Deadline 避免 I/O 饥饿,保证延迟。

  4. 桌面多媒体系统

    • BFQ 提供良好响应性与公平性。

7.1 参数优化示例

  • Deadline 调度器:

echo 500 > /sys/block/sda/queue/iosched/read_expireecho 500 > /sys/block/sda/queue/iosched/write_expire
  • CFQ 调度器:

ionice -c 2 -n 0 <process>
  • BFQ 调度器:

echo bfq > /sys/block/sda/queue/scheduler

第八章 总结

Linux 的 I/O 调度器各有特点:

  • CFQ:公平性高,适合多进程混合 I/O,但 SSD 适用性一般。

  • Deadline:延迟保证强,防止 I/O 饥饿,适合高负载数据库或随机 I/O。

  • BFQ:预算驱动,高度公平,桌面和多媒体场景响应性最佳。

实际选择应根据 设备类型、应用负载和性能目标 决定。未来内核中,随着 NVMe 和 SSD 的普及,调度器的优化重点将从减少寻道转向延迟控制与队列优化。

最新文章

随机文章