Page Cache in Linux | My Note

Page Cache in Linux

Updated: Sep 21st, 2023


Page Cache是Linux Storage Stack中非常重要的一部分,该子系统使得Linux系统得以将大多数需要常常访问的文件系统元数据、硬盘文件等缓存在内存中,避免了大量的硬盘读写操作,从而显著地提高系统整体性能。

背景介绍

cache这种东西的存在,本身的意义就是作为小容量高速存储与大容量低速存储之间的桥梁,无论是CPU中的cache还是内存中的cache都是如此。这里的Page Cache,便是在内存与物理磁盘之间的性能差异之下而必然会产生的一种cache。 首先看看内存和物理磁盘之间的性能差距到底有多大。

存储设备 带宽 延时
普通HDD 120MB/s左右 1x ms
普通SSD(SATA) 550MB/s 左右 100 us
SSD (NVMe on PCIe) 1~2GB/s 左右 50 us左右
DRAM(DDR4 3200MHz) 25.7GB/s 每通道 75 ns

相关benchmark可在以下网站查询到 1. RAM: https://ram.userbenchmark.com/ 1. SSD:https://ssd.userbenchmark.com/ (没有latency/iops) 1. HDD: https://hdd.userbenchmark.com/ 1. SSD vs HDD : https://www.enterprisestorageforum.com/storage-hardware/ssd-vs-hdd.html 1. nvme ssd : https://www.anandtech.com/show/8104/intel-ssd-dc-p3700-review-the-pcie-ssd-transition-begins-with-nvme/3

通过这些数据,我们可以知道,即便是目前最快的NVMe on PCIe 的SSD固态硬盘,其延时依然在内存的百倍以上,而机械硬盘的延时更是内存的万倍以上,如果在IO的时候重复读取物理硬盘上的数据,其造成的性能损失便极为可观。因此,需要有page cache,缓存物理硬盘上的内容,减少硬盘IO的次数。 以上便是page cache的背景介绍。

Page Cache的整体框架

在更进一步阐述之前,先从全局的高度对Page Cache的构成与作用进行分析。 在Linux的磁盘I/O中,Page Cache工作在vfs与具体文件系统之间。在用户程序执行read、write操作时,通过【vfs子系统】定位到文件之后,并不是直接就调用具体的文件系统读取数据块,而是先判断需要读取的数据块是否在Page Cache中,并定位到具体的Page上。这部分的定位工作,由2-定位Page中描述的radix tree完成。

  1. 如果存在Page Cache中,则直接从Page Cache中读取。
  2. 否则在Page Cache中通过调用【内存管理】相关接口分配新page,并调用【具体文件系统】接口获取相应的数据块,填充该page。

从这部分可以看出,Page Cache工作在vfs和具体文件系统之间,并通过调用内存管理的接口对页进行管理。

Page Cache的组成要素

要想弄明白什么叫做Page Cache,我们需要对以下四个问题进行回答:

  1. 什么叫做页(Page)?页与数据块(Block)之间的关系是?
  2. 在进行IO的时候,如何判断一个页是否在该Page Cache中呢?
  3. 如果一个页不在Page Cache中,如何增加一个Page呢?
  4. 在什么条件下,Page Cache中的哪些Page需要被写回到硬盘甚至被驱逐呢?

弄明白以上4点之后,就能够对Page Cache有一个较为清晰的了解了。

1-Page是什么?

首先说一下什么是Block。 对磁盘而言,一般以“块设备”的抽象来进行描述,也就是说块设备就是由连续的数据块排列而成。在文件系统中,数据块的大小是文件系统管理的基本单位,一般是512bytes的倍数,现在比较常见的值是1KB/2KB/4KB/8KB。 与Block类似的是Page,在Linux的内存管理中,Page是内存管理的基本单位,我们可以通过下面这个命令,来了解linux系统中页的大小。我在Ubuntu 20.04中试了一下, 大小是4KB。

wyf@wyf-book:/mnt/d/0code$ getconf PAGESIZE
4096

Page Cache将Linux中的内存管理和磁盘I/O联系在了一起,因此这里也可以看到,为了方便内核的实现,Page的大小和Block的大小是有明显的倍数关系的。这样的关系在下图中显得更为直观。

no title picture

2-定位Page

