题目
设计一个生产者-消费者模型实现进程间通信
信息
- 类型:问答
- 难度:⭐⭐
考点
进程间通信机制选择,同步机制实现,资源竞争处理
快速回答
实现生产者-消费者模型的核心要点:
- 通信机制:使用共享内存传递数据,POSIX信号量控制同步
- 同步设计:两个信号量分别表示空槽位(free)和已用槽位(used)
- 缓冲区管理:循环队列实现固定大小的缓冲区
- 进程安全:信号量操作保证原子性,避免竞争条件
- 资源清理:进程退出时释放共享内存和信号量资源
问题背景
生产者-消费者模型是经典的进程同步问题,要求:
- 生产者进程生成数据放入缓冲区
- 消费者进程从缓冲区取出数据
- 当缓冲区满时生产者阻塞
- 当缓冲区空时消费者阻塞
- 避免竞争条件和死锁
解决方案(C语言实现)
#include <sys/mman.h>
#include <fcntl.h>
#include <semaphore.h>
#include <unistd.h>
#define BUFFER_SIZE 10
typedef struct {
int data[BUFFER_SIZE];
int in; // 生产者写入位置
int out; // 消费者读取位置
sem_t *free; // 空槽位信号量
sem_t *used; // 已用槽位信号量
} shm_buffer;
// 生产者进程
void producer() {
// 创建/打开共享内存
int fd = shm_open("/prod_cons_shm", O_CREAT | O_RDWR, 0666);
ftruncate(fd, sizeof(shm_buffer));
shm_buffer *buf = mmap(NULL, sizeof(shm_buffer), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// 初始化信号量
buf->free = sem_open("/free_sem", O_CREAT, 0666, BUFFER_SIZE);
buf->used = sem_open("/used_sem", O_CREAT, 0666, 0);
buf->in = buf->out = 0;
for (int i = 0; i < 100; i++) {
sem_wait(buf->free); // 等待空槽位
// 写入数据
buf->data[buf->in] = i;
buf->in = (buf->in + 1) % BUFFER_SIZE;
sem_post(buf->used); // 增加已用槽位
}
// 清理资源
munmap(buf, sizeof(shm_buffer));
close(fd);
}
// 消费者进程(类似结构,等待used信号量后读取数据)
// ...关键原理说明
- 共享内存:最高效的IPC方式,直接内存共享
- POSIX信号量:
free_sem:初始值为缓冲区大小,表示可用空位used_sem:初始值为0,表示已存数据量sem_wait():原子减少信号量(阻塞当值为0)sem_post():原子增加信号量
- 循环队列:通过取模运算实现缓冲区复用
最佳实践
- 错误处理:检查所有系统调用返回值
- 命名规范:共享内存和信号量使用唯一标识(如
/prod_cons_shm) - 资源释放:
- 使用
shm_unlink()删除共享内存对象 - 使用
sem_close()和sem_unlink()清理信号量
- 使用
- 选择信号量而非互斥锁:信号量支持跨进程同步
常见错误
- 竞争条件:未使用同步机制直接操作共享数据
- 死锁:错误顺序获取信号量(如先wait used再wait free)
- 资源泄漏:忘记关闭/删除共享内存和信号量
- 缓冲区溢出:未正确处理循环队列边界
扩展知识
- 替代方案对比:
机制 适用场景 性能 管道 父子进程简单通信 中 消息队列 结构化数据传递 中低 Socket 跨主机通信 低 - 多生产者/消费者:增加互斥信号量保护in/out指针
- 条件变量:结合互斥锁可实现更复杂的同步逻辑