Linux的页面回收与反向映射机制

本文介绍了Linux操作系统中的页面回收机制,包括触发页面回收的两种方式、关键代码流程以及核心函数shrink_zone()和shrink_slab()的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

页面回收的实现

Linux 操作系统进行页面回收需要考虑的方面很多,下图列出了 Linux 操作系统进行页面回收的关键代码流程图,该图给出了实现页面回收的关键代码函数名,并说明它们之间是如何彼此链接的。

图 2. 页面回收关键代码流程图

图 2. 页面回收关键代码流程图

上文提到 Linux 中页面回收主要是通过两种方式触发的,一种是由“内存严重不足”事件触发的;一种是由后台进程 kswapd 触发的,该进程周期性地运行,一旦检测到内存不足,就会触发页面回收操作。对于第一种情况,系统会调用函数 try_to_free_pages() 去检查当前内存区域中的页面,回收那些最不常用的页面。对于第二种情况,函数 balance_pgdat() 是入口函数。

当 NUMA 上的某个节点的低内存区域调用函数 try_to_free_pages() 的时候,该函数会反复调用 shrink_zones() 以及 shrink_slab() 释放一定数目的页面,默认值是 32 个页面。如果在特定的循环次数内没有能够成功释放 32 个页面,那么页面回收会调用 OOM killer 选择并杀死一个进程,然后释放它占用的所有页面。函数 shrink_zones() 会对内存区域列表中的所有区域分别调用 shrink_zone() 函数,后者是从内存回收最近最少使用页面的入口函数。

对于定期页面检查并进行回收的入口函数 balance_pgdat() 来说,它主要调用的函数是 shrink_zone() 和 shrink_slab()。从上图中我们也可以看出,进行页面回收的两条代码路径最终汇合到函数 shrink_zone() 和函数 shrink_slab() 上。

函数 shrink_zone()

其中,shrink_zone() 函数是 Linux 操作系统实现页面回收的最核心的函数之一,它实现了对一个内存区域的页面进行回收的功能,该函数主要做了两件事情:

●将某些页面从 active 链表移到 inactive 链表,这是由函数 shrink_active_list() 实现的。

●从 inactive 链表中选定一定数目的页面,将其放到一个临时链表中,这由函数 shrink_inactive_list() 完成。该函数最终会调用 shrink_page_list() 去回收这些页面。

函数 shrink_page_list() 返回的是回收成功的页面数目。概括来说,对于可进行回收的页面,该函数主要做了这样几件事情,其代码流程图如下所示:

图 3. 函数 shrink_page_list() 实现的关键功能

图 3. 函数 shrink_page_list() 实现的关键功能

●对于匿名页面来说,在回收此类页面时,需要将其数据写入到交换区。如果尚未为该页面分配交换区槽位,则先分配一个槽位,并将该页面添加到交换缓存。同时,将相关的 page 实例加入到交换区,这样,对该页面的处理就可以跟其他已经建立映射的页面一样;

●如果该页面已经被映射到一个或者多个进程的页表项中,那么必须找到所有引用该页面的进程,并更新页表中与这些进程相关的所有页表项。在这里,Linux 2.6 操作系统会利用反向映射机制去检查哪些页表项引用了该页面,关于反向映射的内容在后边会有介绍;

●如果该页面中的数据是脏的,那么数据必须要被回写;

●释放页缓存中的干净页面。

函数 shrink_slab()

函数 shrink_slab() 是用来回收磁盘缓存所占用的页面的。Linux 操作系统并不清楚这类页面是如何使用的,所以如果希望操作系统回收磁盘缓存所占用的页面,那么必须要向操作系统内核注册 shrinker 函数,shrinker 函数会在内存较少的时候主动释放一些该磁盘缓存占用的空间。函数 shrink_slab() 会遍历 shrinker 链表,从而对所有注册了 shrinker 函数的磁盘缓存进行处理。

从实现上来看,shrinker 函数和 slab 分配器并没有固定的联系,只是当前主要是 slab 缓存使用 shrinker 函数最多。

注册 shrinker 是通过函数 set_shrinker() 实现的,解除 shrinker 注册是通过函数 remove_shrinker() 实现的。当前,Linux 操作系统中主要的 shrinker 函数有如下几种:

●shrink_dcache_memory():该 shrinker 函数负责 dentry 缓存。

●shrink_icache_memory():该 shrinker 函数负责 inode 缓存。

●mb_cache_shrink_fn():该 shrinker 函数负责用于文件系统元数据的缓存。

### Linux 中匿名页的反向映射机制Linux 内核中,匿名页面属于进程的一个匿名区域[^1]。这些页面通常不文件关联,而是由程序运行期间动态分配而来。为了有效地管理内存中的匿名页面Linux 使用了一种称为反向映射(reverse mapping)的技术。 #### 反向映射的作用 反向映射允许内核快速找到给定物理地址对应的虚拟地址以及拥有该页面的进程。这对于处理页面错误、回收内存和其他内存管理操作至关重要。通过维护从物理帧到其对应虚拟地址集合之间的双向链接,可以显著提高性能并简化某些类型的内存管理任务。 #### 实现细节 具体来说,在 Linux 的实现里: - **radix tree 结构**:用于存储和查找物理页框号(PFN) 到 `struct page` 对象的映射关系; - **page->mapping 字段**:对于匿名页面而言,此字段指向一个特殊的 anon_vma 对象而不是 inode 或 dentry 等结构体; - **anon_vma 链表**:用来连接具有相同 anon_vma 上下文的所有 vmas (Virtual Memory Areas),即使它们可能分布在不同的进程中; 当发生缺页中断时,可以通过 radix tree 查找 PFN 并访问相应的 struct page 描述符来获取更多关于这个特定页面的信息,比如它所属哪个 anon_vma 和具体的偏移量等。 ```c // 获取指定物理地址所对应的 struct page* struct page *pfn_to_page(unsigned long pfn); // 访问 page->mapping 来判断是否为匿名页 if (!PageAnon(page)) { // 不是匿名页... } else { // 是匿名页, 继续处理... } ``` 这种设计使得即使是复杂的多线程或多进程环境下的共享匿名内存也能被高效而可靠地管理和追踪。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值