欢迎投稿Linuxer:稿件命中获200元稿费红包+人民邮电出版社任意技术图书+读者打赏+帅酷。点击了解详情:Linuxer-"Linux开发者自己的媒体"第五月稿件和赠书名单
本文作者简介: 刘正元 (liuzhengyuan@kylinos.cn), linux内核爱好者,对内核IO子系统和内核调试工具这块比较感兴趣,向内核上游内核贡献过一些patch: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?h=v4.16&qt=grep&q=zhengyuan, 目前就职于国内一家操作系统研发公司,负责文件IO协议栈的调试调优。
deadline 简介
deadline 是linux内核通用块层中自带的四个IO电梯调度器之一,其他的三个分别为noop(no operation)、cfq(completely fair queuing)、as(anticipatory scheduling),其中noop是个空的调度器,仅仅直接转发IO请求,不提供排序和合并的功能,适合ssd和nvme等快速存储设备。
deadline 调度器由通用块层的maintainer Jens Axboe在2002年首次写出并于同期合并进内核,此后几乎没经过大的修改,历久弥坚,现在已是通用块层单队列的默认IO调度器。deadline相对与其他电梯调度器综合考虑IO请求的吞吐量和时延性,最要体现在以下三个方面:
ü 电梯性
ü 饥饿性
ü 超时性
各个性质的解析后面会具体介绍。测试数据表明,deadline在大多数多线程应用场景和数据库应用场景下表现的性能要优于cfq和as。
通用块层调度器框架
内核通用块层自带四种IO调度器,每个块设备在注册的时候都会为其指定一种调度器,块设备在运行的时候可以通过/sys接口动态调整当前块设备使用的调度器,当然如果你喜欢,也可以自己实现一种满足自己需求的IO调度器,并按照接口标准注册到通用块层。通用块层为了满足调度器的扩充,方便管理各式各样的调度器,提炼出了一个统一的框架,称之为电梯框架。每个调度器只需要需按照框架的标准实现自己调度策略的钩子函数,然后向电梯框架注册自己,后续的块设备就可以使用该注册的调度器。内核中大量使用了设计与实现分离的编程手法,熟悉内核md(multi device)子系统的开发人员应该很了解这种手法,md子系统规范出一个统一的接口,然后各个特性化的raid实现这些接口,并向md层注册自己。
电梯调度器框架所包含的接口由内核源码下的include/linux/elevator.h中的struct elevator_ops结构体定义,每个调度器根据自己的特性只需要实现其中部分接口即可。其中最基本的接口有四个:
l elevator_init_fn: 调度器初始化接口,注册时被调用,用于初始化调度器的私有数据结构;
l elevator_exit_fn: 调度器退出接口,解注册时被调用,用于释放申请的数据结构;
l elevator_add_req_fn: 调度器入口函数,用于向调度器中添加IO请求;
l elevator_dispatch_fn: 调度器出口函数,用于将调度器内的IO请求派发给设备驱动;
deadline 实现原理
deadline调度器实现的接口有:
static struct elevator_type iosched_deadline = {
.ops.sq = {
.elevator_merge_fn = deadline_merge,
.elevator_merged_fn = deadline_merged_request,
.elevator_merge_req_fn = deadline_merged_requests,
.elevator_dispatch_fn = deadline_dispatch_requests,
.elevator_add_req_fn = deadline_add_request,
.elevator_former_req_fn = elv_rb_former_request,
.elevator_latter_req_fn = elv_rb_latter_request,
.elevator_init_fn = deadline_init_queue,
.elevator_exit_fn = deadline_exit_queue,
},
.elevator_attrs = deadline_attrs,
.elevator_name = "deadline",
.elevator_owner = TH