shmget分配huge page返回ENOMEM
同学跑程序要用大页来提升性能,然后发现在机房中的某些机器上能跑起来,而另外一些不行,然后就找到我这debug来了。表现出来的症状是,调用shmget
生成共享内存id时直接报ENOMEM
问题。
回顾一下计算机基础知识,页是定长、连续的内存物理结构,而大页区别于普通页最大的区别就是其大小,于是首先想到的是在内存分配上是否会因为没有足够长的连续空间而出现即使内存数量足够、但是分配不出连续大空间的问题,解决该问题最直接的方法就是重启。
然而,重启过后,问题仍然存在,遂排除连续内存空间不够的可能性(然而如果是数十/百M的大页应该也不算大,内存不至于那么碎)。而后怀疑是程序本身的问题,于是把shmget
操作从程序中抄出来,单独用一段代码执行:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/shm.h>
int main(int argc, char *argv[]) {
int status;
int segment_id;
const int SEGMENT_SIZE = 0x1234;
segment_id = shmget (IPC_PRIVATE, SEGMENT_SIZE,
IPC_CREAT | IPC_EXCL | 0666 | SHM_HUGETLB);
printf("segment_id=%d\n", segment_id);
perror("");
shmctl(segment_id, IPC_RMID, 0);
exit(EXIT_SUCCESS);
}
由于我们只需要验证共享内存是否可以创建,而不是是否可以使用,因而后面的shmat
/shmdt
都省去了。运行后发现,ENOMEM
错误仍然存在。
然后思路又回到大页这方面,猜测是不是SHM_HUGETLB
参数导致的共享内存分配失败,编译执行发现程序运行正常,然后就想到了用SHM_HUGETLB
作为关键字上网冲浪。然后在StackOverflow上发现有人遇到了相似的问题,说是因为总大页数达到了系统设置的上限。然后顺着问题中的思路检查机器,用sysctl -a
发现了问题:对比两台分别可否运行程序的机器,发现有个叫vm.nr_hugepages
的参数,看起来和大页的数量有关,在不能运行同学程序的机器上,该参数值为0,而可以运行的机器上参数值为16384。
然后就去网上搜索这个参数到底是什么含义。经过艰难摸索,在官方文档里找到了描述:
/proc/sys/vm/nr_hugepages
indicates the current number of “persistent” huge pages in the kernel’s huge page pool. “Persistent” huge pages will be returned to the huge page pool when freed by a task. A user with root privileges can dynamically allocate more or free some persistent huge pages by increasing or decreasing the value ofnr_hugepages
.
大意是说大页是有单独的内存池的,而这个nr_hugepages
可以通过控制允许创建的大页数目来控制大页内存池的大小,因而合理推测当该内存池大小为0的时候,等价于实际上禁用了大页功能。于是通过使用sysctl
命令,临时设置大大页内存池的大小:
sysctl vm.nr_hugepages=1
测试后发现问题解决。
然后继续往下阅读文档发现,似乎还有个选项,允许程序运行时动态调整大页内存池的大小,而不至于“撞墙”:
/proc/sys/vm/nr_overcommit_hugepages
specifies how large the pool of huge pages can grow, if more huge pages than/proc/sys/vm/nr_hugepages
are requested by applications. Writing any non-zero value into this file indicates that the hugetlb subsystem is allowed to try to obtain that number of “surplus” huge pages from the kernel’s normal page pool, when the persistent huge page pool is exhausted. As these surplus huge pages become unused, they are freed back to the kernel’s normal page pool.
大意是说往/proc/sys/vm/nr_overcommit_hugepages
里面写任意一个非0值就会开启这个功能,允许当大页内存池的固定内存用完时弹性借用普通内存池的内存(未经测试)。