这是我参与「第五届青训营 」笔记创作活动的第9天
Linux 文件系统IO
本篇笔记从Linux系统内核的视角研究Linux内核是如何帮助应用程序完成磁盘IO的。
Linux IO栈主要有以下几层:
- 虚拟文件系统层:该层屏蔽了底层不同文件系统的差异性,向应用程序提供统一的文件IO接口。VFS支持三种类型的系统:基于磁盘的文件系统、网络文件系统、特殊文件系统
- PageCache层:由于磁盘和内存之间存在巨大的差异,Linux内核添加了页缓存,把磁盘抽象成一个个大小固定的页。通过将部分页缓存到内存中来提高磁盘的性能。这点和将内存分配为页以将部分内存缓存到CPU Cache上的思路是一致的。
- 映射层:映射层由多个文件系统以及块设备文件组成,主要完成计算逻辑块号和物理块号等操作。
- 通用块层:和VFS类似,屏蔽底层不同设备驱动的差异性,提供统一的、首相的通用块层API
- IO调度层:IO调度层介于通用块层和块设备驱动层之间,主要是为了减少磁盘IO次数,增大磁盘整体吞吐量。IO调度层会将多个读写请求进行排序和合并,提供多种算法来适应不同的场景。
- 块设备驱动层:提供对物理块设备(磁盘等)的驱动程序。每类物理块设备都需要对应的驱动程序
- 物理设备层:物理磁盘设备
在Linux整个IO栈中,可以看出整体都采用了封装的思想:经过内核的层层封装,抹平了不同物理设备之间的差异,提供给应用程序的则是跨越不同底层设备都通用的API。同时,栈中还加入了诸如PageCache层和IO调度层等来增加IO效率和性能。在我之前简单学习CPU时,就了解了CPU为了快速执行指令所采取的多级流水线架构和超标量架构等,在这次对IO栈的学习中也了解到了IO栈对IO性能的优化。
部分零碎知识点总结
write函数一定立即完成了写吗?
答案是否定的。内核提供的这个函数的作用只是将用户态的数据复制到内核缓冲区中。内核会在之后的合适的时间调用内核相关同步函数将数据同步到IO栈中,之后再经由IO栈的层层操作同步到磁盘中。
如何减少数据拷贝?
当我们进行普通的读写系统调用时,数据可能会在用户态和内核态之间进行多次复制。当我们的数据量较大时,多次复制产生的多余数据可能会导致内存急剧增加。因此,我们需要找到一种方法使得内核函数能够直接读写用户态缓冲区的数据。这是就可以引入mmap函数,它将IO栈中的PageCache直接映射到用户态,减少了数据的拷贝。
什么是mmap?
mmap是Linux内核提供的一个函数,它将虚拟内存空间映射到IO栈的Pagecache。在笔者的理解中来说,mmap的作用相当于嵌入式处理器中的硬件或软件设计,这些设计使得用户的程序在某一特定内存空间就可以直接访问硬件接口的寄存器。
C库标准IO函数和Linux系统IO函数的关系
C库标准IO函数是在Linux系统IO函数的基础上实现的,C库标准IO函数具有缓冲区,读写效率比直接调用系统IO函数效率在某些情况下会更高。