且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

[深入理解文件系统之九] Linux 中页高速缓存的实现

更新时间:2021-12-19 17:56:40

    深入理解Page Cache要求对Unix/Linux系统内存管理有深入的了解。以Linux为例,在x86系统上它提供了基于三级页表和TLB的虚拟内存管理方式。实现了虚拟地址到物理地址的转换,同时为上层的内核态和用户态调用提供了统一的接口。对于内核态的调用,内核自身除了代码段、静态数据之外的内存的申请和是否而言,它提供了基于页框管理的API; 对应用户态的应用,在申请和使用内存的时候,提供了基于缺页异常的内存描述符合线性区对象的管理。具体需要阅读《深入理解Linux内核》。而和文件系统相关的一部分是内核中的线性区数据结构vm_area_struct。


  • Page Cache中的内容

Linux 系统中 page cache可以支持多种不同类型的页,比如下面的几种:

a.页中包含普通文件的数据和基于磁盘文件系统的目录;

b.特殊设备和文件基于内存映射后的页;

c.页中的数据是直接从块设备读到的数据(跳过了文件系统层@@@)

d.页中还包含已经交换到磁盘上的数据

e. IPC共享的存储区,比如share memory


  • 页缓存数据结构

上述不同类型的页,对应了在调入和替换的时候不同的操作,为此需要为不同类型的页定义和实现不同的操作,为此引入了address_space作为桥梁,保证具体不同页的操作各自能实现期望的功能,同时向上提供统一的接口。涉及到的数据结构包括: inode/address_space:


struct inode {

    …..

    struct address_space * i_mapping;

    struct address_space  i_data;

    ….

}


struct address_space {

    struct list_head clean_pages;

    struct list_head dirty_pages;

    struct list_head locked_pages;

    unsigned long nrpages;


    …...

    struct address_space_operations * a_ops; // 属主页上的操作的方法

    strcut indoe * host; //point to host node

struct vm_area_struct * i_mmap; // 指向私有的内存映射区域

    struct vm_area_struct * i_mmap_shared;  // 指向公有的内存映射区域

    …….

    int gfp_mask; // 属主页的内存分配器标志

}


可以看到上面两个数据结构可以相互指向,同时在address_space送还有一个统一的struct a_ops, 看看它里面的具体内容可以看到,它包括下面的操作方法:

. writepage(): 从页写入属主的磁盘映像

. readpage(): 从属主的磁盘映像读到页

.sync_page():启动在页上已经安排的I/O数据传送操作

.prepare_write():准备针对基于磁盘文件的写操作

.commit_write():完成磁盘文件的写操作

.bmap():从文件块索引获得逻辑块号(很重要的一个函数,在IO路径上很关键)

.flushpage():准备删除来自属的磁盘映射的页

.releasepage():由日志文件系统来准备释放页

.direct_IO():数据页的直接传送 (SDDK/nvme上应该考虑用到过,以便充分释放硬件性能)


  • 页散列表

考虑到对大文件的处理过程,页高速缓存里装了许多和这个文件相关的页,此时为了查找指定偏移的文件内容,就需要扫描长长的页描述符链表,这可能变成了一个很耗时的操作,反而影响了page cache设计的初衷。为此,引入了页散列表(hash值)来加快查找。Linux内核中用page_hash_table【】的页描述符指针散列表,以address_space对象的地址和偏移量作为index来找到页表中指定文件偏移的内容。


可以看到,其实SVR4中的实现和Linux中的实现还是很像的,都有数据结构来实现程序中的段,然后基于段进行数据的缓存。




















本文转自存储之厨51CTO博客,原文链接:http://blog.51cto.com/xiamachao/1905121 ,如需转载请自行联系原作者