Linux块设备中的IO路径及调度策略

当文件系统通过submit_bio提交IO之后,请求就进入了通用块层。通用块层会对IO进行一些预处理的动作,其目的是为了保证请求能够更加合理的发送到底层的磁盘设备,尽量保证性能最佳。这里面比较重要的就是IO调度模块。大家可能都听说过CFQ,除此之前还有DeadLine和Noop等,这些都是磁盘的调度算法。其中CFQ调度算法用的最多。
如果忽略块设备的层叠结构和各种映射,简化的结构大概有3层,如图1所示。这里的3层并非都是软件,还包含硬件。通用块层就不用多说了,这里主要完成IO的合并和调度等操作。其下是驱动层,驱动层是硬件的驱动程序,用于将IO请求转换为对硬件寄存器的操作(注:不同的块设备又有差异,必然iSCSI设备是不会有寄存器操作的)。物理设备不同该驱动层的程序就不同,比如对于SAS直连的磁盘,该驱动层的程序就是SAS驱动,而如果是FC-HBA卡连接的FC-SAN,那么这个驱动层就是FC驱动(比如Qlogic的驱动)。

图1 块设备分层

最下面一层是设备层,设备层通常是一个硬件设备。这里的硬件种类繁多,比如SAS卡、SATA卡、FC-HBA卡或者iSCSI-HBA卡等等。但有的时候又可能并不是硬件设备,比如对于iSCSI来说,该层可能是通过软件模拟的一个设备层,而其请求则是通过网卡发送到目标器端。

主要数据结构及流程

绝大多数程序都是由数据结构和算法2部分内容组成的,数据结构相当于程序的骨架,而算法则是程序的筋和肉。通过算法将数据结构关联起来,从而形成一个完整的整体。人类认识问题的规律是从具体到抽象,从简单到复杂,因此我们先从数据结构开始。理解了数据关键的数据结构,那我们就能更加容易的理解块设备IO的整个逻辑。
在块设备IO中最为关键的数据结构是request_queue,也就是请求队列。该数据结构的简图如图2所示,这个数据结构本身非常复杂,我们这里进行了简化,只保留了部分关键的成员。如图彩色部分是2个函数指针,分别用于接收请求和处理请求。

图2 请求队列数据结构

为了便于理解,我们这里举一个例子。以NBD块设备为例,在块设备初始化的时候make_request_fn被初始化为blk_queue_bio,request_fn被初始化为do_nbd_request。对于SCSI块设备而言,request_fn会被初始化为scsi_request_fn
有了上面数据结构的知识及关键成员初始化的结果,接下来我们就可以分析一下块设备的整个流程的细节。块设备请求的入口是submit_bio,经过简单的检查后调用generic_make_request进行IO的处理。

void generic_make_request(struct bio *bio)
{
    struct bio_list bio_list_on_stack;

    do {
        /*获取块设备的请求队列*/
        struct request_queue *q = bdev_get_queue(bio->bi_bdev);
        /*调用请求处理函数,本文为blk_queue_bio*/
        q->make_request_fn(q, bio);

        bio = bio_list_pop(current->bio_list);
    } while (bio);
    current->bio_list = NULL; /* deactivate */
}

由上述代码可以看出IO处理的入口函数其实是函数指针make_request_fn,而我们知道该指针实际上是函数blk_queue_bio。因此块设备的请求会由blk_queue_bio函数进行处理。

磁盘调度策略

Linux内核在设计磁盘的调度策略时提供了极大的灵活性。磁盘的调度策略以插件的注册到内核当中,也就是用户可以自由的选择磁盘的调度策略。
调度算法的思想其实非常简单,主要是通过对IO的排序、合并和批量处理来优化磁盘寻道和请求的处理时间。这里值得说明的目前的调度算法其实更多的是针对机械磁盘,因为机械磁盘磁头定位耗时占整个IO处理时间的很大比例。当然对于SSD磁盘,调度算法也有一定的帮助,这就需要针对IO的特性具体来看了。

图3 调度策略结构体

