共享内存练习问题:消费者进程无法接收并打印数据
问题分析与修复方案
你的代码存在几个核心问题,导致消费者无法打印内容,逐一拆解:
1. 进程创建逻辑错误
两次fork()会生成4个进程,而非你需要的1个生产者+1个消费者。你对进程身份的判断只能匹配其中两个进程,剩下的进程会空转,同时这种方式也会导致共享内存的创建混乱。
2. 共享内存创建时机错误
每个进程都调用了shmget(IPC_PRIVATE, ...),IPC_PRIVATE会为每个调用进程创建独立的私有共享内存段,这意味着生产者和消费者根本不在同一块内存上操作,自然读不到数据。必须在fork之前创建共享内存,子进程才会继承同一个shmid,访问同一块内存。
3. 无进程同步机制
生产者和消费者执行完全异步,消费者可能在生产者还没写入数据时就开始读取,或者读取的位置从未被写入(共享内存初始值随机),导致没有有效输出。
4. 其他细节问题
- 未初始化随机数生成器
srand(time(NULL)),rand()会生成固定序列; - 消费者一直读取
buffer[0],但生产者循环写入i%32的位置,大概率buffer[0]不会被持续写入; - 未清理共享内存,会造成系统资源泄漏;
- 未等待子进程结束,父进程提前退出可能导致输出异常。
修复后的代码
#include <stdio.h> #include <stdlib.h> #include <sys/shm.h> #include <sys/stat.h> #include <unistd.h> #include <sys/wait.h> #include <time.h> #include <semaphore.h> #include <sys/mman.h> // 定义共享内存结构体,包含缓冲区和同步信号量 typedef struct { char buffer[32]; sem_t empty; // 标记缓冲区空位 sem_t full; // 标记缓冲区已填充位 int write_idx; // 生产者写入位置 int read_idx; // 消费者读取位置 } SharedData; int main() { int shmid; SharedData *shared; pid_t producer_pid, consumer_pid; // 1. 先创建共享内存,大小为结构体大小 shmid = shmget(IPC_PRIVATE, sizeof(SharedData), IPC_CREAT | S_IRUSR | S_IWUSR); if (shmid == -1) { perror("shmget failed"); exit(EXIT_FAILURE); } // 映射共享内存到当前进程地址空间 shared = (SharedData *)shmat(shmid, NULL, 0); if (shared == (void *)-1) { perror("shmat failed"); exit(EXIT_FAILURE); } // 2. 初始化信号量(必须在fork前完成,确保子进程继承初始化后的信号量) sem_init(&shared->empty, 1, 32); // 初始有32个空位,pshared=1表示进程间共享 sem_init(&shared->full, 1, 0); // 初始没有已填充位 shared->write_idx = 0; shared->read_idx = 0; // 3. 创建生产者进程 producer_pid = fork(); if (producer_pid == -1) { perror("fork producer failed"); exit(EXIT_FAILURE); } if (producer_pid == 0) { // 生产者进程 srand(time(NULL) + getpid()); // 用进程ID区分随机序列 char alphabet[26]; for (int k = 0; k < 26; k++) { alphabet[k] = 'A' + k; } for (int i = 0; i < 128; i++) { sem_wait(&shared->empty); // 等待空位 // 生成随机字母写入缓冲区 char c = alphabet[rand() % 26]; shared->buffer[shared->write_idx] = c; shared->write_idx = (shared->write_idx + 1) % 32; sem_post(&shared->full); // 标记有新数据 usleep(10000); // 模拟生产耗时,可选 } // 生产者结束,分离共享内存 shmdt(shared); exit(EXIT_SUCCESS); } // 4. 创建消费者进程 consumer_pid = fork(); if (consumer_pid == -1) { perror("fork consumer failed"); exit(EXIT_FAILURE); } if (consumer_pid == 0) { // 消费者进程 for (int i = 0; i < 128; i++) { sem_wait(&shared->full); // 等待有数据 // 读取缓冲区数据并打印 char c = shared->buffer[shared->read_idx]; printf("%c ", c); fflush(stdout); // 强制刷新输出缓冲区,避免卡顿 shared->read_idx = (shared->read_idx + 1) % 32; sem_post(&shared->empty); // 标记空位可用 usleep(15000); // 模拟消费耗时,可选 } printf("\n"); // 消费者结束,分离共享内存 shmdt(shared); exit(EXIT_SUCCESS); } // 5. 父进程等待子进程全部结束 waitpid(producer_pid, NULL, 0); waitpid(consumer_pid, NULL, 0); // 6. 销毁信号量,删除共享内存 sem_destroy(&shared->empty); sem_destroy(&shared->full); shmdt(shared); shmctl(shmid, IPC_RMID, NULL); return 0; }
编译与运行
编译时需要链接实时库:
gcc -o shm_prod_cons shm_prod_cons.c -lrt
运行后就能看到消费者打印出生产者生成的随机字母。
内容的提问来源于stack exchange,提问作者Gustavo Soares
相关产品推荐
相关产品推荐

