Linux 虚拟内存的演进与挑战:从 rmap 到 2026 年的 COW 上下文提案

Linux 虚拟内存(VM)子系统是操作系统中最为复杂且关键的组件之一。数十年来,随着硬件规模的扩大和工作负载的增长,用于管理物理内存和虚拟内存的机制经历了巨大的架构变革。

本文将深入探讨 Linux 内存管理中的核心挑战、历史上“逆向映射”(rmap)的引入,以及在 2026 年 Linux 存储、文件系统、内存管理和 BPF(LSFMM+BPF)峰会上提出的一项前沿提案——旨在解决长期存在的匿名内存追踪架构包袱。

第一部分:核心问题与逆向映射(rmap)的诞生

要理解现代 Linux 内存管理的发展,我们必须回到 2000 年代初期的 Linux 2.4 内核系列。

Linux 是一个虚拟内存系统。进程生成的每个虚拟地址都必须由硬件在页表(page table)中进行查找。页表项(PTE, Page Table Entry)要么将该虚拟地址映射到物理内存地址上,要么记录该页当前不存在(即触发缺页异常)。

传统虚拟内存的局限性

核心问题在于,页表是一种单向映射。已知一个进程上下文中的虚拟地址,可以轻松找到对应的物理地址。然而,在早期的原生内核中,没有简单的方法可以反向查出“究竟是哪个进程上下文中的哪一个页表项指向了某个特定的物理页”。

由于内存共享,情况变得更加复杂。例如,如果一个物理页存放的是共享 C 标准库的代码,那么可能会有数百个进程的页表项同时指向这同一个物理页。

在没有反向路径的情况下,管理物理内存的效率极低:

  • 释放内存页: 在内核释放物理页之前,它必须找到并使指向该页的每一个页表项失效。唯一的办法就是扫描每一个进程的页表,递减引用计数,直到归零。

  • 区域内存回收(Zone Reclaiming): 如果内核急需释放物理内存某个特定区域(Zone,物理内存的子结构)中的页,扫描虚拟内存是毫无用处的。内核在找到目标区域内的单个物理页之前,不得不筛查海量的无关页表项。

rmap 解决方案

2002 年,内核黑客 Rik van Riel 引入了“逆向映射”(rmap)补丁集。这个概念非常直接:创建一种数据结构,给定一个物理页,它能够返回指向该页的页表项(PTE)列表。

存放该信息最合理的地方是 struct page(系统内存映射表数组,系统中的每个物理页都对应一个这样的结构体)。Rik 的补丁在 page 结构体中增加了一个 pte_chain 成员,它指向一个由 PTE 指针组成的简单链表。

引入 rmap 后,内存管理变得极具响应性:

  • 直接修改: 释放一个页变得轻而易举,因为所有相关的页表项都可以被同时找到并修改。

  • 精准追踪页活跃度: 虚拟内存系统可以通过页的 pte_chain 链表轻松检查其“已引用(referenced)”位,以确定该页真正的活跃程度(age),从而确保系统在内存紧张时精确淘汰正确的页。

优化内存开销

天下没有免费的午餐,rmap 当然也是有代价的:内存开销。在系统中的每个页上增加一个 12 字节的指针,会导致明显的内核膨胀。

为了减轻这种影响,rmap 补丁从 struct page 中移除了专用的等待队列(wait_queue_head_t,同样占用 12 字节)。因为进程真正需要排他性(独占)等待某个页的情况并不频繁,rmap 将这一职责转交给了规模小得多的等待队列哈希表。尽管偶尔会发生哈希冲突(导致进程被稍微提前唤醒),但其带来的巨大内存节省完全弥补了这一微小的性能折损。

第二部分:文件映射内存与匿名内存——架构的分水岭

随着 rmap 的演进,Andrea Arcangeli 在 2004 年对其进行了大幅重构,内核根据被追踪内存的类型,将逆向映射分成了两种完全不同的机制:文件映射内存(File-Backed Memory)匿名内存(Anonymous Memory)

理解这一区别,就能明白为什么 rmap 的其中一侧设计得依然优雅,而另一侧却演变成了极度复杂的抽象层。

特征文件映射内存的逆向映射匿名内存的逆向映射
定义内存直接映射到磁盘上的实际文件(如二进制文件、共享库、mmap 文件)。没有磁盘文件作为支撑的内存(如进程的堆、栈、MAP_ANONYMOUS 分配)。
核心关联结构struct address_space(直接绑定到文件的 inode)。struct anon_vma(与虚拟内存区域 VMA 绑定)。
查找路线Page $\rightarrow$ address_space $\rightarrow$ 区间树(Interval Tree) $\rightarrow$ VMA $\rightarrow$ PTEPage $\rightarrow$ anon_vma 链表/树 $\rightarrow$ VMA $\rightarrow$ PTE
架构稳定性高。 文件在磁盘上的偏移量结构是静态的。即使有 100 个进程映射同一个文件,它们与文件数据的关系也是统一的。低。 深度受到 fork() 和**写时复制(COW)**的影响,导致父子进程之间产生复杂的共享拓扑关系。