磁盘调度策略的结构体定义如图3所示,各个变量的含义也是比较明确,本文不再赘述。本文主要看一下 其中elevator_ops类型的变量ops,这个变量是调度策略具体的功能实现,任何调度算法都要实现其中某些函数。
调度策略的实现就是通过这些回调函数完成的。为了理解调度策略的函数集具体做哪些事情,本文整理了一个表格,我们先从整体上看一下每个函数具体做了哪些事情。对于调度策略来说,这里的函数并非每个都要实现,下表中只有带*的才是必须要实现的函数。

函数名称 描述
elevator_merge_fn 查询可以与bio合并的请求时被调用
elevator_merge_req_fn 当合并2个请求是被调用。当一个请求被合并到另外一个时,被合并的请求将不可见。
elevator_merged_fn 当调度程序中的请求参与合并时被调用。以deadline调度策略为例,如果其排序位置可以被改变,那么将会改变请求的位置。
elevator_allow_merge_fn 当块层确定一个bio可以安全的合并到一个已经存在的请求时被调用。如果该函数返回了内部冲突,IO调度器可能会停止合并。本函数实现了这个判断功能。
elevator_dispatch_fn* 将已经准备就绪的请求放入分发队列。一旦请求被分发,IO调度器将不能再操作该请求。
elevator_add_req_fn* 将一个新的请求添加到调度器
elevator_former_req_fn elevator_latter_req_fn 返回以磁盘顺序的某个特定请求之前或者之后的请求。块层用于判断合并的可能性。
elevator_completed_req_fn 当一个请求完成时将被调用
elevator_may_queue_fn 如果调度器允许当前上下文对新请求排队时,此函数返回
elevator_set_req_fn elevator_put_req_fn 为某个请求分配或者是否存储相关的内容
elevator_activate_req_fn 当设备驱动第一次发现一个请求的时候被调用。IO调度器通过该回调函数确定一个请求什么时候真正开始执行。
elevator_deactivate_req_fn 当设备驱动计划通过重排队的方式延迟请求时调用。
elevator_init_fn* elevator_exit_fn

简而言之,上述回调函数的功能就是判断请求是否可以被合并、执行合并和请求下发等等操作。上述回调函数比较多,而且使用场景也比较复杂,具体使用分散在调度器的很多流程中。因此,我们很难一下子介绍清楚所有的场景。为了更加直观的理解上述回调函数的作用,我们以Deadline调度策略为例进行简单的介绍。
如图4是Deadline初始化的回调函数,从图中可以看出这里并没有初始化所有的回调函数,而只初始化了16个回调函数中的9个。

图4 Deadline回调函数

我们具体分析一下函数的调用场景,前文我们介绍到elevator_merge_fn函数用于查询可以与bio合并的请求。如图5所示为整个调用栈,入口为blk_queue_bio,这个函数我们之前介绍过,它就是调度程序的入口。该函数调用elv_merge用于查找是否有可以合并的请求,并返回。而elv_merge函数调用的正式Deadline调度器提供的回调函数。完成判断后,该函数会根据实际情况返回请求(或者没有找到,不返回)和可合并的方向(例如向前合并,向后合并等),后续流程就是进行具体的合并操作了。
图5 函数调用栈

由于IO调度涉及的流程比较多,限于本文篇幅,今天就先介绍到这里。后续我们再更加深入的介绍关于IO调度的其它内容。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容

  • 知道异步IO已经很久了,但是直到最近,才真正用它来解决一下实际问题(在一个CPU密集型的应用中,有一些需要处理的数...
    CXYMichael阅读 1,677评论 0 0
  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 4,975评论 0 9
  • 硬盘物理结构 硬盘内部主要部件为磁盘盘片、传动手臂、读写磁头和主轴马达。实际数据都是写在盘片上,读写主要是通过传动...
    滩主阅读 3,661评论 0 5
  • 概述 在块设备上的操作,涉及内核中的多个组成部分,如图1所示。假设一个进程使用系统调用read()读取磁盘上的文件...
    zh_harry阅读 1,812评论 0 1
  • 【材质】 一、虾饺材质: 1、建立一个新材质,勾选颜色,在纹理通道添加燥波,设置如下: 2、勾选发光通道,在纹理添...
    DIGITALMAN阅读 2,817评论 6 23