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

多进程场景下如何在共享内存准备就绪前阻塞对其的访问?

多进程共享内存初始化竞态问题解决方案

你遇到的核心问题是「共享内存文件创建」和「初始化配置」两步操作不具备原子性,中间的时间窗口会导致其他进程访问未就绪的资源,你提到的flock方案是成本最低、可行性最高的解法,具体实现逻辑如下:

方案1:基于flock的排他锁控制(推荐)

flock是基于文件描述符的协同锁,刚好可以匹配共享内存文件的访问控制场景,不会出现死锁(进程异常退出后锁会自动释放),修改后的代码逻辑如下:

// 第一步:不管是创建还是打开,先获取共享内存fd
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
int is_creator = 0;
if (fd < 0) {
    // 不是创建者,循环尝试打开,规避权限未设置导致的打开失败
    while (1) {
        fd = shm_open(name, O_RDWR, S_IRUSR | S_IWUSR);
        if (fd >= 0) break;
        if (errno != EACCES) {
            // 非权限错误直接退出
            perror("shm_open failed");
            exit(1);
        }
        // 权限未就绪,短暂睡眠重试
        usleep(1000);
    }
} else {
    is_creator = 1;
}

// 第二步:所有进程拿到fd后先加排他锁,创建进程初始化完才会解锁,其他进程会阻塞在这里
if (flock(fd, LOCK_EX) < 0) {
    perror("flock failed");
    close(fd);
    exit(1);
}

void *shm_addr = NULL;
if (is_creator) {
    // 只有创建者需要做初始化操作
    ftruncate(fd, sizeof(pthread_mutex_t)); // 先设置共享内存大小,原实现漏了这步
    shm_addr = mmap(NULL, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    
    // 初始化进程间共享的mutex
    pthread_mutexattr_t mutex_attr;
    pthread_mutexattr_init(&mutex_attr);
    pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
    pthread_mutex_init((pthread_mutex_t *)shm_addr, &mutex_attr);
    pthread_mutexattr_destroy(&mutex_attr);
    
    // 初始化完成后改权限
    fchmod(fd, S_IRUSR | S_IWUSR);
} else {
    // 非创建者直接映射即可,此时已经加锁,共享内存一定是初始化完成的状态
    shm_addr = mmap(NULL, sizeof(pthread_mutex_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
}

// 解锁,后续正常使用mutex即可
flock(fd, LOCK_UN);
close(fd); // fd可以关闭,不影响mmap的映射和flock的状态

// 后续业务逻辑
pthread_mutex_lock((pthread_mutex_t *)shm_addr);
// 临界区操作
pthread_mutex_unlock((pthread_mutex_t *)shm_addr);

常见问题说明

  • 为什么非创建者要循环打开共享内存?
    创建者刚调用shm_open的时候权限是只读的,未执行fchmod之前其他进程用O_RDWR打开会返回权限错误,所以需要加重试逻辑,避免启动时报错。
  • 能不能直接让shm_open阻塞等待权限就绪?
    标准POSIX的shm_open没有提供这个能力,它的语义是只要文件存在、权限匹配就立刻返回,没有等待就绪的参数,所以必须通过上层加锁或者轮询的方式实现。
  • 有没有其他替代方案?
    也可以在共享内存的头部预留一个就绪标记位,创建者初始化完成后将标记位改为1,其他进程打开后轮询等待标记位变为1即可,不过这种方案的效率比flock低,且需要处理轮询间隔的权衡问题。

内容的提问来源于stack exchange,提问作者J. Doe

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 23:36:03