LWN:在 4KB 内核中提供 64KB 基础页的两种不同方案 作者Jonathan Corbet2026年5月11日LSFMMBPF某些 CPU 架构能够以多种不同的基础页base-page大小运行使用较大的页大小通常可以获得更好的性能但代价是增加内存消耗。其他架构的限制则更多。在 2026 年的 Linux 存储、文件系统、内存管理和 BPF 峰会Linux Storage, Filesystem, Memory Management, and BPF Summit上内存管理分论坛的两场会议探讨了在底层内核不支持 64KB 页的情况下如何让进程以 64KB 页大小运行的选择。第一场会议重点讨论了让每个进程拥有自己的页大小而第二场会议则涉及为 x86 系统引入 64KB 页。进程级页大小使用 64KB 页可以提高性能但这样做也会产生内部碎片internal fragmentation和大量的内存浪费。这种内存使用的代价往往限制了较大基础页大小的使用。Ryan Roberts 和 Dev Jain远程提出了一项计划旨在允许进程以与系统整体不同的页大小运行试图兼顾两者的优点。Roberts 首先指出在大页系统和小页系统之间存在性能差距。通过 “随机选择的一组基准测试”使用较大的页大小可以获得 2-17% 的性能提升。但随之而来的内存消耗增加促使人们坚持使用许多架构支持的标准 4KB 页大小。某些最新处理器中提供的连续PTE支持物理上连续的页可以共享一个转译后备缓冲区 (Translation Lookaside Buffer, TLB) 条目提供了一定帮助但即便使用了该特性性能差距依然存在。性能差异的原因有很多。在软件方面较大的页大小意味着更少的页错误page fault和内核中更短的最近最少使用 (Least-Recently-Used, LRU) 列表。在硬件方面较大的页可以提高TLB的利用率运行 64KB 页的系统其TLB覆盖的内存区域是原来的 16 倍。Arm CPU 可以缓存最后一次页表遍历page-table walk的结果从而加速落在同一个页表项 (Page-Table Entry, PTE) 页内的地址转换较大的页大小增加了该缓存的覆盖范围。此外使用较大的页还会使页表更加紧凑减少它们对TLB和缓存的影响。Roberts 说目前有一些针对架构层面的工作旨在消除这种性能差距但这些工作的结果在未来几年内还无法应用。因此有理由探索在软件层面可以做些什么。一种可能性是给每个进程分配自己的页大小这样受益于大页的进程可以使用大页而不会增加系统整体的内存负担。特别是 Arm 架构支持这种概念允许内核保持 4KB 页大小同时允许单个进程以更大的页大小运行。Jain 接手介绍了提议的实现该实现分为三层。第一层是 “ABI 适配器”ABI adaptor旨在隐藏内核页大小与任何给定进程页大小之间的差异。每个进程的页大小存储在mm_struct结构中它在进程派生fork时被保留但可以通过execve()调用进行更改。各种系统调用例如mmap()将修改长度和对齐参数以匹配内核的页大小。Jain 说这项工作相当直接但ioctl()调用可能需要更多关注。ELF 加载器经过增强可以理解具有不同页大小进程的对齐需求。在各种/proc文件的实现中加入了不少技巧使得以 64KB 页运行的进程看到的结果就像是在 64KB 内核上运行一样。第二层是对内核内存管理子系统的一系列修改。事实证明用于实现透明大页 (Transparent Huge Pages, THP) 的许多代码路径可以被复用以便在 4KB 内核上为使用较大页大小的进程提供 64KB 页。对于这类进程分配请求将指定页大小作为最小可接受的分配大小更大的页直至 PMD 级的大页大小仍然是可能的。页缓存 (Page Cache) 自身也面临挑战因为它是由系统中所有进程共享的。一种选择是始终在那里使用 64KB 的 folio但在缓存小文件时这会浪费相当多的内存。因此页缓存大部分时间仍然使用 4KB 页。如果一个 64KB 进程通过mmap()映射一个文件该文件的所有 4KB folio 都将从页缓存中丢弃随后任何新的 folio 都将以较大的尺寸添加到缓存中。Kiryl Shutsemau 询问是否所有文件系统现在都支持页缓存中的大 folioMatthew Wilcox 给予了否定回答称某些文件系统是 “偷懒的家伙”lazy slackers尚未添加该支持。他说最大的问题是 Btrfs。Wilcox 建议作为丢弃页缓存条目的替代方案内核可以在不超出文件末尾的前提下直接使用 64KB folio。Lorenzo Stoakes 表示这项工作看起来相当具有侵入性并质疑为什么不能更多地利用多尺寸透明大页 (multi-size Transparent Huge Pages, mTHPs)它也能提供许多相同的好处。Roberts 回答说mTHPs 并不能提供大页所能提供的所有硬件级收益。Stoakes 还担心广泛使用较大的页大小可能会给内存管理子系统的整理 (compaction) 代码带来巨大压力。由于时间紧迫Roberts 跳过了一些预定的讨论包括第三层即处理不同尺寸页表的架构特定代码直接转到了待解决问题列表。其中第一个问题与当内核在 64KB 进程的上下文中运行时尝试执行需要 4KB 页大小的操作会发生什么有关。一种选择是让进程回退到 4KB 页这将确保功能正确性但会损失性能。另一种选择是让操作失败Roberts 说这个想法 “看起来” 更简单但需要在整个内核中散布大量的页大小检查。用户空间ABI兼容性是一个挑战内核在被 64KB 进程查询时可以假装以 64KB 页运行但它永远无法模拟所有内容。例如某些/proc文件根本无法隐藏内核正在使用 4KB 页的事实。当 64KB 进程读取/proc//PID//pagemap时也无法表示一个 4KB 进程。还有一些系统调用和其他特性例如userfaultfd()是无法模拟的。Roberts 说处理这些问题的一种方法是 “削减”defeature64KB 进程的功能。具有不同页大小的进程彼此不可见页大小大于内核页大小的进程将无法使用userfaultfd()等特性。任何无法向 64KB 进程正确表示的操作都将直接失败。Roberts 最后总结道虽然允许进程拥有不同的页大小带来了好处但也存在一些棘手的问题。添加这一特性还会给内存管理子系统带来相当大的变动。不过这些收益可能确实值得付出这些努力。x86 的 64KB 基础页大小对于受益于较大基础页的工作负载来说使用较大的基础页可能是一个不错的解决方案但存在一个小问题包括 x86 在内的一些次要架构不支持以较大的基础页大小运行。在下一场会议中Shutsemau 提出了一种在 x86 系统上绕过此限制的方法。不过这个想法遭到了在场开发者的某种程度的怀疑。Shutsemau 开始说道在 Arm 处理器上使用 64KB 基础页可以为 “一个非常重要的工作负载” 带来 1.7% 的性能提升他希望也将这种提速带到 x86 系统上。使用较大的页将减少系统内存映射memory map的内存开销允许轻松且能提升性能的TLB合并、更快的 I/O 操作以及更容易分配 1GB 的巨页。他说这样做需要将内核中系统页大小的概念一分为二。目前PAGE_SIZE宏在整个内核中用于表示硬件的基础页大小。Shutsemau 将逐步淘汰该宏转而使用PTE_SIZE描述硬件视角的基础页大小和PG_SIZE内核管理且用户空间可见的页大小。只有当PTE_SIZE和PG_SIZE相等时才会定义PAGE_SIZE宏。他说页帧号Page-frame numbers将始终引用PTE_SIZE帧。毋庸置疑内核中有许多地方需要更改以反映这种新的世界观。创建页表项将变得更加复杂因为必须考虑 (PG_SIZE) 页内的偏移量所有处理PTE的函数都将获得一个新的偏移量参数。虽然内核管理的是 64KB 页但用户空间看到的页大小仍将一如既往地是 4KB。因此在这种系统上成功运行不需要对用户空间进行任何更改。Shutsemau 说最具挑战性的部分是页错误处理因为必须为每个发生错误的页映射多个PTE。用户空间仅被维持在 4KB 的对齐要求上这意味着虚拟内存区域 (Virtual Memory Areas, VMAs) 可能在 64KB 页的中间开始或结束。因此页错误处理程序最终可能在发生错误时仅映射页的一部分在这种情况下页中未映射的部分将直接被浪费掉。未对齐的页也可能导致内存浪费。Wilcox 说在这些系统上写时复制 (Copy-on-Write, COW) 错误将变得更加昂贵因为它们必须将周围的基础页也加载进来以填满一个 64KB 页。相反David Hildenbrand 担心userfaultfd()将如何实现它可能需要一个新的操作来安装单个PTE而不是整个页。Hildenbrand 还建议最好在整个系统中统一使用 64KB 页大小他说这会让大家的生活都变得更轻松。Shutsemau 回答说这实际上只是将复杂性转移到了架构代码中架构代码必须实现一个基础页大小较大的假象并对内核其余部分隐藏细节。转向较大的基础页大小还会破坏一些应用程序。Hildenbrand 对后一点并不表示同情称这类程序要么应该被修复要么就只在 4KB 系统上运行。Jason Gunthorpe 说在 Arm 系统上已经有很多关于 64KB 页大小的经验了。他说用户往往会反对因为总会有那么一个只能在 4KB 页下运行的特殊应用程序。另一位与会者询问既然内核对 mTHPs 的支持正随着时间的推移变得越来越好为什么还需要这种复杂性。Shutsemau 说这个想法的部分问题在于并非所有文件系统都支持较大的 folio。坚持较小的基础页大小也使得系统更难分配较大的内存块。在内存浪费的话题上Hildenbrand 提出了创建 “负阶 folio”negative-order folios来表示子页sub-page内存块的可能性。还有人建议使用 slab 分配器进行子页分配但这并非在所有情况下都有效。随着会议接近尾声Shutsemau 承认他并没有看到大家对他的提议有太多热情。他询问根本的反对意见是什么。Hildenbrand 回答说在当前的内核中零阶 folio 就是单个页改变这一理解将涉及对 folio 处理方式的重大改变。他要求一种更简洁的方法一种不需要 “怪异的页部分接口”weird part-of-page interfaces就能达到预期目标的方法。Gunthorpe 说根本的限制在于必须有一种方法来运行需要 4KB 页大小的旧应用程序。最好能找到一种方法在对内核干扰最小的情况下在具有较大基础页大小的系统上解决这个问题。会议结束时Hildenbrand 表示内核中的其他工作正在解决 Shutsemau 提议更改背后的许多动机。鉴于此他建议 64KB 基础页可能并非未来正确的道路可能是更好地优化 4KB 页系统的运行。LWN 评论概述[本文引发了关于内存管理复杂性与性能收益之间权衡的讨论。]部分评论者指出内核已经拥有大 folio 机制这能以更稳健的方式提供类似收益而无需引入新的怪异特性目前的问题在于并非所有代码都已完成转换。也有人提到为了 1.7% 的性能提升而引入如此巨大的复杂性是否值得特别是在面对内存内碎片等副作用时。此外针对旧应用的兼容性问题有人提议是否可以让旧应用运行在传统虚拟机中而让现代应用原生利用大页。关于 “负阶 folio” 的提议Hildenbrand 澄清那并非他的本意可能只是在讨论过程中产生的某种构想。关注了就能看到更多这么棒的文章哦 全文完LWN 文章遵循 CC BY-SA 4.0 许可协议。欢迎分享、转载及基于现有协议再创作长按下面二维码关注关注 LWN 深度文章以及开源社区的各种新近言论