如何让/dev/shm只读文件通过shm_open/mmap启用Transparent Huge Pages?
如何让/dev/shm映射文件启用透明大页(THP)
问题详情
我在/dev/shm目录下有一个任意大小的只读文件(大小不一定是2MB的倍数,最小可至120KB),尝试通过shm_open+mmap将其映射到内存,再调用带MADV_HUGEPAGE参数的madvise来启用透明大页(THP),但始终无法生效。
已做配置
echo advise >/sys/kernel/mm/transparent_hugepage/shmem_enabled echo never >/sys/kernel/mm/transparent_hugepage/enabled
代码实现
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/sysconf.h> #include <sys/shm.h> int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s <file_path>\n", argv[0]); exit(EXIT_FAILURE); } const char *file_path = argv[1]; int fd = shm_open(file_path, O_RDONLY, 0666); if (fd == -1) { perror("shm_open"); exit(EXIT_FAILURE); } struct stat sb; if (fstat(fd, &sb) == -1) { perror("fstat"); close(fd); exit(EXIT_FAILURE); } size_t huge_page_size = sysconf(_SC_HUGEPAGESIZE); if (huge_page_size == -1) { perror("sysconf"); close(fd); exit(EXIT_FAILURE); } void *addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); if (addr == MAP_FAILED) { perror("mmap"); close(fd); exit(EXIT_FAILURE); } if (madvise(addr, sb.st_size, MADV_HUGEPAGE) == -1) { perror("madvise"); munmap(addr, sb.st_size); close(fd); exit(EXIT_FAILURE); } // TODO: do something for 10 mins or more // ... Read the file and do something. // After 10 mins or more. munmap(addr, sb.st_size); close(fd); return 0; }
复现步骤
- 在
/dev/shm创建任意大小的文件; - 运行上述代码并传入文件路径;
- 查看
/proc/meminfo中的ShmemHugePages值,始终为0。
系统信息
- Linux内核:5.4
- 架构:x86_64
补充说明
- 文件为只读模式,大小不固定;
- 已配置shmem的THP策略为
advise。
解决方法
1. 修正全局THP配置
你当前设置transparent_hugepage/enabled=never会全局禁用所有THP功能,直接覆盖了shmem_enabled的配置。必须将其改为madvise,这样内核才会响应MADV_HUGEPAGE的建议:
echo madvise >/sys/kernel/mm/transparent_hugepage/enabled echo advise >/sys/kernel/mm/transparent_hugepage/shmem_enabled
2. 触发页面实际访问
THP不会在映射完成后立即分配,只有当页面被实际读取/写入时,内核才会尝试将普通页合并为大页。你需要在madvise之后,主动遍历映射区域触发页面加载:
// 替换原代码中的TODO部分 char *ptr = (char *)addr; size_t huge_page_size = sysconf(_SC_HUGEPAGESIZE); // 遍历每个大页起始位置,触发页面加载(volatile避免编译器优化) for (size_t i = 0; i < sb.st_size; i += huge_page_size) { volatile char dummy = ptr[i]; } // 保持进程运行,方便验证 sleep(600);
3. 优化映射对齐(可选)
如果希望尽可能多的区域使用大页,可以将映射大小向上对齐到最近的大页尺寸。不过这不是强制要求,内核会自动处理非对齐部分的普通页:
// 计算对齐后的大小 size_t aligned_size = ((sb.st_size + huge_page_size - 1) / huge_page_size) * huge_page_size; // 使用对齐后的大小进行映射 void *addr = mmap(NULL, aligned_size, PROT_READ, MAP_SHARED, fd, 0);
4. 验证方法
除了查看/proc/meminfo,还可以通过以下方式确认THP是否生效:
- 查看进程的
/proc/<pid>/smaps,查找包含ShmemHugePages的行,查看大页占用量; - 使用
grep AnonHugePages /proc/<pid>/status查看进程的大页使用情况。
内容的提问来源于stack exchange,提问作者Tefone
相关产品推荐
相关产品推荐