Linux中可以支持多大数个TB的文件,这些文件的文件块数量过于庞大,页的数量极多,以至于顺序搜索需要大量时间才能确定某个页是否存在于Page Cache中。为了加快搜索与定位的过程,Linux内核在这里使用了一种搜索树技术。 每个文件都具备一个Inode数据结构,其中含有一个 address_space 类型的成员 i_mapping ,其中含有一个 page_tree 字段,该字段是基树(radix tree)的根。这个基树,便是在搜索中起到重要作用的搜索树。 这个搜索的过程,输入是文件偏移量,根据文件偏移量,该搜索树可以极快地确定对应的页是否在Page Cache中,若存在,则能返回对应的cache项。 搜索调用了函数 radix_tree_lookup() ,该函数的原理可以参考下图:这是 radix tree的一个示意图,该 radix tree 的分叉为4(2^2),树高为4,用来快速定位8位文件内偏移。Linux(2.6.7) 内核中的分叉为 64(2^6),树高为 6(64位系统)或者 11(32位系统),用来快速定位 32 位或者 64 位偏移,radix tree 中的每一个叶子节点指向文件内相应偏移所对应的Cache项。

no title picture

3-增加Page

如果此时需要访问的数据并不在Page Cache中,我们就需要增加一个Page,并从存储设备上读取相应的数据放入该Page了。这个增加Page的过程具体如下:

  1. 调用 add_to_page_cache() 函数,从内存中申请一个新页,并将新页的描述符插入到页高速缓存中。
  2. 调用文件系统提供的 readpage 函数,从硬盘读取一个页面的数据。

这部分似乎不需要特别多的描述? 当然需要注意的是,在实际读取的时候,会根据I/O的情况,进行预读。 ### 4-Page Cache的写回 这部分涉及两个关键的内容:

  1. 被标记为dirty的页什么时候需要被写回?

一般来说,dirty太久了的块?这个我不太清楚,好像找到的资料也说得比较少来着。

  1. cache满的时候,什么页会被优先从cache中被清出和写回。

Linux内核中文件Cache替换的具体过程是这样的:刚刚分配的Cache项链入到active_list头部,并将其状态设置为active,当内存不够需要回收Cache时,系统首先从尾部开始反向扫描active_list并将状态不是referenced的项链入到inactive_list的头部,然后系统反向扫描inactive_list,如果所扫描的项的处于合适的状态就回收该项,直到回收了足够数目的Cache项。

补充:Page Cache造成的问题及解决

由于Page Cache的存在,有一个显而易见的问题很容易被忽略:当我读取一个文件的时候,该文件已经存在Page Cache中了,但是在我调用read函数的时候我自己还必须提供一个buffer来存储读取的内容,这就造成了数据的重复存放, 另外还增加了一次内存拷贝开销。这个在link中有了一个很不错的例子进行讲解。

no title picture

为了解决这个问题,现在有一个系统调用 mmap ,这个系统调用可以让用用户直接拿到page cache对应的文件数据的指针,这样子就可以避免内存中的重复数据,提高了内存的使用效率, 同时还能够减少一次内存拷贝。

另外还有一部分程序不使用Page Cache而是用自己实现的cache机制,因此他们的IO操作中就会增加一个 O_DIRECT flag(在linux link)。

Page Cache(页高速缓存)

  1. 在页高速缓存中,确定某个页是否存在(索引页)
  2. 如何在页高速缓存中增加指定页。
  3. cache满后的回写页选择:LRU、等等

一些研究方向

  1. 更高效的cache替换算法:https://linux-mm.org/AdvancedPageReplacement

参考资料

  1. 2007《深入理解Linux源码》第三版 Chapter.15
  2. 2013《Linux内核探秘-深入解析文件系统和设备驱动的架构和设计》高剑林
  3. 写得很好!感觉就是我想写的东西! https://www.ibm.com/developerworks/cn/linux/l-cache/index.html
  4. 举了一个很好的例子 https://manybutfinite.com/post/page-cache-the-affair-between-memory-and-files/
  5. 一个可以展示文件的page cache情况的小工具:https://github.com/hoytech/vmtouch
  6. https://zhuanlan.zhihu.com/p/35277219
  7. https://qinglinmao8315.github.io/linux/2018/03/14/linux-page-cache.html
  8. https://en.wikipedia.org/wiki/Page_cache

Instead of authenticating the giscus application, you can also comment directly on GitHub.


Notes mentioning this note

There are no notes linking to this note.