可否通过mmap将现有内存映射为另一地址的只读映射且不拷贝数据?
实现方案(类Unix平台,以Linux为例)
你的需求可通过内存映射机制零拷贝实现,无需任何数据复制:
1. 单块内存的只读别名映射
你之前尝试fmemopen的思路不适用,应该改用内存文件描述符实现多地址映射:
- 前提:如果需要完全零拷贝,原内存不能是
malloc分配的堆内存,需要改用内存映射的方式分配初始内存(堆内存的页不属于可共享映射范围,无法直接二次映射) - 实现步骤:
#include <sys/mman.h> #include <fcntl.h> #include <unistd.h> // 第一步:创建匿名内存文件描述符 int memfd = memfd_create("mem_alias", MFD_CLOEXEC); ftruncate(memfd, len); // 第二步:映射原可写地址 mem char *mem = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, 0); // 第三步:映射独立的只读别名 mem2 char *mem2 = mmap(NULL, len, PROT_READ, MAP_SHARED, memfd, 0);
此时mem和mem2指向同一块物理内存,修改mem的内容会实时同步到mem2,两者虚拟地址不同,无任何数据拷贝。
- 兼容其他类Unix平台:如果系统没有
memfd_create,可以用shm_open创建POSIX共享内存对象替代,逻辑完全一致,使用完成后调用shm_unlink删除共享内存对象即可。
2. 多段非连续内存拼接为连续虚拟地址
你的最终目标可以通过固定位置映射实现:
- 先申请一块足够大的连续虚拟地址空间作为占位,不分配实际物理内存:
// 所有待拼接内存块的总长度 size_t total_len = block1_len + block2_len + block3_len; // 申请PROT_NONE的匿名映射作为占位 char *continuous_base = mmap(NULL, total_len, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- 按顺序将每块内存映射到连续地址的对应偏移位置:
// 映射第一块到偏移0位置 mmap(continuous_base + 0, block1_len, PROT_READ, MAP_SHARED | MAP_FIXED_NOREPLACE, block1_memfd, 0); // 映射第二块到偏移block1_len位置 mmap(continuous_base + block1_len, block2_len, PROT_READ, MAP_SHARED | MAP_FIXED_NOREPLACE, block2_memfd, 0); // 按此逻辑拼接所有内存块
注意事项
MAP_FIXED_NOREPLACE为Linux 4.17及以上版本支持的参数,会在目标地址已有映射时报错避免误覆盖;如果需要兼容旧版本内核可以替换为MAP_FIXED,但需提前确认占位的连续地址空间没有被其他映射占用。- 所有映射使用完成后需要调用
munmap释放,同时关闭对应的内存文件描述符。
内容的提问来源于stack exchange,提问作者Ismail Badawi
相关产品推荐
相关产品推荐

