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
相关产品推荐
相关产品推荐

