D-32 讲了 TLB miss 代价巨大。大页让一条 TLB 项覆盖 2MB 或 1GB——TLB miss 率可降 70-90%。但 Linux 的两种大页机制在工程上各有陷阱。
一、先看图
flowchart LR
APP[应用 mmap] --> TYPE{大页类型}
TYPE -->|HugeTLB| POOL[HugeTLB pool<br/>预留 2M/1G]
TYPE -->|THP| KHUGE[khugepaged<br/>后台合并]
TYPE -->|madvise| MADV[仅标记区域<br/>尝试 THP]
POOL --> TLB[TLB: 1 entry = 2M/1G]
KHUGE --> TLB
MADV --> TLB
KHUGE -.合并失败.-> COMPACT[compaction<br/>碎片整理]
COMPACT -.仍失败.-> FALL[fallback 4K]
classDef ok fill:#3fb95022,stroke:#3fb950,color:#adbac7;
classDef warn fill:#f0883e22,stroke:#f0883e,color:#adbac7;
class POOL,TLB ok
class COMPACT,FALL warn
二、HugeTLB(静态大页)
2.1 基础
在系统启动或运行时预留固定数量的 2MB/1GB 页:
# 启动参数
hugepagesz=2M hugepages=1024 # 预留 1024 × 2MB = 2GB
hugepagesz=1G hugepages=4 # 预留 4 × 1GB = 4GB
# 运行时
echo 1024 > /proc/sys/vm/nr_hugepages # 2MB
echo 4 > /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages # 1GB2.2 使用
// mmap
void *p = mmap(NULL, size, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB, -1, 0);
// 或 hugetlbfs
mount -t hugetlbfs none /mnt/huge
fd = open("/mnt/huge/myfile", O_CREAT|O_RDWR);
p = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);2.3 优缺点
优点:
- 保证分配成功(预留了就有)
- 不受 compaction / defrag 影响
- TLB 效果稳定
缺点:
- 需要提前规划(不够灵活)
- 预留的内存不能被其他用途使用
- 1GB 页需要连续物理内存,最好开机时预留
DPDK、QEMU/KVM、Oracle DB 是 HugeTLB 的主力用户。
三、THP(Transparent Huge Pages)
3.1 思路
自动把 4K 页合并成 2M(不需要应用改代码)。
3.2 三种模式
cat /sys/kernel/mm/transparent_hugepage/enabled
# [always] madvise never- always:所有匿名映射都尝试 THP
- madvise:只有
madvise(MADV_HUGEPAGE)标记的区域 - never:关闭
3.3 khugepaged
后台内核线程,扫描进程页表,把 512 个连续 4K 页合并成一个 2M THP。
cat /sys/kernel/mm/transparent_hugepage/khugepaged/pages_to_scan # 4096
cat /sys/kernel/mm/transparent_hugepage/khugepaged/scan_sleep_millisecs # 100003.4 defrag
缺页时如果没有 2M 连续页,要不要做 compaction?
cat /sys/kernel/mm/transparent_hugepage/defrag
# [always] defer defer+madvise madvise never- always:每次缺页都可能触发 compaction → 延迟抖动
- defer:后台整理,缺页时 fallback 4K
- madvise:只对标记区域同步 defrag
- never:不 defrag
四、THP 为什么被数据库关掉
4.1 延迟毛刺
- compaction stall:always 模式下缺页触发同步 compaction → 单次几十 ms
- khugepaged CPU:后台扫描消耗 CPU,间歇性抖动
- THP split:需要回收时把 2M 拆回 512 个 4K → 全核 TLB shootdown
4.2 内存膨胀
THP 按 2M 粒度分配——即使只用 1 个 4K 字节也要 2M → 内部碎片。
4.3 Redis / MongoDB / PostgreSQL 都建议
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag4.4 何时适合 THP
- JVM 大堆(madvise 模式 +
-XX:+UseTransparentHugePages) - HPC 数值计算(连续大数组)
- 明确知道工作集是连续大内存的场景
五、file-backed THP(5.4+)
不只匿名页——文件页也能用 THP:
tmpfs先支持- 6.x 扩展到
ext4、xfs(只读 mmap 场景) read_folio一次读 2M
好处:大文件顺序读效率提升。但生产还在渐进推广。
六、THP 与 NUMA
THP 分配需要连续 2M 物理页——当某个 node 碎片化时可能分配到远端 node,反而增加 NUMA miss。
/sys/kernel/mm/transparent_hugepage/khugepaged/max_ptes_none
控制合并时允许多少空 PTE(越大越容易合并但浪费越多)。
七、观察
# THP 统计
cat /proc/meminfo | grep -i huge
# AnonHugePages: 2048000 kB
# HugePages_Total: 1024
# HugePages_Free: 200
# khugepaged 效果
cat /sys/kernel/mm/transparent_hugepage/khugepaged/pages_collapsed
# compaction 统计
cat /proc/vmstat | grep compact
# compact_stall compact_success compact_failcompact_stall 持续增长 = THP 触发的
compaction 在拖慢系统。
八、调优建议
| 场景 | 建议 |
|---|---|
| 通用服务器 | THP=madvise, defrag=defer+madvise |
| 数据库 | THP=never |
| JVM 大堆 | THP=madvise + MADV_HUGEPAGE |
| DPDK/KVM | HugeTLB 预留 2M/1G |
| HPC | THP=always 或 HugeTLB |
九、2M vs 1G
- 2M:更灵活,compaction 能搞定,THP 默认
- 1G:只能 HugeTLB 预留(THP 不支持 1G);KVM 大内存 guest 用
1G 的好处:一条 TLB 覆盖整个 GB——对几百 GB 的 VM 或 DB 能进一步减少 TLB miss。
十、常见问题
A:HugePages_Free 不等于可用
预留页可能被 cgroup 限制。看
HugePages_Rsvd。
B:THP=always + Redis = 延迟飙升 经典踩坑。改 never + 重启 Redis。
C:1G hugepage 分配失败 启动后内存碎片化无法凑 1G 连续。只能 boot 参数预留。
D:容器内看不到 hugepages 需要 cgroup
hugetlb 控制器 + 设备挂载。
十一、小结
- HugeTLB 是静态预留、确定性强、适合 DPDK/KVM/大型 DB
- THP 是透明自动,但 defrag stall / split / 内存膨胀是数据库的敌人
- madvise 模式是折中——只给标记区域开 THP
- 大页的核心价值是 TLB 效率;代价是连续物理内存的获取难度
至此子系列 D(虚拟内存)完结,共 12 篇(29-40)。下一子系列 E:内核内存分配器(6 篇,41-46)将讲 buddy 系统、SLUB、kmalloc、vmalloc、mempool、用户态分配器。
参考文献
Documentation/admin-guide/mm/hugetlbpage.rstDocumentation/admin-guide/mm/transhuge.rstmm/khugepaged.c、mm/huge_memory.c- Corbet, J. “Transparent huge pages for filesystems.” LWN.net 2020
- Andrea Arcangeli, “Transparent Huge Pages.” KVM Forum 2009
工具
/proc/meminfo(Huge* 行)cat /sys/kernel/mm/transparent_hugepage/*perf stat -e dtlb_load_misses.*numastathugeadm
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【操作系统百科】TLB 工程
TLB 容量有限,shootdown 在多核下常是扩展性终点。本文讲 TLB 层级、PCID/ASID 命中、shootdown 代价、INVLPGB 与 arm64 TLBI 广播、lazy tlb、huge page 对 TLB 的放大效应。
【操作系统百科】内存回收
Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。
【操作系统百科】交换
swap 还值得开吗?本文讲 swap area 基础、swap cache、zram 压缩内存、zswap 前端压缩池、swappiness 的真实含义、容器里的 swap 策略,以及为什么现代 Android 全靠 zram 不靠磁盘。
【操作系统百科】Slab/SLUB 分配器
buddy 只管页粒度(4K+),内核大多数对象只有几十到几百字节。slab/SLUB 在 buddy 之上做对象级缓存。本文讲 slab 历史、SLUB 接手、SLOB 退场、kmem_cache、per-CPU cache、KASAN 集成。