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

信号量程序引发死锁:双进程同步打印'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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:53:15