Linux mremap(2) 函数是否可以将从 mmap() 获得的 HugeTLB 的虚拟地址更改为新的固定虚拟地址?
(背景:我想根据我得到的内存的物理地址重新映射虚拟地址。这是通过直接检查指针地址来有效地执行虚拟地址到物理地址的转换。我将使用内存从 DMA 到硬件用户空间。)
这似乎不适用于我的简单测试程序:
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/mman.h>
#include <stdint.h>
#define LARGE_PAGE_SIZE (1024*1024*1024)
int main() {
void *p1;
void *p2;
p1 = mmap(NULL, LARGE_PAGE_SIZE, PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANONYMOUS|MAP_HUGETLB|MAP_LOCKED,
0, 0);
if (p1 == MAP_FAILED) {
perror("mmap");
return 1;
}
printf("p1 = %p\n", p1);
p2 = mremap(p1, LARGE_PAGE_SIZE, LARGE_PAGE_SIZE,
MREMAP_MAYMOVE|MREMAP_FIXED,
(void*)(((uint64_t)p1) | 0x500000000000ULL));
if (p2 == MAP_FAILED) {
perror("mremap");
return 1;
}
printf("p2 = %p\n", p2);
}
mmap() 成功,mremap() 失败:
$ gcc -o mremap_hugetlb mremap_hugetlb.c && sudo ./mremap_hugetlb
p1 = 0x2aaac0000000
mremap: Invalid argument
请注意,新地址是根据原始 mmap() 获得的地址计算得出的。这很重要。无法提前知道所需的地址,因此我不能简单地将 MAP_FIXED 传递给 mmap()。
我目前使用的解决方法是使 mmap() 文件支持,这样我就可以在固定地址再次对它进行 mmap(),并 munmap() 旧映射。这是次优的,因为它需要我找到一个挂载的 hugetlbfs 文件系统,我不喜欢这种依赖的复杂性。
基于解决方法的当前代码: https://github.com/lukego/snabbswitch/blob/straightline/src/core/memory.c#L56
最佳答案
现在看来您必须使用 hugetlbfs。
除非我弄错了,否则问题出现在 Linux 内核中,因为 mm/mremap.c:mremap_to()
电话 mm/mremap.c:vma_to_resize()
,这fails with EINVAL
for huge pages .
也许测试不正确,或者函数缺少正确处理大页面的代码。我想知道是否应该联系 linux-kernel和 linux-mm邮件列表,看看这是否是一个应该/可以轻松修复的错误。但是,这对依赖当前(和旧版)内核的用户没有帮助。
请记住,在文件描述符上使用 mmap()
时,您通常会使用不同的代码路径,因为每个文件系统都可以指定自己的 mmap
处理程序。对于 hugetlbfs,代码在 fs/hugetlbfs/inode.c:hugetlbfs_file_mmap()
中.
而且,正如您所说,该代码路径似乎适合您。
请注意,最好让用户配置 hugetlbfs 挂载点,而不是从 /proc/mounts
中扫描一个挂载点,因为这样系统管理员可以配置多个 hugetlbfs 挂载点,每个挂载点都有不同的配置,针对服务器上运行的每个服务。 (我希望您的服务不需要以 root 身份运行。)
关于c - mremap(2) 与 HugeTLB 改变虚拟地址?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27997934/