C语言信号量与共享内存实现生产者消费者:消费者始终取0
问题分析与修复
你的代码存在三个核心问题,导致消费者总是读到0:
1. 索引变量未放入共享内存
head和tail是main函数的局部变量,fork()后子进程会复制一份独立的副本,生产者和消费者各自维护自己的索引,完全不共享。消费者始终从初始的0位置读取,而生产者写入的是自己副本的索引位置,两者没有交集。
2. 信号量初始值搞反
full信号量表示缓冲区中已填充的元素数量,初始时缓冲区为空,应该设为0empty信号量表示缓冲区中空闲槽位的数量,初始时缓冲区全空,应该设为N
你当前的代码把两者初始值写反了,导致生产者一开始就阻塞在sem_wait(full),无法写入数据。
3. 共享内存结构使用错误
你定义了STRUCT_BUFFER结构体,但代码中直接把共享内存指针ptr当作int数组使用,没有正确访问结构体中的B数组;同时结构体里的head/tail被定义为指针,这完全没必要,应该用整数索引来标记环形缓冲区的位置。
修正后的完整代码
#include <sys/wait.h> #include <sys/mman.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <errno.h> #include <unistd.h> #include <time.h> #include <semaphore.h> #define SHM_NAME "/shm8" #define MUTEX "/mutex" #define FULL "/full" #define EMPTY "/empty" #define N 10 #define SYSC(value, command, message) if ((value=command) == -1){perror(message); exit(errno);} #define SYSCN(value, command, message) if ((value=command) == NULL){perror(message); exit(errno);} // 修改结构体:用整数索引代替指针,明确数组字段 typedef struct{ int B[N]; int head; // 读索引 int tail; // 写索引 } STRUCT_BUFFER; void put(int, STRUCT_BUFFER*, sem_t*, sem_t*, sem_t*); int get(STRUCT_BUFFER*, sem_t*, sem_t*, sem_t*); int main(){ int ret_value, mem_fd; void * ptr; pid_t pid; sem_t * mutex; sem_t * full; sem_t * empty; STRUCT_BUFFER *buf; // 创建共享内存 SYSC(mem_fd, shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666), "in shm_open"); SYSC(ret_value, ftruncate(mem_fd, sizeof(STRUCT_BUFFER)), "in ftruncate"); // 内存映射并转换为结构体指针 SYSCN(ptr, mmap(NULL, sizeof(STRUCT_BUFFER), PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, 0), "in mmap"); buf = (STRUCT_BUFFER*)ptr; // 初始化共享的索引 buf->head = 0; buf->tail = 0; SYSC(ret_value, close(mem_fd), "in close"); // 创建信号量:修正初始值 mutex = sem_open(MUTEX, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, 1); if(mutex == SEM_FAILED) { perror("In sem_open"); exit(EXIT_FAILURE); } // full初始为0:没有已填充的元素 full = sem_open(FULL, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, 0); if(full == SEM_FAILED) { perror("In sem_open"); exit(EXIT_FAILURE); } // empty初始为N:有N个空闲槽位 empty = sem_open(EMPTY, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, N); if(empty == SEM_FAILED) { perror("In sem_open"); exit(EXIT_FAILURE); } SYSC(pid, fork(), "in fork"); if(!pid) { // 消费者代码 printf("Consumer %d starts\n", getpid()); int toGet; while(1) { toGet = get(buf, mutex, full, empty); printf("Consumer %d consumed %d\n", getpid(), toGet); // 收到终止标记则退出 if(toGet == -1) { // 注:单消费者场景下无需放回标记,多消费者才需要 // put(toGet, buf, mutex, full, empty); break; } } sleep(1); printf("Consumer %d finishes\n",getpid()); exit(EXIT_SUCCESS); } else { // 生产者代码 printf("Producer %d starts\n",getpid()); int toPut; int exitToken = -1; // 生产数据 for(int i = 0 ; i < N ; i++) { toPut = i + 1; put(toPut, buf, mutex, full, empty); printf("Producer %d produced %d. Cycle:%d\n", getpid(), toPut, i); sleep(1); } // 放入终止标记 put(exitToken, buf, mutex, full, empty); printf("Producer %d produced termination token\n", getpid()); sleep(1); SYSC(ret_value, wait(NULL), "in wait"); // 清理资源 sem_close(mutex); sem_close(empty); sem_close(full); sem_unlink(MUTEX); sem_unlink(FULL); sem_unlink(EMPTY); munmap(ptr, sizeof(STRUCT_BUFFER)); shm_unlink(SHM_NAME); // 别忘了删除共享内存 printf("Producer %d finishes\n",getpid()); exit(EXIT_SUCCESS); } } void put(int toPut, STRUCT_BUFFER *buf, sem_t *mutex, sem_t *full, sem_t *empty) { sem_wait(empty); // 等待空闲槽位 sem_wait(mutex); // 写入共享缓冲区的指定位置 buf->B[buf->tail] = toPut; buf->tail = (buf->tail + 1) % N; sem_post(mutex); sem_post(full); // 已填充元素数量+1 } int get(STRUCT_BUFFER *buf, sem_t *mutex, sem_t *full, sem_t *empty) { sem_wait(full); // 等待有元素可读 sem_wait(mutex); // 读取共享缓冲区的指定位置 int toGet = buf->B[buf->head]; buf->head = (buf->head + 1) % N; sem_post(mutex); sem_post(empty); // 空闲槽位数量+1 return toGet; }
内容的提问来源于stack exchange,提问作者Grinza
相关产品推荐
相关产品推荐

