首先,要明确一条清晰的 IO 栈路线;
其次,要了解每一个途径地点的大致用途;
最后,可以深入了解内核调用的代码路线;
文件 IO 的内核路线
IO 从用户态走系统调用进到内核,内核的路径:VFS → 文件系统 → 块层 → SCSI 层 。这里提一点,Linux 的 “文件” 的概念已经升华了,一切皆文件,网络 IO 其实进到内核也是走 VFS 。
这条路径可以完全记到心里,更深入的细节可以后续基于这个框架去补充。那么接下来我们稍微了解下这 IO 路径途径的 4 个节点分别做哪些事情?
内核 IO 路线的节点
1 VFS 层
VFS ( Virtual File System 、Virtual FileSystem Switch )层是 Linux 针对文件概念封装的一层通用逻辑,它做的事情其实非常简单,就是把所有文件系统的共性的东西抽象出来,比如 file ,inode ,dentry 等结构体,针对这些结构体抽象出通用的 api 接口,然后具体的文件系统则只需要按照接口去实现这些接口即可,在 IO 下来的时候,VFS 层使用到文件所在的文件系统的对应接口。
它的作用:为上层抽象统一的操作界面,在 IO 路径上切换不同的文件系统。
假设现在你想要写个内核文件系统,那么只需要按照 Linux 预设的一些 api 接口,实现起来就行了。
2文件系统
VFS 把 IO 给到具体的文件系统,文件系统主要做啥呢?
它的作用:对上抽象一个文件的概念,把数据按照策略存储到块设备上。
文件系统管理的是一个线性的空间(分区,块设备),而用户看到的却是文件的概念,这一层的转化就是文件系统来做的。它负责把用户的数据按照自己制定的规则存储到块设备上。比如是按照 4K 切块存,还是按照 1M 切块存储,这些都是文件系统自己说了算。
它这一层就是做了一层空间的映射转化,文件的虚拟空间到实际线性设备的映射。这层映射最关键的是 address_space 相关的接口来做。
3块层
块层其实在真实的硬件之上又抽象了一层,屏蔽不同的硬件驱动,块设备看起来就是一个线性空间而已。块层主要还是 IO 调度策略的实现,尽可能收集批量 IO 聚合下发,让 IO 尽可能的顺序,合并 IO 请求减少 IO 次数等等;
划重点:块层主要做的是 IO 调度策略的一些优化。比如最出名的电梯算法就是在这里。
因为所有的 IO 都会汇聚下来,那么在块层做调度优化是最合适的。Linux 也允许用户自行配置这里的调度策略,比如 CFQ,Deadline,NOOP 等策略。
4SCSI 层
SCSI 层这个就不用多说了,这个就是硬件的驱动而已,本质就是个翻译器。SCSI 层里面按照细分又会细分多层出来。它是给你的磁盘做最后一道程序,SCSI 层负责和磁盘硬件做转换,IO 交给它就能顺利的到达磁盘硬件。
5IO 之旅小结
基本上梳理出上面的主干,这个问题就有解了。后续的就是工作中遇到了某些问题,再针对某个问题细化研究,去查资料,去看内核代码。
比如,这里抛出来一个问题:page cache 是怎么回事?