页缓存预读

Linux的预读架构如图所示,Linux内核会将它最近访问过的文件页面缓存在内存中一段时间,这个文件缓存被称为pagecache。如图1所示。一般的read()操作发生在应用程序提供的缓冲区与pagecache之间。而预读算法则负责填充这个pagecache。应用程序的读缓存一般都比较小,比如文件拷贝命令cp的读写粒度就是4KB;内核的预读算法则会以它认为更合适的大小进行预读 I/O,比如16-128KB

 


 

1 pagecache为中心的读和预读

我们分析如下情况,用户程序调用read函数对设备进行读操作,文件系统会调用相应的方法:generic_file_aio_read() -->do_generic_file_read(),在do_generic_file_read()函数中,使用find_get_page()cache中查找该page。如果没有找到,则调用page_cache_sync_readahead进行适当的预读,预读之后一般page就可以找到了。

页面标志位:PG_readahead,它是“请作异步预读”的一个提示。在每次进行新预读时,算法都会选择其中的一个新页面并标记之。预读规则为:

当读到缺失页面(missing page),进行同步预读;

当读到预读页面(PG_readahead page),进行异步预读

在读取方向上剩余页数为async_size时,进行异步预读。

 

我们来看以下例子:


 

当进程打开一个文件,想要读取第一页,而该页不在缓存中,这时内核采用同步预读page_cache_sync_readahead()读取了若干页(图中为4页),并将预读窗口中的第二页标记为PG_readahead。

进程现在继续读取接下来的各页,当读取到有PG_readahead标志的第二页时,内核触发一个异步预读操作page_cache_async_readahead(),在后台读取若干页。因为这时候缓存中还有两页可用,不必匆忙读取,所以不需要一个同步预读操作。

现在将重复这种做法。由于异步预读又将预读窗口中的一页标记为PG_readahead,在进程遇到该页时,又进行一次异步预读,以此类推。

以上说到预读若干页,那么预读窗口的最优长度到底是几页呢?预读粒度太小的话,达不到应有的性能提升效果;预读太多,又有可能载入太多程序不需要的页面,造成资源浪费。为此,Linux内核设置了一个file_ra_state数据结构,关联到每个file实例,记录每个文件的预读状态。

 

struct file_ra_state{

pgoff_tstart;                        /*预读的起始位置 */

unsigned intsize;                /*预读的页数,即预读窗口长度 */

unsigned intasync_size;        /* 阈值,在读取方向上剩余页数为该值时,启动异步预读*/

 

unsigned intra_pages;                /*预读窗口最大长度 */

intmmap_miss;                        /*Cache miss stat for mmap accesses */

loff_tprev_pos;                /*前一次读取时,最后的访问位置*/

};

用于实现预读的函数之间的关联如下图所示:

 


ondemand_readahead()函数在确定预读窗口长度之后,调用ra_submit()ra_submit()是对函数__do_page_cache_readahead()的封装,于是预读的技术性问题是由后者完成的。

get_init_ra_size()get_next_ra_size()是辅助ondemand_readahead()判断需要读入多少当前不需要的页的辅助函数。其中get_init_ra_size()根据进程请求的页数为一个文件确定最初的预读窗口长度,而get_next_ra_size()为后来的读取计算长度,即此时已经有一个先前的预读窗口存在,函数根据前一个预读窗口长度计算新的预读窗口长度。两个函数都会确保预读窗口长度不超过特定于文件的上限值。该上限值通常设置为VM_MAX_READAHEAD * 1024 /PAGE_CACHE_SIZE,在页长度为4K的系统上,相当于32页。两个函数的结果如下图所示:

 


 

不难看出,初始值一般是最近的2的整数幂的值,而新的窗口长度是原长度的两倍。

 

何时构建一个新的预读窗口?何时是顺序读取?ondemand_readahead()函数进行如下判断:

1)当前偏移量在前一个预读窗口末尾,或在触发阈值的点上时,内核识别为顺序读取,使用get_next_ra_size()为后来的读取计算长度;

2)若是随机读,则直接调用__do_page_cache_readahead()进行读取,而不破坏当前的预读状态;

3)若是遇到预读标志,则使用get_next_ra_size()来计算新的预读窗口长度;

4)如果以上情况都不是,内核则判定为对文件的第一次读取,这时用get_init_ra_size()建立一个新的预读窗口。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HDFS(Hadoop分布式文件系统)作为一种分布式文件系统,具有高可靠性、高性能和可扩展性等优势。HDFS取(HDFS prefetching)是指在数据的取操作中提前将数据入内存,以提高取性能和效率。 HDFS取的原理是通过提前将文件块中的数据加载到内存中,减少了后续取过程中的磁盘访问时间,从而加快了数据的取速度。当用户需要访问文件时,HDFS会在后台自动进行数据块的加载,将数据块从磁盘入到内存中,并将数据缓存在数据节点上。当用户真正进行取操作时,数据已经位于内存中,可以直接从内存中取,省去了磁盘访问的时间和延迟。 HDFS取可以通过多种方式来实现。一种方式是通过使用取算法来确定哪些数据块应该被先加载到内存中。取算法可以根据文件的访问模式、取频率和数据块的位置等因素来确定应该加载的数据块。另一种方式是通过调整HDFS的配置参数来实现取功能,例如增加数据节点的缓存容量和调整加载策略等。 总之,HDFS取是一种提高数据取性能和效率的技术,通过先将数据加载到内存中,减少了磁盘访问时间,从而加快数据的取速度。这对于大规模数据处理和分析等场景下的数据取操作非常有益,可以提高数据处理的效率和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值