返回

如何在同一进程内映射同一块物理内存?详解 Windows、Linux 和 macOS 实现

Linux

在同一进程内映射相同物理内存

引言

在编程中,有时需要在同一进程内将同一块物理内存映射到多个虚拟地址空间。这对于在多个线程或组件之间共享数据非常有用。本文将探讨在 Windows、Linux 和 macOS 系统中实现这一目标的可能性,并提供相应的 API 及其实现示例。

Windows

在 Windows 系统中,我们可以使用 CreateFileMapping 函数创建一个受文件支持的内存块,然后使用 MapViewOfFile 函数将文件的某些部分映射到多个虚拟内存区域。以下是一个示例代码:

#include <windows.h>
#include <stdio.h>

int main() {
    // 创建共享内存文件映射对象
    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;
    }

    // 将文件映射的一个视图映射到当前进程
    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;
    }

    // 将同一文件映射的另一个视图映射到当前进程
    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;
    }

    // 通过 p1 和 p2 使用内存区域
    strcpy((char*)p1, "Hello, world!");
    printf("p2 sees: %s\n", (char*)p2);

    // 清理
    UnmapViewOfFile(p1);
    UnmapViewOfFile(p2);
    CloseHandle(hMapFile);

    return 0;
}

Linux

在 Linux 系统中,可以通过以下步骤实现同一物理内存的映射:

  1. 使用 mmap() 系统调用创建一个共享内存文件符。
  2. 使用 shm_open() 系统调用打开共享内存文件。
  3. 使用 mmap() 系统调用将共享内存文件映射到进程的虚拟地址空间。

以下是一个示例代码:

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main() {
    // 创建共享内存文件符
    int fd = shm_open("/my_shared_memory", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    if (fd == -1) {
        perror("shm_open");
        return 1;
    }

    // 调整文件大小
    ftruncate(fd, 256);

    // 将共享内存文件映射到进程的虚拟地址空间
    char* p1 = mmap(NULL, 256, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (p1 == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return 1;
    }

    // 将同一共享内存文件的另一个视图映射到进程的虚拟地址空间
    char* p2 = mmap(NULL, 256, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (p2 == MAP_FAILED) {
        perror("mmap");
        munmap(p1);
        close(fd);
        return 1;
    }

    // 通过 p1 和 p2 使用内存区域
    strcpy(p1, "Hello, world!");
    printf("p2 sees: %s\n", p2);

    // 清理
    munmap(p1);
    munmap(p2);
    close(fd);

    return 0;
}

macOS

在 macOS 系统中,可以使用以下步骤实现同一物理内存的映射:

  1. 使用 shm_open() 系统调用创建一个共享内存文件描述符。
  2. 使用 ftruncate() 系统调用调整文件大小。
  3. 使用 mmap() 系统调用将共享内存文件映射到进程的虚拟地址空间。

以下是一个示例代码:

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main() {
    // 创建共享内存文件描述符
    int fd = shm_open("/my_shared_memory", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
    if (fd == -1) {
        perror("shm_open");
        return 1;
    }

    // 调整文件大小
    ftruncate(fd, 256);

    // 将共享内存文件映射到进程的虚拟地址空间
    char* p1 = mmap(NULL, 256, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (p1 == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return 1;
    }

    // 将同一共享内存文件的另一个视图映射到进程的虚拟地址空间
    char* p2 = mmap(NULL, 256, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (p2 == MAP_FAILED) {
        perror("mmap");
        munmap(p1);
        close(fd);
        return 1;
    }

    // 通过 p1 和 p2 使用内存区域
    strcpy(p1, "Hello, world!");
    printf("p2 sees: %s\n", p2);

    // 清理
    munmap(p1);
    munmap(p2);
    close(fd);

    return 0;
}

常见问题解答

  1. 为什么需要将同一物理内存映射到多个虚拟地址空间?

    这对于在多个线程或组件之间共享数据非常有用,避免了不必要的内存复制。

  2. 在哪些情况下应该避免使用此技术?

    如果需要在不同进程之间共享数据,则应使用其他方法,例如共享内存段或套接字。

  3. 此技术是否会影响性能?

    是的,将同一物理内存映射到多个虚拟地址空间可能会导致额外的开销,尤其是频繁写入内存时。

  4. 此技术是否可移植?

    否,本文中描述的技术特定于 Windows、Linux 和 macOS 系统。

  5. 是否有替代方案可以实现相同的功能?

    是的,可以使用共享内存段或套接字来在不同进程之间共享数据。