📌 核心结论(TL;DR):
System V 缺陷:接口(
semget/msgget)极其反人类,且生命周期随内核,不手动删就会导致资源泄漏。信号量(Semaphore):本质是一个计数器。P 操作(申请资源,减1),V 操作(释放资源,加1)。使用建造者模式(Builder)可以优雅地初始化信号量集合。
消息队列(Message Queue):支持带“类型”的数据块(实现优先级)。使用责任链模式(Chain of Responsibility)可以流水线式处理消息(格式化 -> 落盘 -> 备份)。
清理命令:
ipcs -s/ipcs -q查看,ipcrm -s <id>/ipcrm -q <id>手动删除。
一、 为什么需要设计模式?
原生的 System V IPC 接口(如semctl中的union semun构造)非常繁琐。如果在业务代码里直接调用,会导致代码极其臃肿。我们利用经典的设计模式对这些系统调用进行二次封装。
二、 信号量:基于建造者模式封装
信号量(Semaphore)用于多进程/多线程间的同步与互斥。因为它涉及的参数较多(Key生成、创建标志、初始值设定),这里引入建造者模式。
// 核心思想:链式调用,最后 Build 出对象 class SemaphoreBuilder { public: SemaphoreBuilder& SetVal(int val) { _val = val; return *this; } sem_sptr Build(int flag) { // 内部完成 ftok, semget, semctl 的初始化 // ... return std::make_shared<Semaphore>(semid, flag); } }; // 使用方式极其优雅: SemaphoreBuilder sb; auto fsem = sb.SetVal(1).Build(BUILD_SEM); fsem->P(); // 加锁 fsem->V(); // 解锁三、 消息队列:基于责任链模式封装
消息队列允许进程发送带有mtype(类型)的数据块。假设我们有这样一个需求:客户端发来消息,我们需要依次经过“拼接时间格式化” -> “写入文件持久化” -> “判断文件大小,过大则打包备份”。
用if-else写会很乱,引入责任链模式(Chain of Responsibility)。
// 1. 抽象处理者 class HandlerText { public: void SetNextHandler(std::shared_ptr<HandlerText> handler) { _next_handler = handler; } virtual void Execute(std::string &info) = 0; protected: std::shared_ptr<HandlerText> _next_handler; }; // 2. 具体处理者:格式化 class HandlerTextFormat : public HandlerText { void Execute(std::string &info) override { // 处理逻辑:拼接时间 info = time_str + info; if (_next_handler) _next_handler->Execute(info); // 交给下一个节点 } }; // 3. 具体处理者:写文件 class HandlerTextSaveFile : public HandlerText { void Execute(std::string &info) override { // 处理逻辑:写文件 if (_next_handler) _next_handler->Execute(info); } }; // 4. 组装责任链 _format->SetNextHandler(_save); _save->SetNextHandler(_backup); _format->Execute(msg); // 只要触发头部,整个流水线就会自动跑起来四、 避坑指南:资源清理
System V IPC 对象(共享内存、消息队列、信号量)的生命周期随内核。即使创建它们的进程崩溃了,资源也会一直留在系统里。
ipcs -q # 查看消息队列 ipcs -s # 查看信号量 ipcrm -q <msqid> # 删除指定的消息队列