取值 | 含义 | 设置命令 |
0 | 不使用大页 | export GLIBC_TUNABLES=glibc.malloc.hugetlb=0 |
1 | 使用透明大页(THP) | export GLIBC_TUNABLES=glibc.malloc.hugetlb=1 |
2 | 使用系统默认的大页 | export GLIBC_TUNABLES=glibc.malloc.hugetlb=2 |
x(x > 2) | 指定页大小为 x(需与系统支持的页面大小匹配) | export GLIBC_TUNABLES=glibc.malloc.hugetlb=x |
cat /sys/kernel/mm/transparent_hugepage/enabled
always [madvise] never
模式 | 说明 |
always | 总是使用透明大页 |
madvise | 根据应用程序的建议使用透明大页 |
never | 从不使用透明大页 |
always,则表示整个系统的策略都是优先考虑透明大页,相当于默认支持了此特性,如果可以接受此设置,则本小节可以直接跳过。如果设置了 never,则需要使用如下命令调整:echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
export GLIBC_TUNABLES=glibc.malloc.hugetlb=1
/proc/self/smaps 中 AnonHugePages 字段,其值非 0 即表示该进程已使用透明大页,说明透明大页已生效。cat /proc/meminfo | grep Hugepagesize
Hugepagesize: 2048 kB
hugepages-2048kB 对应的 nr_hugepages:ls /sys/kernel/mm/hugepages
hugepages-1048576kB hugepages-2048kB
echo 520 > /sys/kernel/mm/hugepages/hugepages-2048kB
export GLIBC_TUNABLES=glibc.malloc.hugetlb=2
MMUPageSize: 2048 kBPrivate_Hugetlb: 1050624 kB...MMUPageSize: 2048 kBPrivate_Hugetlb: 2048 kB
MMUPageSize(Memory Management Unit Page Size,内存管理单元页大小)为 2048 kB(2M),符合 设置 nr_hugepages 一节中的设置;Private_Hugetlb 有两块,1050624kB 和 2048kB:malloc 通过 chunk 管理内存,除了提供 1G 内存之外,还需要预留 chunk 头来管理这块内存,由于此时是 2M 对齐,因此多出了这部分。malloc 在初次调用时会给 tcache 的数据结构申请空间,又由于 2M 对齐,导致多出了这部分。malloc & memset;仅预留 1G 并不够,因为除了 1G 内存本身,还需要为 chunk 头和 tcache 额外预留 2M 对齐的空间。ls /sys/kernel/mm/hugepages
hugepages-1048576kB hugepages-2048kB
echo 3 > /sys/kernel/mm/hugepages/hugepages-1048576kB
malloc & memset。export GLIBC_TUNABLES=glibc.malloc.hugetlb=1073741824
MMUPageSize: 1048576 kBPrivate_Hugetlb: 2097152 kB...MMUPageSize: 1048576 kBPrivate_Hugetlb: 1048576 kB
MMUPageSize(Memory Management Unit Page Size,内存管理单元页大小)为 1048576 kB(1G),符合 设置 nr_hugepages 一节中的设置;Private_Hugetlb 有两块,2097152kB 和 1048576kB:malloc 还是会优先考虑从这里切内存出去。但是从上例也可以看出大页的弊端,就是使用不当会造成极大的内存浪费,需要开发人员认真规划内存的使用。/proc/<PID>/smaps 输出,判断大页使用情况。test_hugepage.c:#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#define MEM_SIZE (1UL << 30) /* 1GB */int main(){char *p = malloc(MEM_SIZE);if (!p) {perror("malloc failed");return 1;}printf("PID: %d\\n", getpid());/* 初始化内存,触发大页分配 */memset(p, 0, MEM_SIZE);printf("已分配并初始化 %lu MB 内存,按 Enter 键退出...\\n", MEM_SIZE >> 20);getchar();free(p);return 0;}
gcc -o test_hugepage test_hugepage.c
./test_hugepage
cat /proc/<PID>/smaps | grep -E "AnonHugePages|Private_Hugetlb|MMUPageSize"
大页类型 | smaps 字段 | 非零含义 |
透明大页(hugetlb=1) | AnonHugePages | 该进程已使用透明大页 |
普通大页(hugetlb=2 或自定义值) | Private_Hugetlb | 该进程已使用普通大页 |
AnonHugePages 值非 0 表示透明大页已生效;对于普通大页,Private_Hugetlb 值非 0 且 MMUPageSize 与配置的大页规格一致,表示普通大页已生效。0f982c1827 malloc: Enable huge page support on main arena0849eed45d malloc: Move MORECORE fallback mmap to sysmalloc_mmap_fallbackc1beb51d08 malloc: Add Huge Page support to arenas98d5fcb8d0 malloc: Add Huge Page support for mmap6cc3ccc67e malloc: Move mmap logic to its own function7478c9959a malloc: Add THP/madvise support for sbrk5f6d8d97c6 malloc: Add madvise support for Transparent Huge Pages
malloc 时,会调用解析 glibc hugetlb 环境变量的函数,代码如下,value 即为用户指定的值:static __always_inline intdo_set_hugetlb (size_t value){if (value == 1){enum malloc_thp_mode_t thp_mode = __malloc_thp_mode ();/* Only enable THP madvise usage if system does support it andhas 'madvise' mode. Otherwise the madvise() call is wasteful. */if (thp_mode == malloc_thp_mode_madvise)mp_.thp_pagesize = __malloc_default_thp_pagesize ();}else if (value >= 2)__malloc_hugepage_config (value == 2 ? 0 : value,&mp_.hp_pagesize, &mp_.hp_flags);return 0;}
value 为 1 时,通过 __malloc_default_thp_pagesize 函数读取 /sys/kernel/mm/transparent_hugepage/hpage_pmd_size 设置 thp_pagesize 的值:unsigned long int__malloc_default_thp_pagesize (void){int fd = __open64_nocancel ("/sys/kernel/mm/transparent_hugepage/hpage_pmd_size", O_RDONLY);if (fd == -1)return 0;char str[INT_BUFSIZE_BOUND (unsigned long int)];ssize_t s = __read_nocancel (fd, str, sizeof (str));__close_nocancel (fd);if (s == 0)return 0;unsigned long int r = 0;for (ssize_t i = 0; i < s; i++){if (str[i] == '\\n')break;r *= 10;r += str[i] - '0';}return r;}
value 为 2 时,通过 malloc_default_hugepage_size 获取系统默认的大页大小。value 大于 2 时,读取 /sys/kernel/mm/hugepage 判断输入的 value 是否为系统支持的大页大小:void__malloc_hugepage_config (size_t requested, size_t *pagesize, int *flags){*pagesize = 0;*flags = 0;if (requested == 0){*pagesize = malloc_default_hugepage_size ();if (*pagesize != 0)*flags = hugepage_flags (*pagesize);return;}/* Each entry represents a supported huge page in the form of:hugepages-<size>kB. */int dirfd = __open64_nocancel ("/sys/kernel/mm/hugepages",O_RDONLY | O_DIRECTORY, 0);if (dirfd == -1)return;char buffer[1024];while (true){...hpsize *= 1024;if (hpsize == requested){*pagesize = hpsize;*flags = hugepage_flags (*pagesize);__close_nocancel (dirfd);return;}}__close_nocancel (dirfd);}
hugepage_flags 实现如下,flags 最终会传递给 mmap,从而指定传统大页的大小:static inline inthugepage_flags (size_t pagesize){return MAP_HUGETLB | (__builtin_ctzll (pagesize) << MAP_HUGE_SHIFT);}
/sys/kernel/mm/transparent_hugepage/enabled 设置为 madvise 时,需要用户态通过 madvise 系统调用告诉内核指定虚拟内存的使用模式,glibc 对 madvise 进行了如下封装:static inline voidmadvise_thp (void *p, INTERNAL_SIZE_T size){# ifdef MADV_HUGEPAGE/* Do not consider areas smaller than a huge page or if the tunableis not active. */if (mp_.thp_pagesize == 0 || size < mp_.thp_pagesize)return;...__madvise (p, size, MADV_HUGEPAGE);# endif}
thp_pagesize 在初次调用 malloc 时初始化为系统默认的透明大页大小。glibc 通过在每次 mmap 后调用 madvise_thp 即可实现使用透明大页。文档反馈