使用fork与Semaphore控制进程输出顺序问题求助
进程同步问题:信号量无法触发等待结束的原因分析
我创建了p2-p6共5个子进程,期望输出顺序为p4 output、p3 output、p5 output、p1 output、p6 output、p2 output,但始终无法理解为何sem_post无法触发sem_wait结束等待,怀疑自己对信号量的理解有误,相关代码如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <semaphore.h> #include <sys/types.h> #include <sys/wait.h> #include <pthread.h> int main(){ pid_t p2, p3, p4, p5, p6; sem_t sem_p1, sem_p2, sem_p3, sem_p4, sem_p5, sem_p6; sem_init(&sem_p1, 0, 0); sem_init(&sem_p2, 0, 0); sem_init(&sem_p3, 0, 0); sem_init(&sem_p4, 0, 0); sem_init(&sem_p5, 0, 0); sem_init(&sem_p6, 0, 0); p2 = fork(); if(p2 < 0){ perror("fork p1 -> p2 failed"); return 1; } if(p2 == 0){ printf("hi"); sem_wait(&sem_p2); printf("p2 output"); return 0; } p3 = fork(); if(p3 < 0){ perror("fork p1 -> p3 failed"); return 1; } if(p3 == 0){ sem_wait(&sem_p3); printf("p3 output"); sem_post(&sem_p5); return 0; } p4 = fork(); if(p4 < 0){ perror("fork p1 -> p4 failed"); return 1; } if(p4 == 0){ sem_wait(&sem_p4); printf("p4 output"); sem_post(&sem_p3); return 0; } p5 = fork(); if(p5 < 0){ perror("fork p1 -> p5 failed"); return 1; } if(p5 == 0){ sem_wait(&sem_p5); printf("p5 output"); sem_post(&sem_p1); return 0; } p6 = fork(); if(p6 < 0){ perror("fork p1 -> p6 failed"); return 1; } if(p6 == 0){ sem_wait(&sem_p6); printf("p6 output"); sem_post(&sem_p2); return 0; } sem_post(&sem_p4); sem_wait(&sem_p1); printf("p1 output"); sem_post(&sem_p6); return 0; }
核心问题:信号量的作用域误解
你当前代码的问题在于信号量的初始化参数错误:
sem_init的第二个参数设为0,表示这是线程间同步的信号量,信号量存储在进程私有内存中。- fork创建的子进程会复制父进程的内存空间,每个子进程里的信号量都是独立副本。父进程或其他子进程调用
sem_post操作的是自己副本的信号量,完全无法影响其他进程里的sem_wait,自然无法触发等待结束。
解决方案:使用进程间共享的信号量
要实现进程间同步,必须让信号量被所有进程共享,有两种常用方式:
方式1:使用命名信号量(推荐)
通过sem_open创建命名信号量,不同进程可以通过同一个名称访问同一个信号量实例:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <semaphore.h> #include <sys/types.h> #include <sys/wait.h> #include <fcntl.h> // 定义唯一的信号量名称 #define SEM_P1 "/sem_p1" #define SEM_P2 "/sem_p2" #define SEM_P3 "/sem_p3" #define SEM_P4 "/sem_p4" #define SEM_P5 "/sem_p5" #define SEM_P6 "/sem_p6" int main(){ pid_t p2, p3, p4, p5, p6; sem_t *sem_p1, *sem_p2, *sem_p3, *sem_p4, *sem_p5, *sem_p6; // 创建命名信号量,初始值0,权限0644 sem_p1 = sem_open(SEM_P1, O_CREAT | O_EXCL, 0644, 0); sem_p2 = sem_open(SEM_P2, O_CREAT | O_EXCL, 0644, 0); sem_p3 = sem_open(SEM_P3, O_CREAT | O_EXCL, 0644, 0); sem_p4 = sem_open(SEM_P4, O_CREAT | O_EXCL, 0644, 0); sem_p5 = sem_open(SEM_P5, O_CREAT | O_EXCL, 0644, 0); sem_p6 = sem_open(SEM_P6, O_CREAT | O_EXCL, 0644, 0); if(sem_p1 == SEM_FAILED || sem_p2 == SEM_FAILED || sem_p3 == SEM_FAILED || sem_p4 == SEM_FAILED || sem_p5 == SEM_FAILED || sem_p6 == SEM_FAILED){ perror("sem_open failed"); return 1; } p2 = fork(); if(p2 < 0){ perror("fork p1 -> p2 failed"); return 1; } if(p2 == 0){ sem_wait(sem_p2); printf("p2 output\n"); sem_close(sem_p2); return 0; } p3 = fork(); if(p3 < 0){ perror("fork p1 -> p3 failed"); return 1; } if(p3 == 0){ sem_wait(sem_p3); printf("p3 output\n"); sem_post(sem_p5); sem_close(sem_p3); sem_close(sem_p5); return 0; } p4 = fork(); if(p4 < 0){ perror("fork p1 -> p4 failed"); return 1; } if(p4 == 0){ sem_wait(sem_p4); printf("p4 output\n"); sem_post(sem_p3); sem_close(sem_p4); sem_close(sem_p3); return 0; } p5 = fork(); if(p5 < 0){ perror("fork p1 -> p5 failed"); return 1; } if(p5 == 0){ sem_wait(sem_p5); printf("p5 output\n"); sem_post(sem_p1); sem_close(sem_p5); sem_close(sem_p1); return 0; } p6 = fork(); if(p6 < 0){ perror("fork p1 -> p6 failed"); return 1; } if(p6 == 0){ sem_wait(sem_p6); printf("p6 output\n"); sem_post(sem_p2); sem_close(sem_p6); sem_close(sem_p2); return 0; } sem_post(sem_p4); sem_wait(sem_p1); printf("p1 output\n"); sem_post(sem_p6); // 等待所有子进程退出 waitpid(p2, NULL, 0); waitpid(p3, NULL, 0); waitpid(p4, NULL, 0); waitpid(p5, NULL, 0); waitpid(p6, NULL, 0); // 清理信号量资源 sem_close(sem_p1); sem_close(sem_p2); sem_close(sem_p3); sem_close(sem_p4); sem_close(sem_p5); sem_close(sem_p6); sem_unlink(SEM_P1); sem_unlink(SEM_P2); sem_unlink(SEM_P3); sem_unlink(SEM_P4); sem_unlink(SEM_P5); sem_unlink(SEM_P6); return 0; }
方式2:使用共享内存中的无名信号量
将信号量存储在shm_open创建的共享内存区域中,再通过mmap映射到各个进程的地址空间,实现共享。这种方式步骤更繁琐,不如命名信号量直观。
额外说明
- 原代码中p2进程的
printf("hi")会提前输出,不符合预期顺序,已在修改后的代码中移除。 - 使用命名信号量后,记得在程序退出时调用
sem_unlink删除信号量,避免残留系统资源。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

