Linux 多进程生产者消费者模型实现
- 一、模型核心组件
- 二、关键代码解析
- 1. 信号量封装类(csemp)
- 2. 共享内存初始化
- 3. 生产者核心逻辑
- 4. 消费者核心逻辑
- 三、关键同步机制
- 信号量使用策略
- 操作时序图
- 四、扩展知识
- 1. System V与POSIX信号量对比
- 2. 共享内存最佳实践
- 3. 死锁预防策略
- 五、性能优化建议
- 六、错误处理建议
- 七、完整代码示例
- 八、运行与测试
生产者消费者模型是并发编程中的经典案例,本文通过一个具体的C++示例,演示如何在Linux环境下使用System V IPC机制实现跨进程的生产者消费者模型。
一、模型核心组件
本实现采用三种关键IPC机制:
- 共享内存:存储循环队列数据
- 信号量:实现进程间同步
- 循环队列:数据缓冲区
二、关键代码解析
1. 信号量封装类(csemp)
class csemp {
public:
bool init(key_t key, unsigned short value=1, short sem_flg=SEM_UNDO);
bool wait(short sem_op=-1); // P操作
bool post(short sem_op=1); // V操作
// ...其他成员函数...
};
SEM_UNDO
标志保证进程异常终止时自动释放信号量- 采用RAII模式管理信号量生命周期
2. 共享内存初始化
// 获取/创建共享内存
int shmid = shmget(0x5005, sizeof(squeue<ElemType, 5>), 0640|IPC_CREAT);
// 附加到进程地址空间
squeue<ElemType, 5>* QQ = (squeue<ElemType, 5>*)shmat(shmid, 0, 0);
- 使用固定key值0x5005标识共享内存
- 权限设置为0640(用户读写,组读)
3. 生产者核心逻辑
mutex.wait(); // 获取互斥锁
// 数据入队操作
ee.no=3; strcpy(ee.name, "西施"); QQ->push(ee);
// ...其他数据...
mutex.post(); // 释放互斥锁
cond.post(3); // 通知消费者有3个新数据
4. 消费者核心逻辑
while(true) {
mutex.wait();
while (QQ->empty()) {
mutex.post();
cond.wait(); // 等待数据通知
mutex.wait();
}
// 数据出队处理...
mutex.post();
}
三、关键同步机制
信号量使用策略
信号量 | 初始值 | 作用 | 标志位 |
---|---|---|---|
mutex | 1 | 共享内存访问控制 | SEM_UNDO |
cond | 0 | 可用数据数量通知 | 0 |
操作时序图
四、扩展知识
1. System V与POSIX信号量对比
特性 | System V信号量 | POSIX信号量 |
---|---|---|
进程间共享 | 原生支持 | 需要命名信号量 |
原子操作 | 支持批量操作 | 单信号量操作 |
持久性 | 内核持久 | 需显式删除 |
初始化灵活性 | 需要额外控制 | 直接初始化 |
2. 共享内存最佳实践
- 始终使用同步机制保护共享内存访问
- 初始化时使用双重检查锁模式
- 为共享内存设置合理的过期时间
- 使用
shmdt()
及时断开连接 - 在程序退出时使用
shmctl(IPC_RMID)
清理资源
3. 死锁预防策略
- 按照固定顺序获取锁
- 设置超时机制
- 使用
SEM_UNDO
标志 - 避免嵌套锁
- 定期检查系统信号量状态(
ipcs
命令)
五、性能优化建议
- 批量操作优化:适当增大每次生产/消费的数据量
- 双缓冲区技术:使用交替缓冲区减少锁竞争
- 无锁队列实现:CAS原子操作替代互斥锁
- 内存对齐:优化共享内存访问效率
- 信号量分组:区分读写信号量提升并发性
六、错误处理建议
// 示例:改进的信号量初始化
bool csemp::init(key_t key, unsigned short value, short sem_flg) {
if((m_semid = semget(key, 1, 0666)) == -1) {
if(errno != ENOENT) {
// 记录详细错误日志
log_error("Semget failed: %s", strerror(errno));
return false;
}
// 创建新信号量...
}
// ...其他初始化逻辑...
}
建议添加:
- 详细的错误日志记录
- 信号量存在性检测
- 自动清理僵尸信号量
- 重试机制(ETIMEDOUT处理)
七、完整代码示例
(此处插入用户提供的完整生产者/消费者代码)
八、运行与测试
编译执行命令:
g++ -o producer producer.cpp -lrt -pthread
g++ -o consumer consumer.cpp -lrt -pthread
./producer & ./consumer &
监控系统资源:
watch -n 1 'ipcs -s && ipcs -m'