共享内存需用shm_open+mmap,不可直接malloc;消息队列优先选mq_open;跨进程管道须用mkfifo创建FIFO。三者均需手动清理资源,否则残留导致ENOSPC或EEXIST。

共享内存用 shm_open + mmap,别直接 malloc
共享内存不是“开块内存大家读写”那么简单。Linux 下得走 POSIX 共享内存路径:先用 shm_open 创建或打开一个命名内存对象,再用 mmap 映射到进程地址空间。跳过 shm_open 直接 mmap 匿名区域(比如 mmap(nullptr, size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0))只能用于父子进程,不能跨无关进程通信。
常见错误现象:shm_open 返回 -1,errno = ENOENT —— 忘了加 O_CREAT 标志;或者 mmap 失败返回 MAP_FAILED —— 忘了检查 shm_open 返回的 fd 是否有效,或没调 ftruncate 设置大小。
-
shm_open的 name 参数必须以/开头(如"/my_shm"),否则会失败 - 映射后记得用
munmap,程序退出前用shm_unlink清理,否则残留的/dev/shm/下文件会一直占空间 - 多个进程同时读写同一块内存,必须自己加同步机制(
pthread_mutex_t放在共享内存里?可以,但得用PTHREAD_PROCESS_SHARED初始化)
消息队列优先选 mq_open,System V 的 msgget 已过时
POSIX 消息队列(mq_open)接口更现代、权限控制清晰、支持阻塞/非阻塞和超时,而 System V 的 msgget/msgsnd 依赖 key_t 和全局 ID,容易冲突,且没有标准超时机制。
使用场景:需要可靠传递、带优先级、不希望数据丢在内存里(对比管道易断连)——比如主控进程下发配置变更给多个 worker 进程。
立即学习“C++免费学习笔记(深入)”;
-
mq_open的attr参数若为nullptr,队列大小用系统默认值(通常是 10),太小会导致mq_send阻塞或失败 - 发送端用
mq_send,接收端用mq_receive,注意两个函数都要求传入缓冲区大小,且mq_receive会修改实际接收长度指针,别传错地址 - 队列名同样要以
/开头(如"/cfg_queue"),且不能包含额外斜杠 - 关闭后记得
mq_unlink,否则下次mq_open会失败(errno = EBUSY)
管道分两种:匿名管道只适用于父子进程,命名管道(FIFO)才跨进程
很多人以为 pipe() 能让任意两个进程通信,其实它只返回一对 fd,仅对当前进程及其 fork 出的子进程可见。真要跨无关进程,必须用命名管道(FIFO):先用 mkfifo 创建文件,再用 open 以 O_RDONLY 或 O_WRONLY 打开 —— 注意:至少一个端以阻塞方式打开,否则 open 会卡住。
性能影响:FIFO 是字节流,无消息边界,读写双方需自行约定协议(比如每条消息前加 4 字节长度);相比共享内存和消息队列,它不支持随机访问、不支持广播、且一端崩溃容易导致另一端 hang 住。
-
mkfifo创建的文件权限默认是 0666,建议显式传0600控制访问范围 - 用
open打开 FIFO 时,如果只开读端而没有写端,或只开写端而没有读端,open会阻塞(除非加O_NONBLOCK) -
read返回 0 表示对方已关闭写端,这是正常 EOF,不是错误
选型关键不在“功能有无”,而在“谁负责清理、谁承担同步成本”
共享内存最快,但同步逻辑全由你写,pthread_mutex_t 放共享内存里必须用 PTHREAD_PROCESS_SHARED,忘了这句就会静默失败;消息队列自带原子收发和排队,但每次调用都有内核态切换开销;FIFO 最轻量,但容错差——写端 crash 后读端可能永远等不到 EOF。
最容易被忽略的是资源生命周期管理:共享内存对象、消息队列、FIFO 文件,都不随进程退出自动销毁。测试时反复跑程序却没清理,很快会遇到 ENOSPC 或 EEXIST 错误。写完 main 别忘了注册 atexit 或 RAII 析构函数去 shm_unlink/mq_unlink/unlink。










