块IO 栈
块设备IO 栈
介绍块设备的
-
bio: bio 是通用块层IO 请求的数据结构,表示上层提交的IO 请求,一个bio 包含多个page ,这些page 必须对应磁盘上一段连续的空间。由于文件在磁盘上并不连续存放,文件IO 提交到块设备之前,极有可能被拆成多个bio 结构。 -
request: 表示块设备驱动层I/O 请求,经由I/O 调度层转换后的I/O 请求,将会发到块设备驱动层进行处理。 -
request_queue: 维护块设备驱动层I/O 请求的队列,所有的request 都插入到该队列,每个磁盘设备都只有一个queue (多个分区也只有一个) 。
这
请求处理方式
如图所示是块设备的
-
用户调用系统调用
write 写一个文件,会调到sys_write 函数; -
经过
VFS 虚拟文件系统层,调用vfs_write ,如果是缓存写方式,则写入page cache ,然后就返回,后续就是刷脏页的流程;如果是Direct I/O 的方式,就会走到do_blockdev_direct_IO 的流程; -
如果操作的设备是逻辑设备如
LVM ,MDRAID 设备等,会进入到对应内核模块的处理函数里进行一些处理,否则就直接构造bio 请求,调用submit_bio 往具体的块设备下发请求,submit_bio 函数通过generic_make_request 转发bio ,generic_make_request 是一个循环,其通过每个块设备下注册的q->make_request_fn 函数与块设备进行交互; -
请求下发到底层的块设备上,调用块设备请求处理函数
__make_request
进行处理,在这个函数中就会调用blk_queue_bio ,这个函数就是合并bio 到request 中,也就是I/O 调度器的具体实现:如果几个bio 要读写的区域是连续的,就合并到一个request ;否则就创建一个新的request ,把自己挂到这个request 下。合并bio 请求也是有限度的,如果合并后的请求超过阈值(在/sys/block/xxx/queue/max_sectors_kb 里设置), 就不能再合并成一个request 了,而会新分配一个request ; -
接下来的
I/O 操作就与具体的物理设备有关了,交由相应的块设备驱动程序进行处理,这里以scsi 设备为例说明,queue 队列的处理函数q->request_fn
对应的scsi 驱动的就是scsi_request_fn
函数,将请求构造成scsi 指令下发到scsi 设备进行处理,处理完成后就会依次调用各层的回调函数进行完成状态的一些处理,最后返回给上层用户。
request-based 和bio-based
在块设备的
-
request-based:这种处理方式下,会进行
bio 合并到request (即I/O 调度合并)的流程,最后才把请求下发到物理设备。目前使用的物理盘都是request-based 的设备; -
bio-based:在逻辑设备自己定义的
request 处理函数make_request_fn 里进行处理,然后调用generic_make_request 下发到底层设备。ramdisk 设备、大部分Device Mapper 设备、virtio-blk 都是bio-based ;
下图从
一个需要注意的地方是,