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

如何从shm_open获取内存地址?fork进程共享内存mmap地址一致实现

关于fork后共享内存地址一致的问题解答

嘿,我来帮你把这个问题捋清楚!首先明确说:用shm_open完全能实现父子进程的内存共享,根本不用换成shmget——shmget是System V那套老接口,shm_open属于POSIX标准的新接口,用法更贴近文件操作,反而更推荐用它。

核心问题:怎么让mmap返回相同地址

你遇到的mmap地址不同,大概率是因为父子进程各自单独调用了mmap。其实有两种简单的方式解决这个问题:

方式一:父进程先映射,再fork(最简单,自动地址一致)

fork之后,子进程会继承父进程的所有内存映射,包括共享内存的映射地址。也就是说,父进程先完成共享内存的mmap,再fork子进程,子进程直接用父进程的映射地址就行,根本不需要自己再调用mmap。

给你改好的最简示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>

#define SHM_NAME "/my_shared_mem"
#define SHM_SIZE 4096

int main() {
    // 创建/打开共享内存对象
    int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    if (shm_fd == -1) {
        perror("shm_open failed");
        exit(EXIT_FAILURE);
    }

    // 设置共享内存的大小
    if (ftruncate(shm_fd, SHM_SIZE) == -1) {
        perror("ftruncate failed");
        close(shm_fd);
        shm_unlink(SHM_NAME);
        exit(EXIT_FAILURE);
    }

    // 父进程先映射共享内存
    void *shared_addr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (shared_addr == MAP_FAILED) {
        perror("mmap failed");
        close(shm_fd);
        shm_unlink(SHM_NAME);
        exit(EXIT_FAILURE);
    }
    printf("父进程映射地址: %p\n", shared_addr);

    // 写入测试数据
    strcpy((char*)shared_addr, "Hello from parent!");

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork failed");
        munmap(shared_addr, SHM_SIZE);
        close(shm_fd);
        shm_unlink(SHM_NAME);
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // 子进程直接用继承的映射地址,无需重新mmap
        printf("子进程映射地址: %p\n", shared_addr);
        printf("子进程读取到的数据: %s\n", (char*)shared_addr);

        // 修改数据,父进程能看到变化
        strcpy((char*)shared_addr, "Hello from child!");

        munmap(shared_addr, SHM_SIZE);
        close(shm_fd);
        exit(EXIT_SUCCESS);
    } else {
        // 父进程等待子进程结束
        wait(NULL);
        printf("父进程读取到修改后的数据: %s\n", (char*)shared_addr);

        // 清理资源
        munmap(shared_addr, SHM_SIZE);
        close(shm_fd);
        shm_unlink(SHM_NAME); // 删除共享内存对象,避免残留
        exit(EXIT_SUCCESS);
    }
}

方式二:用MAP_FIXED强制指定相同地址(适合特殊场景)

如果因为某些原因,你必须让父子进程各自调用mmap,那可以用MAP_FIXED标志强制指定同一个地址。步骤是:

  1. 父进程先拿到一个可用地址(比如临时申请匿名内存再释放)
  2. fork后,子进程用这个地址+MAP_FIXED去映射同一个共享内存对象

⚠️ 注意:MAP_FIXED是强操作——如果指定的地址已经被其他映射占用,内核会直接覆盖原来的映射,可能导致程序崩溃,所以一定要先确保地址空闲。

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>

#define SHM_NAME "/my_shared_mem_fixed"
#define SHM_SIZE 4096

int main() {
    int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    if (shm_fd == -1) {
        perror("shm_open failed");
        exit(EXIT_FAILURE);
    }

    if (ftruncate(shm_fd, SHM_SIZE) == -1) {
        perror("ftruncate failed");
        close(shm_fd);
        shm_unlink(SHM_NAME);
        exit(EXIT_FAILURE);
    }

    // 先申请临时匿名内存,拿到一个空闲地址
    void *target_addr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    if (target_addr == MAP_FAILED) {
        perror("temp mmap failed");
        close(shm_fd);
        shm_unlink(SHM_NAME);
        exit(EXIT_FAILURE);
    }
    munmap(target_addr, SHM_SIZE); // 释放临时地址,确保它空闲

    // 父进程用指定地址映射共享内存
    void *parent_addr = mmap(target_addr, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, shm_fd, 0);
    if (parent_addr == MAP_FAILED) {
        perror("parent mmap with MAP_FIXED failed");
        close(shm_fd);
        shm_unlink(SHM_NAME);
        exit(EXIT_FAILURE);
    }
    printf("父进程映射地址: %p\n", parent_addr);

    strcpy((char*)parent_addr, "Parent's initial data");

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork failed");
        munmap(parent_addr, SHM_SIZE);
        close(shm_fd);
        shm_unlink(SHM_NAME);
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // 子进程用同一个地址+MAP_FIXED映射
        void *child_addr = mmap(target_addr, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, shm_fd, 0);
        if (child_addr == MAP_FAILED) {
            perror("child mmap with MAP_FIXED failed");
            close(shm_fd);
            exit(EXIT_FAILURE);
        }
        printf("子进程映射地址: %p\n", child_addr);
        printf("子进程读取数据: %s\n", (char*)child_addr);

        strcpy((char*)child_addr, "Child modified data");

        munmap(child_addr, SHM_SIZE);
        close(shm_fd);
        exit(EXIT_SUCCESS);
    } else {
        wait(NULL);
        printf("父进程读取修改后的数据: %s\n", (char*)parent_addr);

        munmap(parent_addr, SHM_SIZE);
        close(shm_fd);
        shm_unlink(SHM_NAME);
        exit(EXIT_SUCCESS);
    }
}

额外问题解答

  • 能不能用shm_open实现共享内存? 当然可以!shm_open是POSIX标准的共享内存接口,通过文件描述符操作,和普通文件用法类似,比shmget更灵活易用,完全能满足你的需求。
  • 必须改用shmget吗? 完全不需要,shmget是老旧的System V接口,现在更推荐使用POSIX的shm_open+mmap组合。

内容的提问来源于stack exchange,提问作者Igor Liferenko

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:45:40