进程通信--内存映射区(用户区)

在进程间通信(IPC)中,内存映射区(Memory-Mapped Area 或 Memory-Mapped File)是一种高效的通信机制,通过共享内存实现进程间的数据交换。使用内存映射区的主要优点是,它允许不同进程访问同一个物理内存区域,而不需要显式的数据拷贝。

内存映射区的概念

内存映射区是将文件或设备的内容映射到进程的虚拟内存地址空间中。进程可以像操作普通内存一样读写这些映射的区域,而这些操作会直接反映到文件或设备上。对于进程间通信,可以使用匿名内存映射区(不对应具体的文件),进程通过共享这个匿名内存区域来进行数据交换。

使用内存映射区进行进程间通信

步骤

  1. 创建内存映射区

    • 使用 mmap() 系统调用创建内存映射区。匿名内存映射区通常通过打开一个虚拟内存文件(如 /dev/zero)或使用 shm_open() 创建。
  2. 同步访问

    • 多个进程访问同一个共享内存区域时,需要使用同步机制(如信号量或互斥锁)来防止竞争条件和数据不一致。

示例代码

以下是使用内存映射区进行进程间通信的示例:

  1. 父子进程间共享内存
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

int main() {
    // 创建匿名内存映射区
    const size_t SIZE = 4096;
    void *shared_memory = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (shared_memory == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (pid == 0) {  // 子进程
        // 写入共享内存
        const char *message = "Hello from child process!";
        strcpy((char *)shared_memory, message);
        // 解除内存映射区
        munmap(shared_memory, SIZE);
        exit(EXIT_SUCCESS);
    } else {  // 父进程
        wait(NULL);  // 等待子进程
        // 读取共享内存
        printf("Parent read: %s\n", (char *)shared_memory);
        // 解除内存映射区
        munmap(shared_memory, SIZE);
        exit(EXIT_SUCCESS);
    }
}

代码解析

  1. 创建内存映射区

    void *shared_memory = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    
    • mmap() 用于创建一个匿名内存映射区。MAP_SHARED 标志表示这个映射区可以在多个进程间共享。MAP_ANONYMOUS 标志表示这个映射区不与任何文件关联。
  2. 进程间通信

    • 父进程使用 fork() 创建子进程。
    • 子进程在共享内存区域写入数据。
    • 父进程等待子进程结束,然后从共享内存区域读取数据。
  3. 解除内存映射

    munmap(shared_memory, SIZE);
    
    • 使用 munmap() 解除内存映射区,释放资源。

使用 shm_openmmap

shm_openmmap 可以结合使用,创建命名共享内存区域,使无亲缘关系的进程间通信变得可能。

以下是使用 shm_openmmap 的示例:

  1. 进程A(创建和写入共享内存)

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <string.h>
    
    int main() {
        const char *name = "/my_shm";
        const size_t SIZE = 4096;
    
        int shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
        if (shm_fd == -1) {
            perror("shm_open");
            exit(EXIT_FAILURE);
        }
    
        if (ftruncate(shm_fd, SIZE) == -1) {
            perror("ftruncate");
            exit(EXIT_FAILURE);
        }
    
        void *shared_memory = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
        if (shared_memory == MAP_FAILED) {
            perror("mmap");
            exit(EXIT_FAILURE);
        }
    
        const char *message = "Hello from process A!";
        strcpy((char *)shared_memory, message);
    
        munmap(shared_memory, SIZE);
        close(shm_fd);
        return 0;
    }
    
  2. 进程B(读取共享内存)

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <string.h>
    
    int main() {
        const char *name = "/my_shm";
        const size_t SIZE = 4096;
    
        int shm_fd = shm_open(name, O_RDONLY, 0666);
        if (shm_fd == -1) {
            perror("shm_open");
            exit(EXIT_FAILURE);
        }
    
        void *shared_memory = mmap(0, SIZE, PROT_READ, MAP_SHARED, shm_fd, 0);
        if (shared_memory == MAP_FAILED) {
            perror("mmap");
            exit(EXIT_FAILURE);
        }
    
        printf("Read from shared memory: %s\n", (char *)shared_memory);
    
        munmap(shared_memory, SIZE);
        close(shm_fd);
        shm_unlink(name);
        return 0;
    }
    

代码解析

  1. 创建和写入共享内存

    • shm_open 创建或打开一个命名共享内存对象。
    • ftruncate 设置共享内存对象的大小。
    • mmap 将共享内存对象映射到进程的地址空间。
    • 写入数据到共享内存。
  2. 读取共享内存

    • shm_open 以只读模式打开命名共享内存对象。
    • mmap 将共享内存对象映射到进程的地址空间。
    • 读取数据。
  3. 清理

    • 使用 munmap 解除内存映射。
    • 使用 close 关闭文件描述符。
    • 使用 shm_unlink 删除命名共享内存对象。

通过内存映射区进行进程间通信,可以实现高效的共享内存访问,这对于需要频繁且高速的数据交换的应用非常有用。

posted @ 2024-05-22 10:48  ponder776  阅读(15)  评论(0编辑  收藏  举报