如何在POSIX信号量上实现等待零值的进程同步功能?
POSIX信号量实现“等待所有进程完成初始化”的非忙等方案
你提到的SystemV信号量sem_op=0等待值为零的操作,POSIX信号量API确实没有直接提供,但可以通过几种非忙等的方式实现你的同步需求(主进程+N个子进程共M=N+1个进程,全部完成init()后再继续执行后续逻辑)。
方案一:共享原子计数器 + POSIX信号量
这是最通用的实现方式,依赖原子操作保证计数的正确性,信号量实现阻塞等待。
实现逻辑
- 创建共享内存存储原子计数器,初始值为0;同时创建一个初始值为0的POSIX信号量。
- 每个进程完成
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屏障实现更简洁的同步:
- 将屏障对象放在共享内存中,设置进程共享属性
- 每个进程完成
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
相关产品推荐
相关产品推荐

