4. 透明大页(THP)机制
透明大页(Transparent HugePages, THP)是 Linux 内核提供的一种自动化大页内存管理机制。与需要应用程序显式调用 mmap 并挂载 hugetlbfs 的显式大页不同,THP 对用户态进程完全透明,内核自动将符合条件的匿名内存映射升级为 2MB 的大页,从而减少 TLB 未命中、提升内存访问性能。
4.1 THP 工作原理
THP 的核心思想是在内核的缺页中断处理路径和内存规整(compaction)路径中,主动检测并合并连续的 4KB 小页为 2MB 大页。整个过程分为两个阶段:
- 缺页分配:当进程访问一个尚未分配的虚拟地址时,内核在分配物理页框时,优先尝试分配一个连续的 2MB 物理块(即大页)。如果当前内存规整度足够,直接返回大页映射。
- 后台合并:内核的
khugepaged内核线程定期扫描进程的页表,寻找由 512 个连续 4KB 小页组成的虚拟地址范围。如果这些页面满足条件(可移动、未锁定、无特殊映射),则将其替换为一个 2MB 大页,并释放 511 个冗余页框。
关键指标:THP 的合并粒度固定为 2MB(x86_64 架构),与显式大页的
hugepagesz=2MB 一致。ARM64 架构支持 64KB 基页 + 2MB/32MB THP,但默认仍为 2MB。
4.2 THP 的合并与拆分
THP 的合并与拆分是动态平衡的过程,直接影响内存利用率和性能:
| 操作 | 触发条件 | 执行者 | 影响 |
|---|---|---|---|
| 合并(collapse) | khugepaged 扫描发现 512 个连续 4KB 小页可合并 | khugepaged 内核线程 | 减少页表项数量,降低 TLB 压力;可能增加内存碎片 |
| 拆分(split) | 内存压力、内存规整、madvise(MADV_NOHUGEPAGE)、页面迁移 | 直接回收路径或显式调用 | 释放大页锁,允许页面被回收或迁移;增加页表层级 |
性能陷阱:THP 拆分操作本身需要遍历 512 个页表项并建立独立的映射,在内存紧张时频繁拆分会导致 CPU 开销飙升。建议在数据库、实时计算等对延迟敏感的场景中评估 THP 的影响。
拆分的主要场景包括:
- 内存回收:当内核需要回收页面时,如果遇到大页,必须先将其拆分为 4KB 小页,再逐页回收。
- 内存规整:为了满足高阶内存分配,规整器需要移动页面,大页会阻碍移动,因此先拆分。
- 用户干预:通过
madvise系统调用指定MADV_NOHUGEPAGE标记,强制内核拆分指定区域的大页。
4.3 THP 的配置参数
THP 的所有运行时配置集中在 /sys/kernel/mm/transparent_hugepage/ 目录下。以下是核心参数及其含义:
| 参数文件 | 可选值 | 说明 |
|---|---|---|
enabled |
always / madvise / never |
全局 THP 启用策略:always 对所有匿名映射启用;madvise 仅对指定 MADV_HUGEPAGE 的区域启用;never 完全禁用 |
defrag |
always / defer / defer+madvise / madvise / never |
控制 THP 分配时的内存规整行为:always 立即规整;defer 延迟规整;never 不规整 |
khugepaged/defrag |
0 或 1 | khugepaged 线程是否参与内存规整,1 表示在合并时主动规整 |
khugepaged/alloc_sleep_millisecs |
整数(毫秒) | khugepaged 两次扫描之间的休眠时间,默认 10000(10 秒) |
khugepaged/scan_sleep_millisecs |
整数(毫秒) | khugepaged 每次扫描后的休眠时间,默认 10000(10 秒) |
khugepaged/pages_to_scan |
整数(页数) | khugepaged 每次扫描的页数,默认 4096 |
配置示例:在数据库服务器上,通常建议设置为
madvise 模式,仅对明确标记的堆或共享内存区域启用 THP,避免全局合并带来的不可控延迟。
查看当前配置状态:
# 查看 THP 启用模式
cat /sys/kernel/mm/transparent_hugepage/enabled
# 输出示例:[always] madvise never
# 查看规整策略
cat /sys/kernel/mm/transparent_hugepage/defrag
# 输出示例:[always] defer defer+madvise madvise never
# 查看 khugepaged 扫描参数
cat /sys/kernel/mm/transparent_hugepage/khugepaged/pages_to_scan
# 输出示例:4096
临时修改配置(重启后失效):
# 禁用 THP
echo never > /sys/kernel/mm/transparent_hugepage/enabled
# 设置为 madvise 模式
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
# 关闭 THP 分配时的内存规整
echo never > /sys/kernel/mm/transparent_hugepage/defrag
持久化配置:在
/etc/default/grub 的 GRUB_CMDLINE_LINUX 中添加 transparent_hugepage=never,然后执行 grub2-mkconfig -o /boot/grub2/grub.cfg 并重启,可永久禁用 THP。
4.4 THP 与显式大页的对比
在实际生产环境中,选择 THP 还是显式大页(hugetlbfs)需要根据应用场景权衡:
| 特性 | 透明大页 (THP) | 显式大页 (hugetlbfs) |
|---|---|---|
| 配置方式 | 内核参数,无需修改应用 | 需挂载 hugetlbfs,应用调用 mmap |
| 页面大小 | 2MB(默认) | 2MB / 1GB(可配置) |
| 分配时机 | 缺页时动态分配 | 启动时预分配(hugepages 参数) |
| 内存规整 | 可能触发规整,导致延迟抖动 | 预分配,无运行时规整开销 |
| 拆分风险 | 内存压力下自动拆分 | 不会拆分,始终为大页 |
| 适用场景 | 通用计算、桌面、Web 服务 | 数据库、HPC、DPDK、虚拟化 |
选型建议:对于延迟敏感且内存占用稳定的应用(如 MySQL、PostgreSQL),推荐使用显式大页 +
madvise 模式 THP 的组合策略;对于通用服务,全局 always 模式通常足够。