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

Linux IPC信号量行为异常求助:代码结果不稳定原因排查

问题排查:Linux信号量父子进程同步异常问题

核心原因分析

你碰到的问题本质是信号量变量没放在共享内存区域:

  • 父子进程是独立的虚拟地址空间,你打印的sem_t mutex地址是虚拟地址,看起来一致但实际对应不同的物理内存块——两个进程操作的是各自的信号量副本,根本没起到同步作用。
  • 打印语句的存在只是无意中引入了短暂延时,让进程执行时序刚好巧合符合预期,掩盖了根本问题;一旦去掉打印,进程调度的随机性就会导致同步失效,结果时对时错。

修复方案

要让父子进程共享信号量,必须把sem_t变量放在共享内存中,两种常用实现方式:

方案1:mmap匿名共享内存

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/wait.h>

int main() {
    // 创建匿名共享内存,存放信号量
    sem_t *mutex = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (mutex == MAP_FAILED) {
        perror("mmap failed");
        exit(EXIT_FAILURE);
    }

    // 初始化共享信号量,pshared=1表示允许跨进程共享
    if (sem_init(mutex, 1, 1) == -1) {
        perror("sem_init failed");
        munmap(mutex, sizeof(sem_t));
        exit(EXIT_FAILURE);
    }

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork failed");
        sem_destroy(mutex);
        munmap(mutex, sizeof(sem_t));
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // 子进程逻辑
        sem_wait(mutex);
        printf("子进程进入临界区\n");
        sleep(1); // 模拟临界区操作
        printf("子进程退出临界区\n");
        sem_post(mutex);
        munmap(mutex, sizeof(sem_t));
        exit(EXIT_SUCCESS);
    } else {
        // 父进程逻辑
        sem_wait(mutex);
        printf("父进程进入临界区\n");
        sleep(1);
        printf("父进程退出临界区\n");
        sem_post(mutex);
        wait(NULL);
        sem_destroy(mutex);
        munmap(mutex, sizeof(sem_t));
        exit(EXIT_SUCCESS);
    }
}

方案2:命名信号量(更简洁)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <fcntl.h>

#define SEM_NAME "/my_shared_sem"

int main() {
    // 创建命名信号量,O_CREAT表示不存在则创建,O_EXCL确保唯一,权限0666,初始值1
    sem_t *mutex = sem_open(SEM_NAME, O_CREAT | O_EXCL, 0666, 1);
    if (mutex == SEM_FAILED) {
        perror("sem_open failed");
        exit(EXIT_FAILURE);
    }

    pid_t pid = fork();
    if (pid == -1) {
        perror("fork failed");
        sem_close(mutex);
        sem_unlink(SEM_NAME);
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        sem_wait(mutex);
        printf("子进程进入临界区\n");
        sleep(1);
        printf("子进程退出临界区\n");
        sem_post(mutex);
        sem_close(mutex);
        exit(EXIT_SUCCESS);
    } else {
        sem_wait(mutex);
        printf("父进程进入临界区\n");
        sleep(1);
        printf("父进程退出临界区\n");
        sem_post(mutex);
        wait(NULL);
        sem_close(mutex);
        sem_unlink(SEM_NAME); // 删除命名信号量,避免残留
        exit(EXIT_SUCCESS);
    }
}

关键注意点

  • 用sem_init时,第二个参数pshared必须设为1,否则信号量只能用于线程同步,不能跨进程。
  • 命名信号量使用后必须调用sem_unlink删除,否则系统中会残留该信号量。
  • 共享内存方式的信号量,进程退出前要调用munmap释放内存、sem_destroy销毁信号量。

内容的提问来源于stack exchange,提问作者Liu Yuqing

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 15:17:34