Linux 的IO栈【转】
转自:https://zhuanlan.zhihu.com/p/39721251
说明
Linux的IO路径可能是Linux系统中最纷繁复杂的模块了,而它又是如此的重要,直接决定了系统的性能。之前在看Linux IO模块源码时,就对IO模块的层次比较模糊,在对IO路径上的各个模块进行了较深入的理解后,将自己的理解写出来,希望对入门者能有所帮助。
整体架构
应用程序:
这没什么好说的,通过相关系统调用(如open/read/write)发起IO请求,属于IO请求的源头;
文件系统:
应用程序的请求直接到达文件系统层。文件系统又分为VFS和具体文件系统(ext3、ext4等),VFS对应用层提供统一的访问接口,而ext3等文件系统则具体实现了这些接口。另外,为了提供IO性能,在该层还实现了诸如page cache等功能。同时,用户也可以选择绕过page cache,而是直接使用direct模式进行IO(如数据库)。
块设备层:
文件系统将IO请求打包提交给块设备层,该层会对这些IO请求作合并、排序、调度等,然后以新的格式发往更底层。在该层次上实现了多种电梯调度算法,如cfq、deadline等。
SCSI层:
块设备层将请求发往SCSI层,SCSI就开始真实处理这些IO请求,但是SCSI层又对其内部按照功能划分了不同层次:
* SCSI高层:高层驱动负责管理disk,接收块设备层发出的IO请求,打包成SCSI层可识别的命令格式,继续往下发;
* SCSI中层:中层负责通用功能,如错误处理,超时重试等;
* SCSI低层:底层负责识别物理设备,将其抽象提供给高层,同时接收高层派发的scsi命令,交给物理设备处理。
各层接口
清晰的接口能让复杂的系统变得容易理解和维护。
应用程序 => 文件系统
做开发的人可能都应该了解,通过诸如open/read/pread/write/writev等POSIX接口来调用文件系统各种功能。由于其普遍性,在这里就不一一描述接口形式了。
文件系统 => 块设备层
这里我们将文件系统当成一个整体,并不区分VFS和具体文件系统。块设备层对文件系统提供的接口为submit_bio(),接口形式如下:
void submit_bio(int rw, struct bio *bio)
文件系统向块设备提交的每个bio请求都设置了完成回调函数,记录在bio->bi_end_io。bio请求完成后,通过该字段通知文件系统。
块设备层 => SCSI上层
scsi_reuqest_fn()和struct request_queue。
老实来说,块设备层和SCSI上层之间分的没有那么清楚,耦合的稍微紧密,块设备层看到的IO请求结构是request。而SCSI层看到的IO命令则是scsi_cmnd。
每个scsi设备(如scsi disk)均维护了一个请求队列request_queue,而每个scsi设备对上层呈现的其实是一个块设备。因此,块设备和scsi设备有着天然的联系,request_queue则是连接块设备层和SCSI层的纽带。块设备层对request请求最终会派发至request_queue中。而在特定条件下通过泄流机制将request_queue中积攒的request派发至SCSI层处理。而泄流的实际处理过程就是scsi_request_fn()函数,因此说它是块设备层和SCSI上层的接口也不为过,虽然不是特别准确。在scsi_reuqest_fn内会进行request至scsi_cmnd的转换。
SCSI上层 => SCSI中间层
SCSI上层在收到块设备层发起的scsi命令后马不停蹄又将其转发至SCSI中间层。SCSI上层至SCSI中间层的接口是 scsi_dispatch_cmd
static void scsi_request_fn(struct request_queue *q) {
......
// 设置scsi命令完成回调函数
cmd->scsi_done = scsi_done;
rtn = scsi_dispatch_cmd(cmd);
......
}
SCSI中间层 => SCSI低层
SCSI中间层收到块设备层发下来的scsi_cmnd命令后,中间层作自己处理后,然后再将该命令继续往下传递,接下来该命令到了scsi底层,而传递的接口是 queuecommand()
static int scsi_dispatch_cmd(struct scsi_cmnd *cmd) {
......
rtn = host->hostt->queuecommand(host, cmd);
......
}
host为该设备所属的主机适配器结构。任何一个SCSI主机适配器都需要实现queuecommand接口。注意这个提交过程是异步的,无需等待该命令完成便直接返回。
scsi 命令完成后,会通过记录在命令内的完成函数回调上层处理,具体是cmd->scsi_done。
总结
参考资料
Linux IO协议栈框图scsi块设备驱动层处理 - CSDN博客