选用文件而非系统消息队列是为了实现简单和跨平台可移植,因SysV IPC或POSIX MQ在Windows不可用且需权限配置;文件方案只需读写目录权限,但牺牲原子性、内核调度,适合低频非关键通信。

为什么不用系统提供的消息队列而选文件?
因为目标是“简单”和“跨平台可移植”,sysv ipc 或 posix mq 在 Windows 上不可用,且需要权限配置;而基于文件的方案只要能读写目录就能跑。但得清楚代价:没有原子性保障、无内核调度、高并发下需自行处理竞争——它适合低频、非关键路径的进程通信,比如日志聚合、配置热更新通知。
如何用普通文件模拟消息入队和出队?
核心思路是把消息写进一个追加模式打开的文件,并用固定长度头部标记每条消息大小。避免直接覆写或截断,否则多进程同时操作会丢数据。
-
fopen("mq.dat", "ab")写入时必须用追加模式("ab"),确保fwrite不受其他进程写入影响 - 每条消息前写 4 字节
uint32_t表示 payload 长度(小端序更稳妥),接收方先读头再读正文 - 读取端不能用
fseek随意跳转,要维护一个本地偏移量(如存在mq.offset文件里),每次读完更新它 - 若读到不完整的头部(文件末尾被截断),说明最后一条消息写了一半,直接丢弃该条(不崩溃)
示例片段(简化版入队):
uint32_t len = htonl(static_cast<uint32_t>(msg.size())); fwrite(&len, sizeof(len), 1, fp); fwrite(msg.data(), 1, msg.size(), fp); fflush(fp); // 关键:强制落盘,否则可能卡在 stdio 缓冲区
如何避免多个进程读/写冲突?
文件本身不提供互斥,必须靠外部同步机制。推荐用 flock()(Linux/macOS)或 LockFileEx()(Windows),而不是 mkdir 或临时文件——后者有竞态窗口。
立即学习“C++免费学习笔记(深入)”;
- 写入前对文件加
LOCK_EX,写完立即解锁;读取端也需加LOCK_SH,哪怕只读也要防写入中读到半条消息 - 不要依赖
fstat()获取文件大小后去读——两次系统调用之间文件可能被扩展,导致读越界或阻塞 - 如果某个进程崩溃未释放锁,
flock是建议性锁,不影响其他进程继续工作,但可能漏消息;更健壮的做法是搭配超时重试 + 偏移校验
消息怎么算“被消费”?要不要删文件?
不删。删除文件会导致所有正在读的进程失效(open fd 仍有效,但新 open 会失败),而且删除+重建有竞态。正确做法是定期归档或截断旧内容。
- 维护一个“已读最大偏移”,当它超过某阈值(如文件大小的 80%),用
truncate()把前面部分清空,并把剩余消息拷贝到开头(类似 log compaction) - 截断操作必须加
LOCK_EX,且拷贝过程中暂停所有读写——这是唯一需要全局停服的点 - 如果业务允许丢失,也可以不做归档,只让文件不断增长(监控磁盘空间即可)
真正难的不是读写逻辑,而是偏移管理与锁粒度的平衡:锁太粗,吞吐低;锁太细,校验逻辑爆炸。实际用时先按“单写多读 + 定期归档”跑起来,再根据压测数据调。


















