透明大页(Transparent hugepages)结合madvise使用失效问题排查
问题背景
已将系统透明大页配置为/sys/kernel/mm/transparent_hugepage/enabled = [madvise],通过boost::interprocess在/dev/shm中分配内存池,随后尝试用madvise将内存映射为大页,代码片段如下:
int64_t ptr = (int64_t)mempool_; ptr = ((ptr >> 21) + 1) << 21; // 对齐到大页边界 int ret_val = madvise((void*)ptr, 2 * 1024 * 1024, MADV_HUGEPAGE); cout << "start : " << (void*)ptr << endl; cout << "end : " << (void*)(ptr + 2 * 1024 * 1024) << endl; cout << "Return value of madvise() : " << ret_val << endl; cout << "Errno set to : " << errno << endl; cout << "Error on madvise: " << strerror( errno ) << endl;
代码执行输出:
start : 0x7f2d17400000 end : 0x7f2d17600000 Return value of madvise() : 0 Errno set to : 0 Error on madvise: Success
从输出看调用成功,但系统并未实际分配大页。已等待足够时间(khugepaged参数:scan_sleep_millisecs = 10000,alloc_sleep_millisecs = 60000),问题仍存在。
可能原因及解决方法
内存对齐范围错误
当前对齐逻辑会把指针移到下一个大页边界,但原内存池mempool_的起始地址到该边界的区域可能不属于你分配的内存范围。madvise仅会处理完全在映射范围内的区域,且不会因部分越界返回错误。
修正方法:将指针对齐到当前大页起始,同时确保整个2MB区域都在内存池范围内:ptr = ptr & ~((1 << 21) - 1); // 对齐到当前大页起始地址/dev/shm挂载属性限制/dev/shm默认是tmpfs文件系统,若挂载时未启用大页支持,即便调用madvise也无法触发透明大页分配。
操作步骤:- 检查挂载属性:
mount | grep /dev/shm,确认是否包含huge=advise选项; - 临时重新挂载:
mount -o remount,huge=advise /dev/shm; - 永久生效:修改
/etc/fstab中/dev/shm的挂载行,添加huge=advise。
- 检查挂载属性:
khugepaged触发条件未满足
khugepaged仅会合并已写入的匿名页面,而/dev/shm的页面默认是文件映射类型。调用madvise后需要对目标内存区域进行实际写入,才能触发扫描合并。
解决方法:在madvise后填充整个2MB区域:memset((void*)ptr, 0, 2 * 1024 * 1024);系统大页资源不足
检查可用大页数量:cat /sys/kernel/mm/hugepages/hugepages-2048kB/free_hugepages,若结果为0,需先调整预留大页数量:echo 100 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages透明大页碎片整理配置限制
若/sys/kernel/mm/transparent_hugepage/defrag设为never,内核不会为合并大页整理内存碎片,可能导致无法分配连续2MB区域。临时调整命令:echo madvise > /sys/kernel/mm/transparent_hugepage/defrag
内容的提问来源于stack exchange,提问作者Priyanshu Yadav

