首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

OCFS2读流程分析与性能特性

OCFS2文件系统读流程与其它文件系统大同小异。在用户层面并不能感知到文件系统的任何差异,通常是通过调用read函数完成读数据的操作。

1OCFS2读数据主流程

用户态的API函数会通过软中断调用内核的函数,对于read来说,最终会调用vfs_read函数。然后,该函数执行下面整个的读数据流程。根据打开文件是使用的旗标的差异,读数据流程也会有差异,比如打开文件时如果使用了O_DIRECT旗标,那么是进行直接读,会绕过文件系统缓存。

如果没有该标记,那么执行read函数的时候会进行缓存读。缓存读会先检查缓存中是否存在需要的数据,如果存在则直接返回。如果缓存中没有数据,则需要从磁盘读取数据。如图1是缓存读,且没有缓存命中情况下的主要流程。

上图中read函数是用户态的API函数。然后蓝色方块中的函数是VFS(虚拟文件系统)中的函数。最后绿色方块中的函数则是具体文件系统的函数,本文为OCFS2文件系统中的函数。可以看到,VFS和OCFS2之间存在交叉调用的情况。

上图中ocfs2_file_read_iter函数是OCFS2文件系统读数据的入口函数,所有的读请求都要经过该函数。在该函数内部会调用VFS的generic_file_read_iter函数,而该函数根据文件的属性有不同的流程,具体如图所示。

这里面的流程分为2个主要流程,如果打开文件的时候有O_DIRECT旗标,则走直接读的流程。该流程不会经过Linux缓存,而是直接从磁盘上读取数据,并返回给应用程序。如果没有该旗标,则走缓存读流程。缓存读又分为2种情况,一种是缓存有数据的情况,那么直接将缓存数据返回;另外一种是缓存没有数据的情况,则需要从磁盘读取数据后填充缓存,最后再返回。

2读文件的性能优化-缓存预读技术

我们知道对于普通机械磁盘,大块连续读肯定比小块读的性能要高的多。这就好比我们用汽车在A,B两个城市运输获取,如果每次运输一整车的效率肯定要比每次运输1/10车的效率高很多。

文件系统的缓存预读技术正是这样一种技术,它通过文件系统的IO模式,预测读数据的请求,并提前读取磁盘数据到缓存的方式来提高文件系统读取数据的性能。

比如我们在看视频的时候,视频软件通常不会直接将整个视频文件读取出来,因为这样可能太大了,也没有必要。通常的方式以一段一段的读取,并进行渲染。由于大的视频文件在磁盘上通常是顺序存储的,因此文件系统可以提前将后续的数据读取到内存,这样视频软件就可以直接从缓存取数据,从而避免由于从磁盘读取数据可能导致的播放卡顿的问题。

废话一箩筐,现在我们介绍一下Linux文件系统的预读技术。为了便于说明问题,我们以页为单位进行介绍,也就是应用读取数据的最小粒度是一个页,而底层从磁盘读取数据的最小粒度也是一个页。实际读取数据的长度总是页大小的整数倍。

预读算法的涉及的结构体(file_ra_state)如图所示,该结构体是file结构体的成员。从结构体之间的关系我们可以意识到,预读是以文件为单位进行的。该结构体各个成员的含义请参考图中英文注释。

预读分为两种模式,一种是同步预读,另外一种是异步预读。同步预读需要等待完成磁盘读取请求;异步预读不需要等待,只有请求提交即可即可返回。

其实所有文件都使用的VFS的公共特性来实现预读。如图1所示,同步预读和异步预读的起始逻辑在函数do_generic_file_read中。其中find_get_page用于查找是否有缓存并返回找到的缓存页。如果没有找到缓存页则运行红色方框的流程,进行同步预读。如果找到缓存页,并且该缓存页有预读标记则运行绿色方框的流程,进行异步预读。

对比一下同步预读和异步预读的主要调用流程,可以看出最终都会调用到__do_page_cache_readahead函数。实现逻辑的差异主要在ondemand_readahead函数中。基本差异是对滑动窗口的计算,对于同步预读由于要考虑随机读的情况,避免读过多的内容;而对于异步预读则根据达到预读标记则调整一个比较大的滑动窗口。

page_cache_sync_readahead->ondemand_readahead->__do_page_cache_readaheadpage_cache_async_readahead->ondemand_readahead->__do_page_cache_readahead

为了更加清楚的理解上述过程,我们通过如图所示的图像来解释同步预读与异步预读整个过程。其中T0-T4表示时间轴。不同颜色的方块代表不同状态的页。

预读的基本规则主要包含两个,一个是在读请求中读到缺失页面(missing page),进行同步预读;另一个是读到预读页面(PG_readahead page),则进行异步预读。如图6是预读的基本示意图。

1) T0触发同步预读,由于读取的数据在缓存中不存在,因此此时触发了同步预读。同步预读不仅仅会读取期望的数据,还会额外读取一部分数据(通过函数get_init_ra_size进行计算);

2) T1时由于读到的缓存页有预读标记,因此会触发异步预读;

3) T2时由于缓存已经存在,因此直接从缓存读取后返回;

以此类推,当再次碰到有预读标记的页时进行异步预读。另外,这里面有个概念叫“预读窗口”,预读窗口是指一次从磁盘预读数据的多少。预读窗口是滑动的,也就是大小会根据命中情况进行变化,如果命中则会变大,这样可以有效的提高读取的效率。

预读算法的基本原理就是这样的。如果想更加清晰的理解预读算法,还需要预读内核的源代码,里面大体流程是一样的,更多的是关于一些预读窗口相关数据的计算过程。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/Oy2AJ26QM9gffUijYzBsQhYw0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券
http://www.vxiaotou.com