Linux内核学习轨迹第五部: 大页HugePage与透明大页THP (第十三节)
13. 大页HugePage与透明大页THP大页HugePage是Linux内核的内存优化机制使用更大的页大小比如2MB、1GB替代默认的4KB页减少页表项的数量降低TLBTranslation Lookaside Buffer翻译后备缓冲器miss的概率提升内存访问性能。透明大页THPTransparent HugePage是大页的自动化版本内核会自动把进程的虚拟内存区域合并为大页不需要用户态手动配置使用更方便但也存在一些性能问题。很多工程师对大页的认知停留在“提升性能”的表层却不理解大页的底层实现、TLB的工作原理、大页与THP的区别、大页的性能优化最终导致大页配置不当性能反而下降。本章节基于Linux 6.6 LTS内核完整拆解大页的核心设计、TLB的工作原理、大页与THP的实现、工程调优最佳实践。13.1 大页的核心设计目标减少页表项的数量默认4KB页1GB的内存需要262144个页表项2MB大页1GB的内存只需要512个页表项1GB大页1GB的内存只需要1个页表项页表项的数量减少了几个数量级降低TLB miss的概率TLB是CPU的高速缓存用于缓存虚拟地址到物理地址的映射TLB的容量很小比如Intel Xeon Scalable处理器的L1 TLB只有64个4KB页项L2 TLB只有1536个4KB页项大页可以让TLB缓存更多的内存映射降低TLB miss的概率提升内存访问性能TLB miss的延迟是TLB hit的几十倍降低TLB miss的概率可以显著提升内存访问性能尤其是内存密集型的应用比如数据库、大数据计算、科学计算减少页表的内存占用页表项的数量减少了几个数量级页表的内存占用也减少了几个数量级节省了物理内存。13.2 TLB的工作原理TLB是CPU的高速缓存用于缓存虚拟地址到物理地址的映射是内存访问性能的核心瓶颈工作原理TLB hit当CPU访问一个虚拟地址时首先在TLB中查找对应的物理地址如果找到直接访问物理内存延迟极低几个CPU周期TLB miss如果TLB中没有找到对应的物理地址CPU会遍历进程的页表找到对应的物理地址把映射关系缓存到TLB中然后访问物理内存延迟很高几十个CPU周期TLB flush当进程的页表发生变化时比如fork、exec、munmap、COW内核会刷新TLB清除旧的映射关系避免内存访问错误TLB的层级现代CPU通常有L1 TLB和L2 TLBL1 TLB的容量很小但访问速度极快L2 TLB的容量较大但访问速度略慢。大页可以显著提升TLB的命中率假设一个应用需要访问1GB的内存使用4KB页需要262144个页表项L2 TLB只有1536个4KB页项TLB命中率只有0.58%使用2MB大页需要512个页表项L2 TLB可以完全缓存TLB命中率接近100%内存访问性能可以提升30%~200%。13.3 静态大页HugePage的实现静态大页HugePage是用户态手动配置的大页需要提前预留不能动态分配性能稳定是生产环境的首选。13.3.1 静态大页的核心数据结构静态大页的核心锚点是struct hstate每个大页大小对应一个hstate实例定义在include/linux/hugetlb.h中核心字段拆解struct hstate { // 大页的大小单位字节 unsigned long huge_page_size; // 大页的大小单位页4KB unsigned long huge_pages; // 预留的大页数量 unsigned long nr_huge_pages; // 空闲的大页数量 unsigned long free_huge_pages; // 预留的大页数量用于特定的进程组 unsigned long nr_overcommit_huge_pages; // 大页的锁 spinlock_t lock; // 大页的链表 struct list_head hugepage_freelists[MAX_NUMNODES]; // 大页的页表操作函数集 const struct hugetlbfs_ops *hugetlbfs_ops; };核心字段深度解析huge_page_size大页的大小x86_64架构支持两种大页大小2MB默认和1GBr_huge_pages预留的大页数量用户可以通过/sys/kernel/mm/hugepages/hugepages-/nr_hugepages设置free_huge_pages空闲的大页数量用户可以通过/sys/kernel/mm/hugepages/hugepages-/free_hugepages查看hugepage_freelists每个NUMA节点的大页空闲链表大页分配时优先从本地NUMA节点分配。13.3.2 静态大页的配置与使用1.配置静态大页2MB# 1. 临时配置重启后失效预留1024个2MB大页共2GB echo 1024 /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages # 2. 永久配置编辑/etc/default/grub添加以下内核启动参数 GRUB_CMDLINE_LINUXdefault_hugepagesz2M hugepagesz2M hugepages1024 # 3. 更新grub配置 grub2-mkconfig -o /boot/grub2/grub.cfg # 4. 重启系统 reboot2.配置静态大页1GB# 1. 检查CPU是否支持1GB大页 cat /proc/cpuinfo | grep pdpe1gb # 2. 临时配置重启后失效预留4个1GB大页共4GB echo 4 /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages # 3. 永久配置编辑/etc/default/grub添加以下内核启动参数 GRUB_CMDLINE_LINUXdefault_hugepagesz1G hugepagesz1G hugepages4 # 4. 更新grub配置重启系统3.查看静态大页的配置# 查看所有大页大小的配置 grep Huge /proc/meminfo # 查看指定大页大小的配置 ls -l /sys/kernel/mm/hugepages/ cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages cat /sys/kernel/mm/hugepages/hugepages-2048kB/free_hugepages4.使用静态大页用户态可以通过两种方式使用静态大页通过hugetlbfs文件系统# 1. 挂载hugetlbfs文件系统 mkdir /mnt/hugepages mount -t hugetlbfs hugetlbfs /mnt/hugepages # 2. 永久挂载编辑/etc/fstab添加以下行 hugetlbfs /mnt/hugepages hugetlbfs defaults 0 0 # 3. 用户态程序通过mmap映射hugetlbfs文件使用大页通过mmap的MAP_HUGETLB标志// 用户态程序通过mmap分配大页 void *addr mmap( NULL, // 期望的映射起始地址NULL由内核自动分配 2 * 1024 * 1024, // 映射的大小2MB PROT_READ | PROT_WRITE, // 映射的权限 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, // 映射类型私有、匿名、大页 -1, // 文件描述符匿名映射为-1 0 // 文件偏移匿名映射为0 );13.4 透明大页THP的实现透明大页THPTransparent HugePage是大页的自动化版本内核会自动把进程的虚拟内存区域合并为大页不需要用户态手动配置使用更方便但也存在一些性能问题比如内存碎片、内存抖动、延迟增加。13.4.1 THP的核心配置THP的行为由/sys/kernel/mm/transparent_hugepage/下的参数控制核心参数参数名默认值核心含义调优场景enabledalways是否启用THP可选值always总是启用、madvise仅对使用madvise(MADV_HUGEPAGE)的区域启用、never禁用生产环境数据库等延迟敏感的服务设置为madvise或never避免THP导致的内存碎片和延迟增加内存密集型的应用比如大数据计算、科学计算设置为always自动使用大页defragalwaysTHP的碎片整理策略可选值always总是整理、defer延迟整理、defermadvise仅对使用madvise(MADV_HUGEPAGE)的区域延迟整理、madvise仅对使用madvise(MADV_HUGEPAGE)的区域整理、never禁用整理生产环境设置为defer或never避免THP碎片整理导致的系统卡顿内存密集型的应用设置为always自动整理碎片use_zero_page1是否使用大零页1使用0不使用生产环境设置为1使用大零页提升内存分配性能测试环境设置为0调试内存问题hpage_pmd_size2097152THP的大页大小单位字节默认2MB生产环境保持默认值2MB如果CPU支持1GB大页且应用需要访问大量内存可以设置为1GB13.4.2 THP的工程实践与避坑1.THP的优点使用方便不需要用户态手动配置自动合并虚拟内存区域为大页提升内存访问性能支持大零页提升内存分配性能。2.THP的缺点可能导致内存碎片无法分配大页碎片整理可能导致系统卡顿可能导致内存抖动THP的换入换出开销很大延迟增加THP的分配和合并需要时间。3.最佳实践生产环境数据库等延迟敏感的服务禁用THP或设置为madvise仅对使用madvise(MADV_HUGEPAGE)的区域启用内存密集型的应用比如大数据计算、科学计算设置为always自动使用大页如果使用THP设置defrag为defer或never避免碎片整理导致的系统卡顿监控THP的使用情况用grep AnonHugePages /proc/meminfo查看THP的匿名页占用用cat /sys/kernel/mm/transparent_hugepage/enabled查看THP的配置。13.5 工程实践与避坑指南1.大页的性能验证启用大页后可以通过以下工具验证性能提升用perf stat -e dTLB-load-misses,dTLB-store-misses,iTLB-load-misses ./your_program跟踪TLB miss的次数启用大页后TLB miss的次数应该减少30%~200%用top → 按f → 勾选nDRTTLB miss的次数查看进程的TLB miss次数用vmstat 1查看pgfault缺页异常的次数启用大页后缺页异常的次数应该减少几个数量级测试应用的吞吐量和延迟启用大页后吞吐量应该提升30%~200%延迟应该降低30%~80%。2.大页的选型1. 静态大页 vs 透明大页特性静态大页透明大页性能稳定最高略低可能有波动配置复杂需要提前预留简单不需要手动配置内存碎片无提前预留可能有需要整理系统卡顿无可能有碎片整理时适用场景生产环境延迟敏感的服务测试环境内存密集型的应用最佳实践生产环境优先使用静态大页性能稳定无内存碎片无系统卡顿测试环境可以使用透明大页配置简单。2. 2MB大页 vs 1GB大页特性2MB大页1GB大页TLB命中率高极高内存占用小大灵活性高预留小数量的大页也能使用低需要预留大数量的大页CPU支持所有x86_64 CPU仅部分x86_64 CPU需要pdpe1gb标志适用场景绝大多数场景内存密集型的应用需要访问几十GB以上的内存最佳实践绝大多数场景使用2MB大页TLB命中率已经很高灵活性好如果CPU支持1GB大页且应用需要访问几十GB以上的内存可以使用1GB大页。大页的避坑指南不要预留过多的静态大页预留过多的静态大页会导致系统的可用物理内存不足触发OOM建议预留应用需要的大页数量10%的缓冲不要在NUMA节点之间分配大页大页分配时优先从本地NUMA节点分配跨NUMA节点分配大页会导致内存访问延迟增加不要在使用大页的应用中使用swap大页的换入换出开销很大会导致系统出现严重的swap抖动不要在生产环境频繁调整大页的数量频繁调整大页的数量会导致系统的内存管理出现混乱甚至触发OOM不要忽略大页的监控用grep Huge /proc/meminfo监控大页的使用情况提前发现大页不足的问题预留更多的大页。