4. 透明大页(THP)机制

透明大页(Transparent HugePages, THP)是 Linux 内核提供的一种自动化大页内存管理机制。与需要应用程序显式调用 mmap 并挂载 hugetlbfs 的显式大页不同,THP 对用户态进程完全透明,内核自动将符合条件的匿名内存映射升级为 2MB 的大页,从而减少 TLB 未命中、提升内存访问性能。

4.1 THP 工作原理

THP 的核心思想是在内核的缺页中断处理路径和内存规整(compaction)路径中,主动检测并合并连续的 4KB 小页为 2MB 大页。整个过程分为两个阶段:

  1. 缺页分配:当进程访问一个尚未分配的虚拟地址时,内核在分配物理页框时,优先尝试分配一个连续的 2MB 物理块(即大页)。如果当前内存规整度足够,直接返回大页映射。
  2. 后台合并:内核的 khugepaged 内核线程定期扫描进程的页表,寻找由 512 个连续 4KB 小页组成的虚拟地址范围。如果这些页面满足条件(可移动、未锁定、无特殊映射),则将其替换为一个 2MB 大页,并释放 511 个冗余页框。
关键指标:THP 的合并粒度固定为 2MB(x86_64 架构),与显式大页的 hugepagesz=2MB 一致。ARM64 架构支持 64KB 基页 + 2MB/32MB THP,但默认仍为 2MB。
THP 工作流程:缺页分配 + 后台合并 进程缺页 尝试分配 2MB 大页 成功 → 大页映射 失败 → 4KB 小页 khugepaged 扫描 检查合并条件 合并为大页 说明:khugepaged 默认每 10 秒扫描一次,每次扫描 4096 字节的虚拟地址空间

4.2 THP 的合并与拆分

THP 的合并与拆分是动态平衡的过程,直接影响内存利用率和性能:

操作 触发条件 执行者 影响
合并(collapse) khugepaged 扫描发现 512 个连续 4KB 小页可合并 khugepaged 内核线程 减少页表项数量,降低 TLB 压力;可能增加内存碎片
拆分(split) 内存压力、内存规整、madvise(MADV_NOHUGEPAGE)、页面迁移 直接回收路径或显式调用 释放大页锁,允许页面被回收或迁移;增加页表层级
性能陷阱:THP 拆分操作本身需要遍历 512 个页表项并建立独立的映射,在内存紧张时频繁拆分会导致 CPU 开销飙升。建议在数据库、实时计算等对延迟敏感的场景中评估 THP 的影响。

拆分的主要场景包括:

  • 内存回收:当内核需要回收页面时,如果遇到大页,必须先将其拆分为 4KB 小页,再逐页回收。
  • 内存规整:为了满足高阶内存分配,规整器需要移动页面,大页会阻碍移动,因此先拆分。
  • 用户干预:通过 madvise 系统调用指定 MADV_NOHUGEPAGE 标记,强制内核拆分指定区域的大页。
THP 合并与拆分状态机 4KB 小页 2MB 大页 khugepaged 合并 内存压力 / 显式拆分 拆分后释放的页框可被回收或迁移,合并后减少页表项数量

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。
THP 配置参数树 transparent_hugepage enabled defrag khugepaged 其他参数 pages_to_scan alloc_sleep_ms

4.4 THP 与显式大页的对比

在实际生产环境中,选择 THP 还是显式大页(hugetlbfs)需要根据应用场景权衡:

特性 透明大页 (THP) 显式大页 (hugetlbfs)
配置方式 内核参数,无需修改应用 需挂载 hugetlbfs,应用调用 mmap
页面大小 2MB(默认) 2MB / 1GB(可配置)
分配时机 缺页时动态分配 启动时预分配(hugepages 参数)
内存规整 可能触发规整,导致延迟抖动 预分配,无运行时规整开销
拆分风险 内存压力下自动拆分 不会拆分,始终为大页
适用场景 通用计算、桌面、Web 服务 数据库、HPC、DPDK、虚拟化
选型建议:对于延迟敏感且内存占用稳定的应用(如 MySQL、PostgreSQL),推荐使用显式大页 + madvise 模式 THP 的组合策略;对于通用服务,全局 always 模式通常足够。