在我的应用程序中,我需要在父母和孩子之间共享记忆(使用fork
+ execl
)。我使用memfd_create
来分配内存,因为它提供了一个文件描述符,可以方便地在子进程中使用(在dup2
之前,discriptor通过execl
绑定到stdin)以附加到分配的内存。
我不使用write
和read
- 我使用指针直接读写内存。
唯一需要解决的难题是如何获取通过fd = memfd_create ...
分配的内存地址。
使用mmap
是不可取的,因为它复制了内存,而不是给出已经由memfd_create
分配的内存地址。
以下代码对此进行了演示。在其输出中,每个mmap
地址递增4096
,这是fd
引用的内存大小:
0x7f98411c1000
0x7f98411c0000
而如果mmap
给出了直接地址,则输出中的地址将是相同的。
#include <stdio.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
int main(void)
{
int fd = syscall(SYS_memfd_create, "shm", 0);
if (fd == -1) return 1;
size_t size = 4096; /* minimal */
int check = ftruncate(fd, size);
if (check == -1) return 1;
void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) return 1;
void *ptr2 = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr2 == MAP_FAILED) return 1;
printf("%p\n%p\n", ptr, ptr2);
return 0;
}
那么,如何获得直接地址,避免mmap
的内存重复?
不确定你是否还需要答案,因为你已经在自己的答案中写了主要观点,但只是添加它以确保它是完整的:
memfd_create创建一个仅限内存的文件(意味着它不存储在磁盘上,尽管它可以被换出)。当您在答案中写下时,它会返回一个文件描述符。
mmap确保文件描述符后面的文件在内存中(在仅内存文件的情况下不需要操作),并为您提供指向该内存的指针。它不会复制内存(从磁盘映射文件时,从磁盘到内存除外)。如果多次映射同一文件,则每次调用mmap都会保留一个新的虚拟内存区域,但所有这些区域都会访问相同的物理内存部分。
所以对你的问题的简短回答就是你误解了mmap;它不会复制内存,它是解决您问题的完美方案。
我们从memfd_create
获取文件描述符而不是物理地址。文件描述符是一个句柄,通过它我们可以访问内存(即,它是从文件描述符到内存的映射)。文件描述符比内存地址更方便使用:它可以传递给分叉进程等(参见OP)。
mmap
只是将物理地址(由文件描述符引用)映射到虚拟地址。虚拟内存地址实际上是内存地址,因为用户永远不会看到物理地址。每个mmap调用都返回一个不同的虚拟地址,所有这些地址都由内核映射到同一个物理地址。