同一进程内能否映射同一物理内存至多虚拟地址空间?Linux/macOS如何实现?
同一进程内将同一物理内存映射到多个虚拟地址空间的实现
核心结论
可以在同一进程内将同一物理内存映射到多个虚拟地址空间,不同操作系统有对应的标准API实现方案。
一、Windows系统实现
在Windows中,可通过CreateFileMapping创建内存映射对象(支持文件备份或匿名共享内存),再多次调用MapViewOfFile将同一内存区域映射到进程的不同虚拟地址。示例代码如下:
#include <windows.h> #include <stdio.h> int main() { // 创建匿名共享内存的文件映射对象(大小256字节) HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 256, L"MySharedMemory"); if (hMapFile == NULL) { printf("Could not create file mapping object (%d).\n", GetLastError()); return 1; } // 将映射对象映射到当前进程的虚拟地址p1 LPVOID p1 = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 256); if (p1 == NULL) { printf("Could not map view of file (%d).\n", GetLastError()); CloseHandle(hMapFile); return 1; } // 将同一映射对象再次映射到当前进程的虚拟地址p2 LPVOID p2 = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 256); if (p2 == NULL) { printf("Could not map view of file (%d).\n", GetLastError()); UnmapViewOfFile(p1); CloseHandle(hMapFile); return 1; } // 验证两个虚拟地址指向同一物理内存 strcpy((char*)p1, "Hello, world!"); printf("p2 sees: %s\n", (char*)p2); // 清理资源 UnmapViewOfFile(p1); UnmapViewOfFile(p2); CloseHandle(hMapFile); return 0; }
二、Linux与macOS系统实现
Linux和macOS没有Windows中CreateFileMapping那样的直接句柄概念,但可以通过共享内存文件描述符结合mmap实现相同效果,核心是用文件描述符关联同一物理内存区域,再多次调用mmap映射到不同虚拟地址。
1. 通用方案(shm_open + mmap)
shm_open是POSIX标准API,可创建/打开共享内存对象,返回的文件描述符可多次用于mmap,实现同一物理内存的多虚拟地址映射。该方案同时兼容Linux和macOS:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main() { // 创建POSIX共享内存对象(名称全局可见,权限0666) int fd = shm_open("/MySharedMemory", O_CREAT | O_RDWR, 0666); if (fd == -1) { perror("shm_open failed"); return 1; } // 设置共享内存大小为256字节 if (ftruncate(fd, 256) == -1) { perror("ftruncate failed"); close(fd); shm_unlink("/MySharedMemory"); return 1; } // 第一次映射到虚拟地址p1 void* p1 = mmap(NULL, 256, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (p1 == MAP_FAILED) { perror("mmap p1 failed"); close(fd); shm_unlink("/MySharedMemory"); return 1; } // 第二次映射同一共享内存到虚拟地址p2 void* p2 = mmap(NULL, 256, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (p2 == MAP_FAILED) { perror("mmap p2 failed"); munmap(p1, 256); close(fd); shm_unlink("/MySharedMemory"); return 1; } // 验证地址关联 strcpy((char*)p1, "Hello from POSIX shared memory!"); printf("p2 sees: %s\n", (char*)p2); // 清理资源 munmap(p1, 256); munmap(p2, 256); close(fd); shm_unlink("/MySharedMemory"); // 删除共享内存对象 return 0; }
2. Linux专属方案(memfd_create + mmap)
Linux下可使用memfd_create创建匿名内存文件描述符(无需挂载到文件系统,仅当前进程可见),再多次调用mmap实现映射:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/memfd.h> #include <unistd.h> int main() { // 创建匿名内存文件描述符(MFD_CLOEXEC确保进程退出时自动关闭) int fd = memfd_create("MyAnonymousMem", MFD_CLOEXEC); if (fd == -1) { perror("memfd_create failed"); return 1; } // 设置内存大小为256字节 if (ftruncate(fd, 256) == -1) { perror("ftruncate failed"); close(fd); return 1; } // 两次映射同一内存到不同虚拟地址 void* p1 = mmap(NULL, 256, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); void* p2 = mmap(NULL, 256, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (p1 == MAP_FAILED || p2 == MAP_FAILED) { perror("mmap failed"); munmap(p1, 256); close(fd); return 1; } strcpy((char*)p1, "Hello from memfd!"); printf("p2 sees: %s\n", (char*)p2); // 清理资源 munmap(p1, 256); munmap(p2, 256); close(fd); return 0; }
3. 无文件描述符的本地映射技巧(Linux)
如果不需要跨进程共享,Linux还可以通过MAP_FIXED_NOREPLACE将已映射的物理内存重新映射到新的虚拟地址,无需文件描述符:
#include <stdio.h> #include <sys/mman.h> #include <unistd.h> #include <string.h> int main() { // 匿名映射一块内存到p1 void* p1 = mmap(NULL, 256, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (p1 == MAP_FAILED) { perror("mmap p1 failed"); return 1; } // 分配一块空的虚拟地址空间p2(权限设为PROT_NONE避免冲突) void* p2 = mmap(NULL, 256, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (p2 == MAP_FAILED) { perror("mmap p2 failed"); munmap(p1, 256); return 1; } // 将p1对应的物理内存重新映射到p2 void* result = mmap(p2, 256, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_PRIVATE, -1, 0); if (result == MAP_FAILED) { perror("mmap fixed failed"); munmap(p1, 256); munmap(p2, 256); return 1; } strcpy((char*)p1, "Hello, remapped memory!"); printf("p2 sees: %s\n", (char*)p2); // 清理资源 munmap(p1, 256); munmap(p2, 256); return 0; }
关键说明
- Windows的
CreateFileMapping句柄,对应POSIX系统中shm_open/memfd_create返回的文件描述符,都是用来关联同一物理内存区域的标识。 - 多次调用
mmap时,只要使用关联同一物理内存的文件描述符,就能将该内存映射到不同虚拟地址,读写操作会在所有映射地址间同步。
内容的提问来源于stack exchange,提问作者0xcaff
相关产品推荐
相关产品推荐

