主要描述从用户态启动文件读开始,直到磁盘驱动,linux内核代码所走过的流程。阅读者需要对linux内核的内存管理、ext系列的文件系统,块设备,页高速缓存等有一定了解,不了解也没关系,顺着代码读可能会吃力点而已,鉴于不可能将代码全贴出来,中间缺失的部分,请大家自行脑补吧。至于写文章的原因,作为一名高效的客服,电话咨询来临时,就要把链接给出来,没有现成的就自己造了。

linux内核版本号:3.0.13-0.27 sles11sp2版本。

首先看系统调用处代码:

然后系统通过vfs_read函数进入,通过vfs的文件对象操作函数read,进行操作。

其中read函数是根据不同的文件系统定义初始化不同的,其中ext3文件系统初始化如下:

read被定义为do_sync_read函数,在do_sync_read函数中初始化了iovec,kiocb结构体,将一些函数入参藏到里面,需要注意调用到里面会释放出来,注意要内外对应。此时系统进入了一个死循环,开始以一个页大小方式反复读数据,最后完成读任务。

系统调用aio_read函数,前面可知是generic_file_aio_read函数,然后进入到里面。需要注意的是,内核很多函数命名是不规范了,往往函数名不能表意,很多情况下按单词理解函数刚好反了。

generic_file_aio_read并不是异步读,函数中的一些检查操作请自动忽略,函数通过文件对象标准flag判断读文件是不是O_DIRECT形式,如果是,则进入直读模式,也就是跳过页缓存,实际代码实现上差别很大,O_DIRECT放到后面再写,先说其他的。函数则再次初始化了desc结构体,又藏了一些入参。

进入do_generic_file_read,内核通过read的偏移量ppos,获取的基树的索引index,这个时候就根据索引查找页高速缓存:

读取的页有可能不在页高速缓存中,page_cache_sync_readahead函数看是否在预读中找到,(关于文件预读机制,后面单独写),如果没有找到,就进入no_cached_page里,为准备读取的页分配缓存,然后从文件系统上读取数据。

页的磁盘读取是通过调用readpage函数完成的,由于ext3的日志处理有三种方式,所以初始化的函数也有3个,即ext3_ordered_aops、ext3_writeback_aops、ext3_journalled_aops。因为ext3默认的日志写入方式是ordered,所以后面都是以ordered为准。虽然读操作和日志没有关系。

ext3_readpage中将ext3_get_block传入给mpage_readpage,先记下了。同时在do_mpage_readpage中将ext3_get_block作为get_block传递进去。

do_mpage_readpage大致就是通过传递进来的page结构体通过

获取page对应文件的块相对位置,然后通过ext3_get_block获取磁盘的绝对块号,见块号赋值给BIO,然后提交给设备层读取,即完成了一次读文件的过程。


Linux内核读文件流程来自于OenHan

链接为:http://oenhan.com/linux-kernel-read

7 对 “Linux内核读文件流程”的想法;

  1. 请问大神,上述代码中的 PageReadahead 函数是做什么的?

    找了半天也没有找到函数定义

  2. 请问大神 linux读裸盘的流程是不是和这个不一样呢?是不是把裸盘整个空间看成一个文件了。

      1. @OENHAN 就是一个普通的硬盘 不进行格式化 也不挂载。直接通过open(“/dev/sda”,O_RDWR)这样的方式来读写

ucshell进行回复 取消回复