关注了就能看到更多这么棒的文章哦~
Improving control over transparent huge page use
By Jonathan Corbet
August 5, 2025
Gemini flash translation
https://lwn.net/Articles/1032199/
大页(huge pages)的使用可以显著提升许多工作负载的性能,因为它能减少内核中的内存管理开销以及系统转换旁路缓冲(TLB)的压力。2011 年,随着透明大页(THP)特性在 2.6.38 内核版本中的加入,内核开始自动分配大页,从而让所有工作负载无需用户空间做出任何努力即可享受到其益处。然而,事实证明,由于内部内存碎片(internal memory fragmentation),大页的使用可能会导致某些工作负载变慢,因此 THP 特性常常被禁用。目前,两个旨在更好地调整透明大页使用方式的补丁集正在审查流程中。
多年来,内核已经演变出多种控制 THP 使用的方式;这些方式在 Documentation/admin-guide/mm/transhuge.rst 中有所描述。在全局层面,`/sys/kernel/mm/transparent_hugepage/enabled` 配置项控制着系统范围的行为。它可以设置为 "always" 或 "never",结果显而易见。该配置项还支持 "madvise" 设置,它仅对那些通过调用 madvise() 明确选择启用特定内存区域 THP 的进程启用此功能。换句话说,内核允许强制执行系统范围的策略,同时也有可能将 THP 的使用限制在应用程序明确启用了它的地方。
调整 prctl()
然而,THP 的使用还有更多的控制点,包括一系列用于 khugepaged 内核线程(它在后台将基础页构建成大页)的配置项,以及一系列内核命令行选项。另外,prctl() 也提供了 PR_SET_THP_DISABLE 选项,它允许进程禁用其整个地址空间的 THP 使用,而不受整体系统策略的影响。当开发者认为 THP 分配只会损害性能时,程序会使用此选项;例如,MariaDB 就使用了这个特性。
PR_SET_THP_DISABLE 的实现是强制性的;它将覆盖进程随后为某个地址范围启用 THP 所做的任何 madvise() 调用。以这种方式在进程范围内禁用 THP 是一种粗暴的“一刀切”做法。在某些用例中,能够普遍禁用 THP(在全局启用 THP 的系统上),同时仍能为特定内存区域启用 THP 会很有用,但内核目前不提供该选项。换句话说,进程无法请求“除非另有要求,否则不使用 THP”。
正如 David Hildenbrand 的这个补丁(Usama Arif 已将其作为 这个系列 的一部分)所描述的,人们可以考虑让 madvise() 覆盖 PR_SET_THP_DISABLE,但正如 Hildenbrand 所说,“这会大大改变现有文档中描述的语义”。此外,在某些情况下,当前的语义可能正是所期望的,因此这似乎不是一个好选择。
相反,Hildenbrand 的补丁为 PR_SET_THP_DISABLE 添加了一个新选项,名为 PR_THP_DISABLE_EXCEPT_ADVISED,它提供了新的语义。当提供此选项(作为 prctl() 的第三个参数)时,THP 将在进程范围内禁用,除非 madvise() 已被用于请求例外区域,从而解决了上述用例。这个补丁系列已经经过了几次修订,目前看来正趋于稳定。
这个新选项可能解决了眼前的问题,但它只是在复杂且相互作用的一堆选项中又增加了一个。它也没有解决 THP 问题的另一个方面。曾几何时,THP 只有一种大小:即“PMD 大小”(PMD size),在许多系统上为 2MB。多尺寸 THP(multi-size THP)的出现,允许使用许多不同大小的大页,使情况变得复杂。其结果之一可以在 `/sys/kernel/mm/transparent_hugepage` 目录中看到,该目录现在包含了各种大页尺寸的子目录。系统管理员可以使用这一系列大量的选项来控制系统范围内的特定 THP 尺寸分配,但目前无法根据特定进程的需求以尺寸粒度来调整策略。为 prctl() 和 madvise() 接口增加更多复杂性以提供这种灵活性,这种想法正变得越来越不具吸引力。
使用 BPF
在他的更改日志(changelog)末尾,Hildenbrand 承认新的 prctl() 选项并不能解决所有问题:
未来很可能会使用 BPF 或类似技术来实现更好的策略,特别是为了在选用 THP 尺寸方面做出更好的决策,但这肯定需要一段时间,因为这项工作才刚刚开始。
这项工作的当前形式可以在 Yafang Shao 的 这个补丁集 中看到。它引入了一个新的 结构体操作(struct-ops) 钩子,带有以下 BPF 回调:

如果一个 BPF 程序被附加到这个钩子,它将在进行页分配决策时被内存管理子系统调用。`mm` 参数将指向描述地址空间的 mm_struct 结构体,`tva_flags` 描述了当前上下文(特别是是否正在处理页错误),而 `order` 则是此次分配正在考虑的最大尺寸。该函数应返回一个阶数(order number),指示实际应执行的最大分配尺寸;在这种情况下,返回零将完全禁用大页。
为了做出这个决策,BPF 程序可能需要了解一些分配发生时的上下文信息。该补丁系列添加了几个新的 kfuncs,`bpf_mm_get_mem_cgroup()` 和 `bpf_mm_get_task()`,以便该程序访问相关的控制组和任务信息。邵在补丁系列中指出,未来可能需要其他辅助函数(helpers)来提供关于系统当前正在经历的内存压力信息。
该补丁系列目前已修订到第四版,但尚未达到可以进入主线(mainline)的阶段。齐彦(Zi Yan)请求了一项重要更改,他希望回调函数也能访问相关的虚拟内存区域(VMA)。邵回答说“mm 对于我们的用例来说已经足够了”,但 Hildenbrand 表示,基于虚拟内存区域的方法是必需的。
另一项顾虑,针对之前的修订版提出,是任何 BPF 回调都可能很快地被视为内核 ABI(Application Binary Interface)的组成部分而变得确定不变。考虑到目前尚未完全理解如何最好地控制 THP 使用的问题,这一点尤其不理想。正如 Hildenbrand 所说:
我与每一位内存管理人员谈及此事时,他们都认为“一旦它被广泛使用(例如,某个发行版支持它),你就无法再轻易更改这些回调函数了——它会悄无声息地变得稳定。”
这实际上是内存管理方面最大的担忧:被一个曾被承诺为“不稳定”的接口所困,但它突然不再那么不稳定了,而我们不得不支持一些在未来很可能会被改变的东西。
由于这些担忧,当前的补丁集(non-merge changeset)包含了警告,指出该接口在未来的内核版本中可能会更改甚至被移除。这与对 BPF 程序的普遍理解相符;它们通常不被视为内核稳定 ABI 的一部分。尽管如此,如果某个 BPF 接口在系统管理中被深度集成,那么更改或移除它将变得越来越困难。内存管理开发者最终可能不得不支持它多年,即使它阻碍了必要的更改。
因此,即使是那些认为 BPF 是解决 THP 使用控制问题最合乎逻辑的方式的开发者,也会以高度谨慎的态度来对待为核心内存管理决策添加 BPF 控制。几乎可以肯定,在未来的内核中将会有用 BPF 控制 THP 分配的方法,但这可能不会像有些人希望的那样快发生。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~


被折叠的 条评论
为什么被折叠?



