一个IO传奇的一生-5

来源:互联网 发布:sql 求平均值 编辑:程序博客网 时间:2024/05/01 19:56

块设备层分析

无论是经过EXT3文件系统还是块设备文件,最终都要通过writeback机制将数据刷新到磁盘,除非用户在对文件进行读写的时候采用了DirectIO的方式。为了提高性能,文件系统或者是裸设备都会采用Linuxcache机制对数据读写性能进行优化,因此都会采用writeback回写机制将数据写入磁盘。

通过上述分析我们已经知道�%8ss="as3 plain">{

/* 获取块设备的请求队列 */
struct request_queue *q = bdev_get_queue(bio->bi_bdev);
/* 调用对应驱动程序的处理函数 */
q->make_request_fn(q, bio);
bio = bio_list_pop(current->bio_list);
} while(bio);
current->bio_list = NULL;/* deactivate */
}

generic_make_request函数中,最主要的操作是获取请求队列,然后调用make_request_fn方法处理bio。在Linux中一个块设备驱动通常可以分成两大类:有queue和无queue。有queue的块设备就是驱动程序提供了一个请求队列,make_request_fn方法会将bio放入请求队列中进行调度处理,调度处理的方法有CFQDeadlineNoop之分。设置请求队列的目的是考虑了磁盘介质的特性,普通磁盘介质一个最大的问题是随机读写性能很差。为了提高性能,通常的做法是聚合IO,因此在块设备层设置请求队列,对IO进行聚合操作,从而提高读写性能。关于IO scheduler的具体算法分析请见后续文章。

Linux的通用块层,提供了一个通用的请求队列压栈方法:blk_queue_bio,在老版本的Linux中为__make_request。在初始化一个有queue块设备驱动的时候,最终都会调用blk_init_allocated_queue函数对请求队列进行初始化,初始化的时候会将blk_queue_bio方法注册到q->make_request_fn。在generic_make_request转发bio请求的时候会调用q->make_request_fn,从而可以将bio压入请求队列进行IO调度。一旦bio进入请求队列之后,可以好好的休息一番,直到unplug机制对bio进行进一步处理。

另一类块设备是无queue的。无queue的块设备我们通常可以认为是一种块设备过滤驱动,这类驱动程序可以自己实现请求队列,绝大多数是没有请求队列的,直接对bio进行转发处理。这类驱动程序一个很重要的特征是需要自己实现q->make_request_fn方法。这类驱动的make_request_fn方法通常可以分成如下几个步骤:

1)根据一定规则切分bio,不同的块设备可能存在不同的块边界,因此,需要对请求bio进行边界对齐操作。

2)找到需要转发的底层块设备对象。

3)直接调用generic_make_request函数转发bio至目标设备。

因此,无queue的块设备处理过程很直观。其最重要的作用是转发bio。在Linux中,device_mapper机制就是用来转发bio的一种框架,如果需要开发bio转发处理的驱动程序,可以在device_mapper框架下开发一个target,从而快速实现一个块设备驱动。

通过上述描述,我们知道,IO通过writeback或者DirectIO的方式可以抵达块设备层。到了块设备层之后遇到了两类块设备处理方法。如果遇到无queue块设备类型,bio马上被转发到其他底层设备;如果遇到了有queue块设备类型,bio会被压入请求队列,进行合并处理,等待unplug机制的调度处理。IO曾经在page cache游玩了很长时间,大家都很高兴。所有得请求在page cache受到得待遇是相同的,大家都会比较公平得被调度走,继续下面的旅程。但是,在块设备层情况就变的复杂了,不同IO受到的待遇会有所不同,这就需要看请求队列中的io scheduler具体算法了。因此,IO旅程在块设备这一站,最为重要的核心就是io scheduler

 

本文出自 “存储之道” 博客,转载请与作者联系!

0 0
原创粉丝点击