关注了就能看到更多这么棒的文章哦~
Yet another way to configure transparent huge pages
By Daroc Alden
July 1, 2025
Gemini flash translation
https://lwn.net/Articles/1025629/
透明巨页(Transparent Huge Pages, THPs)理论上应该允许进程在不修改代码的情况下,受益于更大的页大小。虽然这确实有效,但 THP 带来的性能影响并非总是有益的。因此,对自身工作负载有特定了解的系统管理员,可能希望能够针对应用程序精细调整 THP。5 月 15 日,Usama Arif 分享了一个补丁集,该补丁集将为设置进程的 THP 默认值添加一个 prctl()
选项;该补丁集引发了关于此设置是否适合 prctl()
,以及可能有哪些替代设计方案的讨论。
该补丁集添加了三个新的 prctl()
标志。其中两个将全局启用或禁用某个进程的 THP,而第三个将恢复系统默认值。这三个标志都将在调用 fork()
和 exec()
后持久化。Arif 解释说,能够为每个进程设置独立的策略——并由父进程(例如系统管理器)配置这些策略——将有助于在同一机器上存在多种不同类型工作负载的系统。
THP 可以将许多较小的内存页组合在一起;这消除了跟踪大量页的开销,并减少了将虚拟内存地址解析为物理内存位置过程中的间接性。其代价是内部碎片(internal fragmentation)的潜在风险更大,从而导致更高的内存使用。这种权衡是否适用于某个程序,取决于可用内存量、其内存访问模式,以及系统上其他哪些作业正在竞争内存。
Lorenzo Stoakes 担心 Arif 的更改会使当前控制系统范围 THP 策略的机制变得毫无意义。在当前的内核上,有一个 sysfs 文件可用于选择 THP 是应用于所有进程、不应用于任何进程,还是仅应用于通过调用 madvise()
请求它的进程。Arif 试图向他保证,现有机制将保持不变;prctl() 的提议实际上将取代 madvise()
。Stoakes 并未释怀:
prctl()
感觉它真的、从来、都不是正确的选择。感觉我们把所有想藏起来的“黑料”都塞到那里了。
阅读手册页(man page)确实令人恐惧。[那里]有一些关于虚拟内存区域(VMAs)的内容,_我甚至都不知道_ 。
在与 David Hildenbrand 就 Arif 提案的优点进行了大量来回讨论之后,Stoakes 最终 提议创建一个全新的系统调用,该调用将涵盖当前的 madvise()
接口以及 Arif 对可配置进程范围默认值的需求。他提议的接口将是一个“结构体配置函数(struct-configured function)”,它接受一个描述所用应用程序编程接口(API)版本的新结构体,以及几种可能的操作之一。一个枚举将被用来选择是将建议应用于单个范围、多个范围,还是整个地址空间。在后一种情况下,它可以被设置为默认值。一个可选的 pidfd 可以将操作应用于一个单独的进程,并且一组布尔标志将配置该调用如何响应错误或请求范围中的空洞。
Arif 认为Stoakes 的提议没有必要,并且会“引入大量代码来解决可以用非常非常简单的方式完成的事情”。在 Arif 看来,修改单个范围、多个范围和整个进程这三种用例已经分别由madvise()
、process_madvise()
和prctl()
提供服务。Stoakes不同意,他指出了当前接口的几个问题,并称自己的提议是“修复后的madvise()
” 。其中一个抱怨是,当前的madvise()
接口在遇到第一个错误时就会停止,用户无法得知它执行到何处,也无法了解除了错误码之外的错误详细信息。他还表示,process_madvise()
接口被限制为八个独立的范围。SeongJae Park指出这似乎不是真的,但 Stoakes并未改变主意,鉴于他还有其他顾虑。
尽管存在分歧,Stoakes 还是就 Arif 的补丁集提供了大量审查意见,这些意见都被欣然接受。5 月 19 日,Arif 发布了 新版本的补丁集。同一天,Stoakes 又拿出了一个 替代补丁集,扩展了现有 madvise()
接口以支持设置进程默认值。
Stoakes 的补丁集引入了四个新的 madvise()
标志。PMADV_SKIP_ERRORS
将允许madvise()
调用在遇到错误后继续将建议应用于后续范围。PMADV_NO_ERROR_ON_UNMAPPED
是一个限制较少的版本;有了这个标志,madvise()
会在错误时停止,但不会将区域中间遇到的未映射范围视为错误。PMADV_ENTIRE_ADDRESS_SPACE
将提供的建议应用于进程的整个地址空间,通常应与PMADV_SKIP_ERRORS
一起调用,因为地址空间中的并非所有映射都必然支持给定的建议。最后,PMADV_SET_FORK_EXEC_DEFAULT
指定给定的一组建议应成为进程的默认值,并在fork()
和exec()
之间持久化。
Hildenbrand 发现 Stoakes 的方法很有趣,但他认为补丁应该引入最少数量的所需标志。具体来说,他质疑 PMADV_NO_ERROR_ON_UNMAPPED
是否真的需要作为一个独立于 PMADV_ENTIRE_ADDRESS_SPACE
的标志。Stoakes 同意让后者标志隐含前者标志,但他认为前者标志在某些情况下仍然有用。
Shakeel Butt 不喜欢这个接口的原因不同;他指出 THP 工作的最终目标是达到可以默认对所有工作负载启用它们的状态。在一个实现该目标的世界里,这些 madvise()
标志有什么用呢?他宁愿看到对 prctl()
进行临时更改,只持续到完成 THP 工作为止。
然而,Stoakes 并不认同这种对未来的展望。他指出 MADV_HUGEPAGE
和 MADV_NOHUGEPAGE
已经存在,并且出于兼容性原因,需要在 madvise()
接口中维护它们。Butt 澄清说他并非对此有异议,他只是认为 这个 选项将来可能会消失,因此不应该嵌入到 madvise()
接口中。
Stoakes 提出了 Liam Howlett 的一个非列表讨论建议:创建一个新的“命名优美”的 mmadvise()
系统调用,用于进程范围选项(其中已经有一些),从而将它们从 madvise()
接口中移除。Hildenbrand 认为这个想法值得尝试。Johannes Weiner 表示同意,但他担心如果新接口实际上只用于一项功能,可能会引入一个过于宽泛的解决方案。
Arif 希望在人们展示更多代码之前,能就设计达成一些共识,并认为在讨论中引入另一个选项只会使事情变得更加复杂。最终,Arif 和 Stoakes 都同意制作新版本的补丁集进行比较。
尽管这些补丁集尚未公布,但邮件列表上的讨论清楚表明,设置进程特定 THP 策略的用例是存在的。启用此功能的应用程序编程接口(API)的确切形式仍悬而未决,但这项功能很可能迟早会添加到内核中。临时解决方案的性质表明它可能会伴随我们一段时间。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~