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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 13:27:34