You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

同一进程内能否映射同一物理内存至多虚拟地址空间?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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 16:57:14