如何实现无sleep()的前后台进程同步IPC,完成数组输入与排序交互
进程同步问题解决方案
需求描述
存在前台和后台两个进程,共享一个整数数组:
- 前台进程从终端接收整数,逐个追加到共享数组
- 后台进程在每次添加元素后,将共享数组保持升序排序
- 所有元素添加并排序完成后,前台进程显示最终的共享数组
现有实现问题
当前代码依赖sleep()实现进程间同步:
- 主进程创建前台进程后sleep 1秒,确保前台先执行
- 前台、后台进程每次循环后各sleep 1秒,保证交替执行
移除sleep()后会出现:后台进程饥饿、后台先于前台执行、数据竞争等问题,且现有代码中sem_init(&D->mutex, 7, 1)的pshared参数错误(进程间共享需设为1,而非7),且信号量初始化放在前台进程可能导致重复初始化问题。
修正方案
1. 修复信号量初始化问题
进程间共享的信号量,sem_init的第二个参数pshared必须设为1,且初始化操作要放在主进程中,避免子进程重复初始化。
2. 使用双信号量实现严格交替同步
新增两个信号量替代单一互斥锁:
sem_fg:控制前台进程写入权限,初始值为1(允许前台先执行)sem_bg:控制后台进程排序权限,初始值为0(后台初始需等待前台写入)
前台写入一个元素后,唤醒后台进程并等待自己的下一次写入权限;后台完成排序后,唤醒前台进程并等待下一次排序权限,彻底摆脱对sleep()的依赖。
修改后的完整代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/mman.h> #include <sys/wait.h> #include <semaphore.h> #define MAX_BUFFER_SIZE 64 #define BUFFER_NAME "/shared" struct data { int buffer[MAX_BUFFER_SIZE]; sem_t sem_fg; // 前台进程写入信号量 sem_t sem_bg; // 后台进程排序信号量 }; int foreground_process(int size) { int element; int buff_fd = shm_open(BUFFER_NAME, O_RDWR, 0666); struct data *D = mmap(NULL, sizeof(struct data), PROT_READ | PROT_WRITE, MAP_SHARED, buff_fd, 0); for (int i = 0; i < size; ++i) { sem_wait(&D->sem_fg); // 等待写入权限 printf("Enter element %d: ", i); scanf("%d", &element); D->buffer[i] = element; sem_post(&D->sem_bg); // 唤醒后台进程排序 } // 等待最后一次排序完成 sem_wait(&D->sem_fg); printf("Final array: "); for (int i = 0; i < size; ++i) printf("%d ", D->buffer[i]); printf("\n"); sem_post(&D->sem_bg); // 释放后台信号量,让后台进程正常退出 munmap(D, sizeof(struct data)); close(buff_fd); exit(0); } int background_process(int size) { int prev_element, tmp; int buff_fd = shm_open(BUFFER_NAME, O_RDWR, 0666); struct data *D = mmap(NULL, sizeof(struct data), PROT_READ | PROT_WRITE, MAP_SHARED, buff_fd, 0); for (int i = 0; i < size; ++i) { sem_wait(&D->sem_bg); // 等待前台写入完成 prev_element = D->buffer[i]; printf("Got element %d at B[%d]!\n", D->buffer[i], i); // 插入排序逻辑 for (int j = 0; j < i; ++j) { if (D->buffer[j] > prev_element) { tmp = D->buffer[j]; D->buffer[j] = prev_element; prev_element = tmp; } } D->buffer[i] = prev_element; sem_post(&D->sem_fg); // 唤醒前台进程写入下一个元素 } munmap(D, sizeof(struct data)); close(buff_fd); exit(0); } int main(void) { int size; pid_t fg_pid, bg_pid; pid_t wpid; int status; printf("Size: "); scanf("%d", &size); // 创建并初始化共享内存 int buff_fd = shm_open(BUFFER_NAME, O_CREAT | O_RDWR, 0666); ftruncate(buff_fd, sizeof(struct data)); struct data *D = mmap(NULL, sizeof(struct data), PROT_READ | PROT_WRITE, MAP_SHARED, buff_fd, 0); // 初始化信号量:pshared=1表示进程间共享 sem_init(&D->sem_fg, 1, 1); sem_init(&D->sem_bg, 1, 0); munmap(D, sizeof(struct data)); close(buff_fd); // 创建前台进程 if ((fg_pid = fork()) == 0) foreground_process(size); else if (fg_pid < 0) perror("foreground process"); // 创建后台进程(无需sleep) if ((bg_pid = fork()) == 0) background_process(size); else if (bg_pid < 0) perror("background process"); // 等待所有子进程退出 while ((wpid = wait(&status)) != -1) fprintf(stderr, "Process %d exits with status %d\n", wpid, WEXITSTATUS(status)); // 清理共享内存和信号量 buff_fd = shm_open(BUFFER_NAME, O_RDWR, 0666); D = mmap(NULL, sizeof(struct data), PROT_READ | PROT_WRITE, MAP_SHARED, buff_fd, 0); sem_destroy(&D->sem_fg); sem_destroy(&D->sem_bg); munmap(D, sizeof(struct data)); close(buff_fd); shm_unlink(BUFFER_NAME); return 0; }
方案说明
- 双信号量
sem_fg和sem_bg实现了前台写入与后台排序的严格交替,完全不需要sleep() - 主进程统一初始化共享内存和信号量,避免子进程重复初始化问题
- 信号量的
pshared参数设为1,确保进程间共享有效 - 新增
munmap调用,规范释放映射的内存区域
内容的提问来源于stack exchange,提问作者3rdgrade-dropout
相关产品推荐
相关产品推荐

