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

如何在POSIX信号量上实现等待零值的进程同步功能?

POSIX信号量实现“等待所有进程完成初始化”的非忙等方案

你提到的SystemV信号量sem_op=0等待值为零的操作,POSIX信号量API确实没有直接提供,但可以通过几种非忙等的方式实现你的同步需求(主进程+N个子进程共M=N+1个进程,全部完成init()后再继续执行后续逻辑)。

方案一:共享原子计数器 + POSIX信号量

这是最通用的实现方式,依赖原子操作保证计数的正确性,信号量实现阻塞等待。

实现逻辑

  1. 创建共享内存存储原子计数器,初始值为0;同时创建一个初始值为0的POSIX信号量。
  2. 每个进程完成init()后:
    • 原子递增计数器
    • 如果递增后的值等于总进程数M,就循环调用sem_post() M次,唤醒所有等待的进程
    • 否则调用sem_wait()阻塞,直到被唤醒

代码示例

主进程初始化共享资源的代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <stdatomic.h>

#define M 5 // 总进程数,比如1主+4子
#define SHM_NAME "/sync_shm"
#define SEM_NAME "/sync_sem"

typedef struct {
    atomic_int count;
} SyncData;

void init() {
    // 你的初始化逻辑
    sleep(1);
    printf("%d: Init done\n", getpid());
}

int main() {
    // 创建共享内存
    int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    if (shm_fd == -1) { perror("shm_open"); exit(1); }
    ftruncate(shm_fd, sizeof(SyncData));
    SyncData *sync_data = mmap(NULL, sizeof(SyncData), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
    if (sync_data == MAP_FAILED) { perror("mmap"); exit(1); }
    atomic_init(&sync_data->count, 0);

    // 创建信号量(初始值0)
    sem_t *sem = sem_open(SEM_NAME, O_CREAT | O_RDWR, 0666, 0);
    if (sem == SEM_FAILED) { perror("sem_open"); exit(1); }

    // Fork创建子进程(示例中创建4个子进程)
    for (int i = 0; i < M-1; i++) {
        pid_t pid = fork();
        if (pid == 0) {
            // 子进程执行同步逻辑
            init();
            
            int current = atomic_fetch_add(&sync_data->count, 1);
            if (current + 1 == M) {
                for (int j = 0; j < M; j++) {
                    sem_post(sem);
                }
            }
            sem_wait(sem);

            printf("%d: Proceeding to main logic\n", getpid());
            exit(0);
        }
    }

    // 主进程的同步逻辑
    init();
    
    int current = atomic_fetch_add(&sync_data->count, 1);
    if (current + 1 == M) {
        for (int j = 0; j < M; j++) {
            sem_post(sem);
        }
    }
    sem_wait(sem);

    printf("%d: Proceeding to main logic\n", getpid());

    // 等待子进程退出
    for (int i = 0; i < M-1; i++) {
        wait(NULL);
    }

    // 清理资源
    sem_close(sem);
    sem_unlink(SEM_NAME);
    munmap(sync_data, sizeof(SyncData));
    shm_unlink(SHM_NAME);
    return 0;
}

方案二:两个POSIX信号量配合

用两个信号量模拟SystemV的“递减+等待零”逻辑:

  • sem_counter:初始值为M,每个进程调用sem_wait()递减计数
  • sem_barrier:初始值为0,最后一个进程递减完成后,调用sem_post() M次唤醒所有进程

代码示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/wait.h>

#define M 5
#define SEM_COUNTER "/sem_counter"
#define SEM_BARRIER "/sem_barrier"

void init() {
    sleep(1);
    printf("%d: Init done\n", getpid());
}

int main() {
    sem_t *sem_counter = sem_open(SEM_COUNTER, O_CREAT | O_RDWR, 0666, M);
    sem_t *sem_barrier = sem_open(SEM_BARRIER, O_CREAT | O_RDWR, 0666, 0);

    // 创建子进程
    for (int i = 0; i < M-1; i++) {
        if (fork() == 0) {
            init();
            
            sem_wait(sem_counter);
            int val;
            sem_getvalue(sem_counter, &val);
            if (val == 0) {
                for (int j = 0; j < M; j++) {
                    sem_post(sem_barrier);
                }
            }
            sem_wait(sem_barrier);

            printf("%d: Proceeding to main logic\n", getpid());
            exit(0);
        }
    }

    // 主进程逻辑
    init();
    
    sem_wait(sem_counter);
    int val;
    sem_getvalue(sem_counter, &val);
    if (val == 0) {
        for (int j = 0; j < M; j++) {
            sem_post(sem_barrier);
        }
    }
    sem_wait(sem_barrier);

    printf("%d: Proceeding to main logic\n", getpid());

    // 等待子进程
    for (int i = 0; i < M-1; i++) {
        wait(NULL);
    }

    // 清理
    sem_close(sem_counter);
    sem_close(sem_barrier);
    sem_unlink(SEM_COUNTER);
    sem_unlink(SEM_BARRIER);
    return 0;
}

方案三:进程间pthread屏障

如果你的系统支持PTHREAD_PROCESS_SHARED属性,可以用pthread屏障实现更简洁的同步:

  1. 将屏障对象放在共享内存中,设置进程共享属性
  2. 每个进程完成init()后调用pthread_barrier_wait(),所有进程会阻塞直到全部到达屏障点

代码示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <pthread.h>
#include <sys/wait.h>

#define M 5
#define SHM_NAME "/barrier_shm"

void init() {
    sleep(1);
    printf("%d: Init done\n", getpid());
}

int main() {
    // 创建共享内存存储屏障
    int shm_fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
    ftruncate(shm_fd, sizeof(pthread_barrier_t));
    pthread_barrier_t *barrier = mmap(NULL, sizeof(pthread_barrier_t), PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);

    // 初始化屏障,设置进程共享属性
    pthread_barrierattr_t attr;
    pthread_barrierattr_init(&attr);
    pthread_barrierattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
    pthread_barrier_init(barrier, &attr, M);
    pthread_barrierattr_destroy(&attr);

    // 创建子进程
    for (int i = 0; i < M-1; i++) {
        if (fork() == 0) {
            init();
            pthread_barrier_wait(barrier);
            printf("%d: Proceeding to main logic\n", getpid());
            exit(0);
        }
    }

    // 主进程逻辑
    init();
    pthread_barrier_wait(barrier);
    printf("%d: Proceeding to main logic\n", getpid());

    // 等待子进程
    for (int i = 0; i < M-1; i++) {
        wait(NULL);
    }

    // 清理
    pthread_barrier_destroy(barrier);
    munmap(barrier, sizeof(pthread_barrier_t));
    shm_unlink(SHM_NAME);
    return 0;
}

以上三种方案都不需要忙等,能可靠实现你的同步需求。

内容的提问来源于stack exchange,提问作者kiryls

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 20:29:54