引言:为什么 THP 在 2026 年依然关键
Transparent Huge Pages(THP)自 Linux 2.6.38(2011)引入以来,始终是 Linux 内存管理中最具争议又不可或缺的特性之一。在 2026 年的今天,随着 AI 推理集群的单节点内存突破数 TB、数据库工作集动辄数百 GB、以及云原生场景下容器密度不断提高,THP 已经从"可选项"变成了影响系统延迟和吞吐的决定性因素。
然而,THP 并非银弹。错误的工作模式会导致内存碎片恶化、khugepaged 占用过高 CPU、数据库出现诡异的性能抖动。本文将从内核源码级原理出发,完整梳理 THP 的工作机制、NUMA 感知策略、khugepaged 参数调优方法,并给出 PostgreSQL、Redis、PyTorch/vLLM、Java 等典型工作负载的生产级配置建议。
1. THP 架构原理:从 4KB 到 2MB/1GB
传统 x86_64 页表采用四级分页结构(PML4 → PDPT → PD → PT),每个页表项(PTE)映射 4KB 页面。当物理内存达到 128GB 时,内核需要维护 3300 万个 PTE,这带来了两类性能开销:
- TLB 压力:每级页表都有独立的 TLB,4KB 颗粒度下单次页面访问产生 4 次 TLB Miss 的概率极高
- Page Fault 开销:每个 4KB 页面必须独立触发缺页中断、完成物理页分配
大页(Huge Page)通过让一个 PMD(Page Middle Directory)或 PUD(Page Upper Directory)直接指向大颗粒物理页框,将单次映射从 4KB 提升到 2MB 甚至 1GB,显著减少 TLB Miss 次数。THP 的特殊之处在于它完全自动——无需应用修改代码、无需预分配大页池。
1.1 匿名大页 vs 文件大页
Linux 内核将 THP 分为两类,触发机制完全不同:
- Anon HugePages:匿名内存区域(堆、栈)的 THP。缺页中断 → 分配 4KB 页面 → 启动
khugepaged后台线程异步合并相邻页面 → 如果合并成功则拆除零散 PTE 并建立 PMD 大页映射 - Shmem HugePages:文件映射内存区域(tmpfs、mmap 文件、hugetlbfs)的 THP。系统在缺页时直接分配 2MB 页面(如果可用),不需要异步合并阶段,延迟更低也更可预测
关键是:anon THP 实际上是一个"先返回 4KB 页再慢慢拼凑"的异步过程,这意味着进程启动后的前几百毫秒仍使用普通页,随后逐步升级。shmem THP 则是一次性分配,行为更可预测。
1.2 khugepaged 的工作循环
/sys/kernel/mm/transparent_hugepage/khugepaged 目录控制着内核线程 khugepaged 的行为。该线程运行于 khugepaged_alloc_thread 上下文中,主循环逻辑位于 mm/khugepaged.c。
核心扫描流程如下:
// 简化后的 khugepaged 主循环逻辑
static int khugepaged(void *none)
{
set_freezable();
set_user_nice(current, MAX_NICE); // 优先级最低
while (!kthread_should_stop()) {
// 1. 控制扫描频率:默认 10 秒一次睡眠
khugepaged_scan_sleep_millisecs);
// 2. 扫描所有匿名 VMA,寻找有足够"已分配"子页面的区域
for_each_mm(mm) {
for (vma = mm->mmap; vma; vma = vma->vm_next) {
// 检查是否需要 khugepaged_scan
// - VM_NOHUGEPAGE 标记的跳过
// - MADV_HUGEPAGE 标记的优先
// - 只在 madvise 模式下需要显式标记
offset = (vma->vm_pgoff

发表评论 取消回复