使用POSIX命名信号量同步父子进程遇阻塞问题求助
问题描述
尝试通过两个进程向文件写入0-100的递增数字,父进程写奇数,子进程写偶数,期望文件内容为:
1 2 3 4 5 6 ...... 100
采用POSIX命名信号量实现同步,但运行程序后仅输出进程启动提示,无数字输出,程序卡住:
####Parent################# ####Child#################
代码问题分析
- 信号量未跨进程共享打开:父进程仅打开
s_odd信号量,子进程仅打开s_even信号量,导致双方调用对方信号量的sem_post/sem_wait时,使用未初始化的野指针,触发未定义行为,直接导致进程卡住。 - 信号量创建冲突:双方都使用
O_CREAT | O_EXCL标志,若一方先创建信号量,另一方会因O_EXCL导致sem_open失败,无法获取信号量句柄。 - 文件缓冲区未刷新:父子进程拥有独立的文件指针缓冲区,写入后未刷新会导致内容无法立即写入文件,甚至出现输出错位。
- 循环逻辑冗余:原代码循环中每次判断奇偶,增加不必要的分支,结合信号量初始化逻辑,可直接遍历目标数字类型优化流程。
修复方案
- 统一信号量打开逻辑:父子进程都打开两个信号量,由父进程负责初始化信号量值,子进程直接打开已存在的信号量。
- 移除
O_EXCL标志:避免创建信号量时的冲突,确保后续进程能正常打开已存在的信号量。 - 写入后刷新文件缓冲区:每次
fprintf后调用fflush(ptr),确保内容立即写入文件,避免缓冲区问题。 - 优化循环逻辑:直接遍历奇数/偶数,减少分支判断,提高代码效率。
- 规范信号量生命周期:父进程在子进程退出后再清理信号量,避免子进程仍在使用时信号量被提前删除。
修复后的完整代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <semaphore.h> #include <sys/types.h> #include <fcntl.h> #include <sys/wait.h> FILE *ptr; sem_t *s_even, *s_odd; const char *even_sem = "even_sem", *odd_sem = "odd_sem"; void fileWR(); void parent(); void child(); int main(void) { ptr = fopen("Semaphore.txt", "w+"); if (ptr == NULL) { fprintf(stderr, "Failed to open file!\n"); exit(EXIT_FAILURE); } fileWR(); return 0; } void fileWR() { // 清理残留的旧信号量 sem_unlink(even_sem); sem_unlink(odd_sem); // 父进程创建并初始化信号量:s_even初始1让子进程先写,s_odd初始0让父进程等待 s_even = sem_open(even_sem, O_CREAT, 0644, 1); s_odd = sem_open(odd_sem, O_CREAT, 0644, 0); if (s_even == SEM_FAILED || s_odd == SEM_FAILED) { perror("sem_open failed"); exit(EXIT_FAILURE); } pid_t pid = fork(); if (pid == -1) { perror("fork failed"); exit(EXIT_FAILURE); } if (pid != 0) { puts("####Parent#################"); parent(); wait(NULL); // 父进程最后清理资源 sem_close(s_even); sem_close(s_odd); sem_unlink(even_sem); sem_unlink(odd_sem); fclose(ptr); } else { // 子进程打开已存在的信号量 s_even = sem_open(even_sem, 0); s_odd = sem_open(odd_sem, 0); if (s_even == SEM_FAILED || s_odd == SEM_FAILED) { perror("child sem_open failed"); exit(EXIT_FAILURE); } puts("####Child#################"); child(); sem_close(s_even); sem_close(s_odd); fclose(ptr); exit(EXIT_SUCCESS); } } void parent() { // 直接遍历所有奇数 for (int i = 1; i <= 100; i += 2) { sem_wait(s_odd); fprintf(ptr, "%d ", i); printf("i=%d ", i); fflush(ptr); // 强制刷新缓冲区 sem_post(s_even); } } void child() { // 直接遍历所有偶数 for (int j = 0; j <= 100; j += 2) { sem_wait(s_even); fprintf(ptr, "%d ", j); printf("j=%d ", j); fflush(ptr); // 强制刷新缓冲区 sem_post(s_odd); } }
内容的提问来源于stack exchange,提问作者ya22y
相关产品推荐
相关产品推荐

