FEATURED · 精选文章

Linux 内核 mq-deadline IO 调度器调优参数详解:read_expire、fifo_batch 与 front_merges 的源码级解析

发布时间 / 2026/9/14 16:41:57
来源 / 创域科博编辑部
栏目 / 资讯中心
Linux 内核 mq-deadline IO 调度器调优参数详解:read_expire、fifo_batch 与 front_merges 的源码级解析 Linux 内核 mq-deadline IO 调度器调优参数详解read_expire、fifo_batch 与 front_merges 的源码级解析【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux本文基于内核文档 Documentation/block/deadline-iosched.rst完整讲解 deadlinemq-deadlineIO 调度器暴露给高级用户的五个可调参数read_expire、write_expire、fifo_batch、writes_starved与front_merges的含义与调优方法并结合 block/mq-deadline.c 源码还原每个参数在请求入队、合并与派发路径中的实际作用帮助你在块设备上做低延迟/高吞吐权衡时有据可依。1. 背景deadline 调度器要解决什么问题deadline 调度器的设计目标是为每个请求保证一个开始服务时间。由于读请求对交互延迟更敏感调度器分别维护读、写两条队列并在各自的截止时间expire到来前尽量完成派发。文档原文明确了这一取向The goal of the deadline io scheduler is to attempt to guarantee a start service time for a request. As we focus mainly on read latencies, this is tunable.在当前内核中deadline 调度器以 blk-mq 框架的适配实现存在block/mq-deadline.c 文件头注释写着MQ Deadline i/o scheduler - adaptation of the legacy deadline scheduler, for the blk-mq scheduling framework其注册名为mq-deadline别名仍保留为deadline见 elevator_type 定义static struct elevator_type mq_deadline { ... .elevator_name mq-deadline, .elevator_alias deadline, ... };因此 sysfs 中你看到的是mq-deadline而写deadline也能通过别名生效。2. 如何查看与切换 IO 调度器关联文档将如何选择调度器指向 Documentation/block/switching-sched.rst其核心操作如下原文内容完整保留每个 IO 队列都有一组调度器调优参数入口位于假设 sysfs 已挂载在 /sys/sys/block/device/queue/iosched若 sysfs 未挂载# mount none /sys -t sysfs可以在运行期为某个块设备在线切换调度器可选mq-deadline、none、bfq或kyberecho SCHEDNAME /sys/block/DEV/queue/scheduler其中SCHEDNAME是已注册的调度器名DEV是设备名如sda。查看可用调度器列表及当前值# cat /sys/block/sda/queue/scheduler [mq-deadline] kyber bfq none # echo none /sys/block/sda/queue/scheduler # cat /sys/block/sda/queue/scheduler [none] mq-deadline kyber bfq选中的括号项即当前生效的调度器。切换到 mq-deadline 后其调优参数就会出现在/sys/block/device/queue/iosched/目录下。3. 调优参数全解含源码默认值与取值范围以下五个 sysfs 属性在 deadline_attrs 表中注册static const struct elv_fs_entry deadline_attrs[] { DD_ATTR(read_expire), DD_ATTR(write_expire), DD_ATTR(writes_starved), DD_ATTR(front_merges), DD_ATTR(fifo_batch), DD_ATTR(prio_aging_expire), __ATTR_NULL };3.1 read_expire毫秒文档语义当一个读请求进入 IO 调度器时它会被分配一个截止时间等于当前时间 read_expire单位毫秒。该参数用于控制读请求允许在调度器中等待的最大时长是读延迟调优的核心旋钮。源码印证默认值定义在 block/mq-deadline.c 第 30 行static const int read_expire HZ / 2; /* max time before a read is submitted. */即默认 500 msHZ 为 1000 时。请求入队时的截止时刻计算在 dd_insert_requestrq-fifo_time jiffies dd-fifo_expire[data_dir]; list_add_tail(rq-queuelist, per_prio-fifo_list[data_dir]);sysfs 写入范围STORE_JIFFIES(deadline_read_expire_store, dd-fifo_expire[DD_READ], 0, INT_MAX)第 771 行单位是毫秒内核通过msecs_to_jiffies()转换后存入因此取值范围 0INT_MAX单位 ms读取时再由jiffies_to_msecs()转回。调优含义read_expire越小读请求越快被强制派发读延迟上限越低但顺序合并窗口也被压缩取 0 相当于每个读请求立即过期。3.2 write_expire毫秒文档语义与 read_expire 相同但作用于写请求。源码印证默认值在 第 31 行static const int write_expire 5 * HZ; /* ditto for writes, these limits are SOFT! */即默认 5000 ms。注意源码注释特别标注这些限制是SOFT软限制——写队列通常按扇区顺序派发到期检查只是兜底手段。sysfs 写入范围同为 0INT_MAX msdeadline_write_expire_store第 772 行。调优含义写请求默认容忍更长的排队时间以便按扇区聚合成顺序写、提升吞吐。若你的负载对写延迟敏感如日志型写入可适当调小该值。3.3 fifo_batch请求个数文档语义完整继承原文请求被按数据方向读或写分组为若干批batch每批内按扇区递增顺序服务。为限制额外寻道deadline 的到期检查只在批与批之间进行。fifo_batch控制每批的最大请求数。This parameter tunes the balance between per-request latency and aggregate throughput. When low latency is the primary concern, smaller is better (where a value of 1 yields first-come first-served behaviour). Increasing fifo_batch generally improves throughput, at the cost of latency variation.源码印证默认值第 38 行static const int fifo_batch 16; /* # of sequential requests treated as one ... */派发时的批处理逻辑在 __dd_dispatch_request若上一次派发的方向上仍有按扇区排序的后续请求且当前批计数dd-batching dd-fifo_batch则直接继续派发该请求goto dispatch_request不重新检查到期状态rq deadline_next_request(per_prio, dd-last_dir); if (rq dd-batching dd-fifo_batch) { /* we have a next request and are still entitled to batch */ data_dir rq_data_dir(rq); goto dispatch_request; }sysfs 写入范围STORE_INT(deadline_fifo_batch_store, dd-fifo_batch, 0, INT_MAX)第 776 行。调优含义fifo_batch1时退化为严格的先来先服务每派一个请求就重新检查到期与方向调大如 16 以上的值会让顺序请求成批派发吞吐更高但单请求延迟波动变大。3.4 writes_starved派发次数文档语义完整继承原文当把请求从调度器队列移到块设备派发队列时调度器总是优先读请求。但也不希望写被无限饿死因此writes_starved控制读相对写被优先处理多少次。读被优先writes_starved次之后调度器会按与读相同的准则派发若干写。源码印证默认值第 37 行static const int writes_starved 2; /* max times reads can starve a write */派发决策__dd_dispatch_request 第 346-351 行只要读 FIFO 非空就检查写 FIFO 中请求的饿死计数dd-starved当dd-starved dd-writes_starved时跳到dispatch_writes派发写并把starved清零if (deadline_fifo_request(per_prio, DD_WRITE) (dd-starved dd-writes_starved)) goto dispatch_writes; ... dispatch_writes: dd-starved 0;sysfs 写入范围STORE_INT(deadline_writes_starved_store, dd-writes_starved, INT_MIN, INT_MAX)第 774 行即理论上可为负负值意味着读永远优先于写写只能等读队列清空。调优含义读密集且对交互延迟敏感的负载可调大读写都要求及时响应的混合负载保持默认 2 或调小。3.5 front_merges布尔值文档语义完整继承原文当一个新请求与队列中已有请求扇区相邻时既可能接在其尾部back merge也可能接在其头部front merge。由于文件的典型布局back merge 远比 front merge 常见某些负载下可以认为尝试 front merge 纯属浪费时间把front_merges设为 0 即可禁用。注意即便禁用后front merge 仍可能通过缓存的last_merge提示发生——因为该路径几乎零成本所以保留内核只是禁用了调用合并函数时的红黑树前扇区查找。源码印证该文档描述的实现细节与 dd_request_merge 完全吻合static int dd_request_merge(struct request_queue *q, struct request **rq, struct bio *bio) { struct deadline_data *dd q-elevator-elevator_data; ... if (!dd-front_merges) return ELEVATOR_NO_MERGE; __rq elv_rb_find(per_prio-sort_list[bio_data_dir(bio)], sector); if (__rq) { ... if (elv_bio_merge_ok(__rq, bio)) { *rq __rq; ... return ELEVATOR_FRONT_MERGE; } } return ELEVATOR_NO_MERGE; }front_merges为 0 时直接返回ELEVATOR_NO_MERGE跳过elv_rb_find红黑树查找省下一次 O(log n) 查找开销而 bio 层的 back merge 走blk_mq_sched_try_merge()dd_bio_merge并不受front_merges影响这与文档last_merge 提示仍保留的说明一致默认值为 1dd_init_sched 第 549 行dd-front_merges 1;sysfs 写入被限制在 01第 775 行。调优含义若负载几乎不存在头部相邻请求例如单向顺序追加写可echo 0 .../iosched/front_merges关闭前向查找换取微弱的入队路径提速。3.6 补充prio_aging_expire源码中额外暴露的参数文档未提及但源码同样通过DD_ATTR(prio_aging_expire)暴露的参数是优先级老化时间默认 10 秒第 36 行static const int prio_aging_expire 10 * HZ;。mq-deadline 按 I/O 优先级类RT/BE/IDLE见 ioprio_class_to_prio分三级派发正常派发时高优先级有请求就跳过低优先级dd_dispatch_prio_aged_requests 会检查入队超过prio_aging_expire的非 RT 请求若多个优先级都有排队则允许老化的低优先级请求被提前派发避免饿死。单位同样是毫秒SHOW_JIFFIES/STORE_JIFFIES范围 0INT_MAX。4. 请求在调度器内的组织方式理解参数为何有效从源码结构看mq-deadline 为每个优先级维护两组结构struct dd_per_priostruct dd_per_prio { struct rb_root sort_list[DD_DIR_COUNT]; /* 按扇区位置排序的红黑树读/写各一棵 */ struct list_head fifo_list[DD_DIR_COUNT]; /* 按到达顺序组织的 FIFO 链表 */ sector_t latest_pos[DD_DIR_COUNT]; /* 最近派发请求的位置作为顺序派发起点 */ struct io_stats_per_prio stats; };sort_list红黑树deadline_next_request()从latest_pos出发调用 deadline_from_pos 做树上查找找到扇区不小于上次派发位置的第一个请求——这就是批内按扇区递增服务的落地实现fifo_list到达序链表deadline_check_fifo 用链表头请求的fifo_time入队时设置的过期 jiffies判断是否已有请求到期一旦到期或方向切换、或扇区上无后续请求派发就改回 FIFO 头请求重新起一批batching 计数新批开始时dd-batching 0此后每派发一个同方向顺序请求dd-batching直到达到fifo_batch才允许重新选择方向/检查到期——这正是第 3.3 节到期检查只在批之间进行的机制来源。理解了这两层结构后五个参数的作用点就很清楚read_expire/write_expire决定 FIFO 头何时到期fifo_batch决定批内能连续顺带多少个请求writes_starved决定读/写方向切换的节奏front_merges决定入队时是否多做一次红黑树前向查找。5. 实操建议与验证方法典型调优命令以 sda 为例切换到 mq-deadline 后调参参数文件位于/sys/block/sda/queue/iosched/下# 查看当前调度器与可调参数 cat /sys/block/sda/queue/scheduler ls /sys/block/sda/queue/iosched/ cat /sys/block/sda/queue/iosched/read_expire # 单位 ms cat /sys/block/sda/queue/iosched/fifo_batch # 低延迟取向读到期 100ms批大小 1严格 FCFS关闭 front merge echo mq-deadline /sys/block/sda/queue/scheduler echo 100 /sys/block/sda/queue/iosched/read_expire echo 1 /sys/block/sda/queue/iosched/fifo_batch echo 0 /sys/block/sda/queue/iosched/front_merges # 吞吐取向默认 fifo_batch16 附近适当放大写到期 echo 5000 /sys/block/sda/queue/iosched/write_expire参数速查表默认值取自 源码常量定义范围取自 STORE 宏参数单位默认值写入范围作用read_expirems500HZ/20INT_MAX读请求最大排队时长控制读延迟上限write_expirems50005*HZ0INT_MAX写请求最大排队时长软限制fifo_batch请求数160INT_MAX批内顺序派发上限1 为 FCFS越大吞吐越高、延迟波动越大writes_starved派发次数2INT_MININT_MAX读连续优先多少次后必须派发写front_merges0/1101是否启用红黑树前向合并查找prio_aging_expirems1000010*HZ0INT_MAX低优先级请求排队多久后允许老化派发运行期观察在开启CONFIG_BLK_DEBUG_FS的内核上mq-deadline 还注册了 debugfs 队列属性deadline_queue_debugfs_attrs包括batching、starved、dispatch、各优先级的readN_fifo_list/writeN_fifo_list与queued/owned_by_driver计数可用于直观核对批处理与饿死计数是否符合预期。6. 适用前提与限制本文所有 sysfs 路径与参数语义以当前仓库 block/mq-deadline.c 的实现为准read_expire等参数单位为毫秒且读写均做 jiffies 换算默认值与 HZ 配置成正比HZ250/1000/1000 时 read_expire 分别为 2000/500/200 ms文档中deadline指 legacy deadline 调度器当前内核块层默认走 blk-mq对应实现为 mq-deadline别名 deadline二者参数语义一致但结构不同本文以 mq 版源码为证据对于 NVMe 等内部已做队列化与命令重排的设备IO 调度器的收益有限可参照 switching-sched.rst 直接选用none参数修改即时生效无需重新挂载或重启但对已在队内的请求只影响后续派发/入队行为。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