为什么匿名内存本质上更难处理

因为文件映射内存有一个明确且永久的锚点(磁盘上的文件),内核可以使用高效的区间树(Interval Tree)来精确查找哪些虚拟内存区域(VMA)映射了文件的特定部分。

而匿名内存没有文件,它仅存在于 RAM 中。当进程调用 fork() 时,子进程会复制父进程的页表,并且所有的匿名页都被标记为只读,以实现写时复制(COW)。如果其中任何一个进程试图写入该页,内核就会为修改者分配一个新的物理页。

为了在多代父子进程之间追踪这个动态分裂、共享和分化的复杂物理页网络,内核不得不依赖一个被称为 anon_vma 的复杂追踪结构。经过二十年对各种边缘情况的修修补补,这个追踪系统变得极其晦涩难懂。

第三部分:2026 年 LSFMM+BPF 峰会与“COW 上下文”提案

在 2026 年的 LSFMM+BPF 峰会上,开发者 Lorenzo Stoakes 针对这一结构性技术债务发难,直言不讳地指出,由于其极高的复杂性、内存开销以及严重的性能瓶颈,内核现有的匿名页逆向映射实现是一个“极其差劲的抽象”。

VMA 级追踪的问题

目前,匿名逆向映射是在虚拟内存区域(VMA)级别追踪关系的。由于一个现代进程(尤其是在大型多线程运行时或内存映射数据库中)很容易拥有数万个 VMA,系统需要管理的内核追踪对象数量大得惊人。

此外,现有的 rmap 代码必须在整个 fork 操作期间都持有锁。这引入了极高的锁竞争,严重损害了多核处理器上多线程应用的可扩展性(scalability)。

引入“COW 上下文”

Stoakes 展示了一个处于初级形态的激进替代方案:将匿名内存的追踪从 VMA 级别提升到 mm_struct(内存描述符)级别。

mm_struct 是描述整个进程地址空间的核心数据结构。由于每个进程只有一个 mm_struct,将匿名映射追踪移至此处可以瞬间削减系统需要维护的对象数量。Stoakes 的设计引入了一个独立的结构,称为 COW 上下文(COW Context)

为什么不直接嵌入到 mm_struct 中?

COW 上下文不能简单地作为 mm_struct 内部的一个字段,因为COW 上下文的生命周期必须能够比其原始进程更长

如果一个父进程 fork 了几个子进程然后退出,父进程的 mm_struct 会被销毁。然而,子进程们会继续通过 COW 共享原始的匿名页。父进程的 COW 上下文必须继续保存在内存中,以维持这一系列 fork 操作的谱系树模型,从而允许内核准确映射出哪些残存的子进程仍在共享这些页。

提议的查找机制如何工作

  1. 每个内存页(严格来说是每个 folio)都会获得一个直接指针,指向最先映射它的进程的 COW 上下文。

  2. 当内核需要对该 folio 进行逆向映射时,它会顺着指针找到位于该进程层级结构最顶部的 COW 上下文。

  3. 随后,内核只需清晰地向下遍历 COW 上下文树,即可锁定每一个可能仍持有有效映射的进程。

为了保持查找的极速响应,补丁集实现了一种优化:动态地将 folio 链接到当前活跃层级结构中尽可能低的 COW 上下文上。

此外,这种新架构允许在读写锁保护(RCU, Read-Copy-Update)下进行查找,而不是依赖沉重的自旋锁或互斥锁。虽然转向 RCU 消除了锁竞争,但也带来了数据竞争的挑战,这意味着 Stoakes 未来要么需要引入一种具有映射粒度的锁,要么通过将释放页表的时间推迟到 RCU 宽限期(grace period)结束,来强迫系统在一定程度上容忍临时的并发冲突。

结语:前行之路

从 Rik van Riel 具有里程碑意义的 2.4 rmap 补丁,到 Lorenzo Stoakes 在 2026 年提出的宏大 COW 上下文提案,Linux 内存管理的哲学始终如一:用局部的内存开销或软件设计复杂性,来换取系统整体性能、可扩展性以及可预测性的巨大飞跃。

Stoakes 在 LSFMM+BPF 峰会上坦言,COW 上下文补丁集目前还是一个实验性的研究项目,还有很多粗糙的地方。然而,无论这一特定实现最终是并入主线内核,还是为某种混合设计铺平道路,它都代表了让 Linux 在面对下一代硬件基础设施时继续高效扩展所必经的、长期的架构重构之路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kernel_RDMA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值