HugePages 使用率?
一天,有同事问,怎么看 HugePages 的使用率。那么问题来了,HugePages 使用率是指什么?这个答案因人而异。
在 /proc/meminfo 里,看到以下四个关于 HugePages 的数值:
HugePages_Total: vvv HugePages_Free: www HugePages_Rsvd: xxx HugePages_Surp: yyy Hugepagesize: zzz kB
从 Kernel Doc (vm/hugetlbpage.txt) 可以知道, HugePages_Total 是指系统总共预留了多少 HugePages. HugePages_Free 指当前还有多少 HugePages 未分配 (allocate),HugePages_Rsvd 是指有多少 HugePages 是系统承诺了会分配给程序(commitment to allocate),但实际并未分配。HugePages_Surp 指超分的页。
所以会有个问题, HugePages_Free 是否包含了 HugePages_Rsvd? 从文档的表述,应该是包含了。可以通过 Kernel doc 自带的示例程序来验证。
测试:用 mmap 申请 Hugepages
代码改编自 Kernel Doc (vm/hugetlbpage.txt)。这个示例程序会申请 128 个 HugePages, 然后会读取第一个 HugePage 的第一个字节,随后会往这 128 个 HugePages 里填满内容。
/* * Example of using huge page memory in a user application using the mmap * system call. Before running this application, make sure that the * administrator has mounted the hugetlbfs filesystem (on some directory * like /mnt) using the command mount -t hugetlbfs nodev /mnt. In this * example, the app is requesting memory of size 256MB that is backed by * huge pages. * * For the ia64 architecture, the Linux kernel reserves Region number 4 for * huge pages. That means that if one requires a fixed address, a huge page * aligned address starting with 0x800000... will be required. If a fixed * address is not required, the kernel will select an address in the proper * range. * Other architectures, such as ppc64, i386 or x86_64 are not so constrained. */ #include#include #include #include #include #define FILE_NAME "/mnt/hugepagefile" #define LENGTH (256UL*1024*1024) #define PROTECTION (PROT_READ | PROT_WRITE) #define ADDR (void *)(0x0UL) /* let kernel choose address */ #define FLAGS (MAP_SHARED) void check_bytes(char *addr) { printf("First hex is %x\n", *((unsigned int *)addr)); } void write_bytes(char *addr) { unsigned long i; for (i = 0; i < LENGTH; i++) *(addr + i) = (char)i; } void read_bytes(char *addr) { unsigned long i; check_bytes(addr); for (i = 0; i < LENGTH; i++) if (*(addr + i) != (char)i) { printf("Mismatch at %lu\n", i); break; } } int main(void) { void *addr; int fd; fd = open(FILE_NAME, O_CREAT | O_RDWR, 0755); if (fd < 0) { perror("Open failed"); exit(1); } addr = mmap(ADDR, LENGTH, PROTECTION, FLAGS, fd, 0); if (addr == MAP_FAILED) { perror("mmap"); unlink(FILE_NAME); exit(1); } printf("Returned address is %p\n", addr); getchar(); // 申请好HugePages后,暂停。 check_bytes(addr); // 获取申请到的HugePages的第一个字节的内容。 getchar(); // 暂停 write_bytes(addr); // 往内存里写数据 getchar(); // 暂停 read_bytes(addr); munmap(addr, LENGTH); close(fd); unlink(FILE_NAME); return 0; }
在运行上面的程序之前,需要先让系统预留好 HugePages, 并且挂载 hugetlbfs。
在 /etc/sysctl.conf 添加
vm.nr_hugepages = 130
执行:
# sysctl -p # mount -t hugetlbfs nodev /mnt
从 /proc/meminfo 可以看到,系统已经预留好了 130 个 HugePages.
HugePages_Total: 130 HugePages_Free: 130 HugePages_Rsvd: 0 HugePages_Surp: 0 Hugepagesize: 2048 kB
运行示例程序:
# ./hugetest Returned address is 0x2aaaaac00000
查看 meminfo,可以看到 HugePages_Rsvd 变成了 128,也就是程序所申请的数量,而 HugePages_Free 仍然是 130.
HugePages_Total: 130 HugePages_Free: 130 HugePages_Rsvd: 128 HugePages_Surp: 0
继续执行程序,程序会检查第一个HugePages的内容。
# ./hugetest Returned address is 0x2aaaaac00000 First hex is 0
从 meminfo 可以看到, HugePages_Free 和 HugePages_Rsvd 都减少了1。这是因为程序读取第一个页的时候,系统为这个页真正地分配空间(allocate)了。
HugePages_Total: 130 HugePages_Free: 129 HugePages_Rsvd: 127 HugePages_Surp: 0
继续执行程序,程序会往所申请到的128个页中写满内容。从 meminfo 可以看到,HugePages_Rsvd 已经变为0了,因为程序所申请的所有HugePages这个时候都真正获得分配了。
HugePages_Total: 130 HugePages_Free: 2 HugePages_Rsvd: 0 HugePages_Surp: 0
所以,关于 HugePages 使用率这个问题,如果我们希望知道的是系统实际分配给了程序多少个 HugePages, 我们可以直接通过 HugePages_Total - HugePages_Free 计算得到。HugePages_Rsvd 只是承诺程序的数量,并未真正使用。