信号量程序引发死锁:双进程同步打印'00110011...'问题排查
排查与修复进程同步死锁问题
嘿,我来帮你搞定这个死锁问题!你的代码现在有两个核心问题,直接导致了同步失效甚至看似死锁的情况:
问题根源分析
- 信号量未真正实现进程间共享:你在栈上声明了
sem_t smid,然后调用sem_init(&smid, 1, 1)。虽然pshared参数设为1表示要进程间共享,但栈内存是进程的私有地址空间——fork之后,父子进程的smid是完全独立的副本,彼此的sem_wait/sem_post操作完全不互通,等于两个进程各玩各的信号量,根本起不到同步作用。如果父进程也有等待信号量的逻辑,就会出现“各自卡在自己的信号量等待上”的假死锁。 - 缺失父进程的核心逻辑:你只实现了子进程打印
00的逻辑,但要输出00110011...的交替序列,父进程需要负责打印11,并且和子进程通过信号量交替执行。
修复方案:使用命名信号量(简单可靠)
推荐用命名信号量(sem_open)来实现进程间同步,不需要手动管理共享内存,代码更简洁:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <semaphore.h> #include <fcntl.h> #include <sys/stat.h> #define SEM_NAME "/print_sync_sem" int main() { // 创建命名信号量,初始值为1,权限设为0666 sem_t *sem = sem_open(SEM_NAME, O_CREAT | O_EXCL, 0666, 1); if (sem == SEM_FAILED) { perror("sem_open"); exit(EXIT_FAILURE); } pid_t pid = fork(); if (pid == -1) { perror("fork"); sem_close(sem); sem_unlink(SEM_NAME); exit(EXIT_FAILURE); } if (pid == 0) { // 子进程:负责打印"00" while (1) { if (sem_wait(sem) == -1) { perror("sem_wait (child)"); sem_close(sem); exit(EXIT_FAILURE); } printf("00"); fflush(stdout); // 强制刷新输出缓冲区,避免输出乱序 sleep(1); if (sem_post(sem) == -1) { perror("sem_post (child)"); sem_close(sem); exit(EXIT_FAILURE); } } } else { // 父进程:负责打印"11" while (1) { if (sem_wait(sem) == -1) { perror("sem_wait (parent)"); sem_close(sem); sem_unlink(SEM_NAME); exit(EXIT_FAILURE); } printf("11"); fflush(stdout); sleep(1); if (sem_post(sem) == -1) { perror("sem_post (parent)"); sem_close(sem); sem_unlink(SEM_NAME); exit(EXIT_FAILURE); } } // 无限循环不会执行到这里,正常退出时要清理信号量 sem_close(sem); sem_unlink(SEM_NAME); } return 0; }
备选方案:共享内存+sem_init
如果坚持要用sem_init,需要把信号量放在共享内存区域,确保父子进程访问的是同一个信号量:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <semaphore.h> #include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> int main() { // 创建共享内存对象 int shm_fd = shm_open("/print_sync_shm", O_CREAT | O_RDWR, 0666); if (shm_fd == -1) { perror("shm_open"); exit(EXIT_FAILURE); } // 设置共享内存大小为sem_t的尺寸 if (ftruncate(shm_fd, sizeof(sem_t)) == -1) { perror("ftruncate"); close(shm_fd); shm_unlink("/print_sync_shm"); exit(EXIT_FAILURE); } // 映射共享内存到进程地址空间 sem_t *sem = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0); if (sem == MAP_FAILED) { perror("mmap"); close(shm_fd); shm_unlink("/print_sync_shm"); exit(EXIT_FAILURE); } close(shm_fd); // 映射完成后可关闭文件描述符 // 初始化共享信号量,pshared=1表示进程间共享 if (sem_init(sem, 1, 1) == -1) { perror("sem_init"); munmap(sem, sizeof(sem_t)); shm_unlink("/print_sync_shm"); exit(EXIT_FAILURE); } pid_t pid = fork(); if (pid == -1) { perror("fork"); sem_destroy(sem); munmap(sem, sizeof(sem_t)); shm_unlink("/print_sync_shm"); exit(EXIT_FAILURE); } if (pid == 0) { // 子进程打印"00" while (1) { if (sem_wait(sem) == -1) { perror("sem_wait (child)"); munmap(sem, sizeof(sem_t)); exit(EXIT_FAILURE); } printf("00"); fflush(stdout); sleep(1); if (sem_post(sem) == -1) { perror("sem_post (child)"); munmap(sem, sizeof(sem_t)); exit(EXIT_FAILURE); } } } else { // 父进程打印"11" while (1) { if (sem_wait(sem) == -1) { perror("sem_wait (parent)"); sem_destroy(sem); munmap(sem, sizeof(sem_t)); shm_unlink("/print_sync_shm"); exit(EXIT_FAILURE); } printf("11"); fflush(stdout); sleep(1); if (sem_post(sem) == -1) { perror("sem_post (parent)"); sem_destroy(sem); munmap(sem, sizeof(sem_t)); shm_unlink("/print_sync_shm"); exit(EXIT_FAILURE); } } // 清理资源(无限循环不会执行到这里) sem_destroy(sem); munmap(sem, sizeof(sem_t)); shm_unlink("/print_sync_shm"); } return 0; }
关键细节提醒
- 一定要用
fflush(stdout):printf是行缓冲模式,不强制刷新的话,输出会堆积在缓冲区里,看不到交替的预期效果。 - 命名信号量使用后要
sem_unlink:否则下次运行程序会因为信号量已存在而报错。 - 共享内存使用后要
shm_unlink:避免系统中残留无用的共享内存对象。
内容的提问来源于stack exchange,提问作者Debanik Dawn
相关产品推荐
相关产品推荐

