第 51 章 POSIX IPC 概述 (Introduction to POSIX IPC)
核心结论
-
POSIX IPC 三件套:消息队列(mq_*)、信号量(sem_*)、共享内存(shm_*)——POSIX.1b 实时扩展为替代 SysV IPC 设计。
-
POSIX vs SysV 哲学:POSIX 用「名字 + open/close/unlink」模型(与 UNIX 文件一致);SysV 用「key + shmget/msgget/semget + ipcctl」模型。
-
对象句柄类型:mqd_t(消息队列描述符)、sem_t*(信号量指针)、int(共享内存文件描述符)。
-
API 对照:mq_open/mq_close/mq_unlink 对应 sem_open/sem_close/sem_unlink 对应 shm_open + mmap + munmap + shm_unlink。
-
引用计数删除:POSIX IPC 对象引用计数——unlink 立即删名字,引用计数归零才真销毁;这比 SysV IPC 的「显式 IPC_RMID」更易用。
-
内核持久性:POSIX IPC 对象一旦创建就持续存在直到 unlink 或系统重启;与 SysV IPC 类似。
-
命令行列出:Linux 把 POSIX IPC 对象放在虚拟文件系统(shmfs/mqueuefs)——可
ls /dev/shm/、rm删除;SysV 用ipcs/ipcrm。 -
链接选项:Linux 用 POSIX IPC 必须
-lrt(librt 实时库)。
|
本章主旨
本章是第 52-54 章的前置——介绍 POSIX IPC(消息队列、信号量、共享内存)的共同特征:API 模型、对象名字、引用计数、权限、删除/持久性、与 SysV 的对比。读者需要建立「POSIX IPC = 命名 + 文件模型」的心智模型——之后三章具体 API 都基于这个模型。POSIX IPC 设计目标之一是解决 SysV IPC 的「key 难用」「不能引用计数」等缺陷;但代价是历史更短(Linux 2.4+ 才完整支持)、可移植性略差(SUSv3 中是可选组件)。 |
一、核心概念
本章围绕 6 个核心概念展开:POSIX IPC 三件套、API 模型、对象名字、引用计数删除、与 SysV 对比、命令行列出。
| 概念 | 定义 + 重要性 | 实现提示 |
|---|---|---|
POSIX IPC 三件套 |
消息队列(mq_*)、命名信号量(sem_*)、共享内存(shm_*)——POSIX.1b 设计为 SysV IPC 的替代 |
表 51-1;共享内存需 mmap;信号量另有名 vs 匿名(第 53 章);消息队列支持优先级 |
对象名字 + open/close/unlink 模型 |
名字以 |
§51.1;Linux 限制 NAME_MAX(shm/mq 255 字符;sem 少 4 字符——内核前缀 |
句柄类型对照 |
mqd_t(消息队列描述符)、sem_t*(信号量指针)、int fd(共享内存文件描述符);关闭方式不同——mq_close/sem_close/munmap |
§51.1;shm_open 返回 fd 但语义是 IPC 对象;之后用 mmap MAP_SHARED 把它映射到地址空间 |
引用计数与删除 |
POSIX IPC 对象有引用计数——close 减引用;unlink 立即删名字;引用归零才真销毁;与 SysV 的显式 IPC_RMID 模式不同 |
§51.1;unlink 后同名 open 创建新对象;unlink 不阻塞其他进程 |
权限与 umask |
权限位同文件(9 位);execute 无意义;mode 与 umask AND;2.6.19+ Linux 支持 shm 与 sem 的 ACL(mq 不支持) |
§51.1;SUSv3 允许 setgid 到父目录;Linux 用 fsuid/fsgid 而非 euid/egid |
SysV vs POSIX 对比 |
POSIX 优点:API 更简洁、文件名模型一致、引用计数删除;POSIX 缺点:可移植性差(每个机制 SUSv3 中可选)、名字约定各实现不同 |
§51.2;Linux 完整支持:shm 2.4、sem 2.6、mq 2.6.6;编译需 -lrt |
二、详细笔记
51.1 API 总览与对象名字
What:POSIX IPC 三件套共用一个 API 模型——open/close/unlink + 名字 + 引用计数。
Why:与 UNIX 文件模型一致——降低学习曲线;引用计数避免「何时销毁」的歧义。
How:
API 对照(表 51-1):
| 操作 | 消息队列 | 信号量 | 共享内存 |
|---|---|---|---|
头文件 |
|
|
|
句柄 |
|
|
|
创建/打开 |
|
|
|
关闭 |
|
|
|
删除 |
|
|
|
IPC 操作 |
|
|
操作共享区地址 |
杂项 |
|
|
(无) |
名字规范:SUSv3 要求「以 / 开头,后跟一个或多个非 / 字符」,如 /myobject。Linux 限制 NAME_MAX(255 字符);semaphore 少 4 字符因为内核前缀 sem.。
When:写可移植 POSIX IPC 代码——把名字生成隔离到函数(不同系统规则不同,如 Tru64 用绝对/相对路径);始终包含 / 前缀。
Example:
// 摘自《The Linux Programming Interface》 第 51 章
#include <sys/mman.h>
#include <fcntl.h>
/* POSIX 共享内存:shm_open 返回 fd,再用 mmap 映射 */
int fd = shm_open("/mymem", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
if (fd == -1) errExit("shm_open");
ftruncate(fd, 4096); /* 必须 ftruncate 设置大小 */
char *addr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
51.2 引用计数与删除语义
What:POSIX IPC 对象引用计数——close 减引用,unlink 立即删名字;引用归零才真销毁。
Why:避免 SysV IPC 的「显式 IPC_RMID」「最后一个用户必须记得删除」的问题。
How:生命周期模型:
| 操作 | 文件名 | 对象 |
|---|---|---|
|
存在 |
refcount = N+1 |
|
仍存在 |
refcount = N-1 |
|
立即删除 |
对象保留到 refcount = 0 |
refcount = 0 |
已删除 |
对象销毁 |
When:(1) 进程启动时 open;用完 close;最终 unlink;(2) 守护进程创建 IPC 对象后立即 unlink(避免名字残留,但对象仍可用);(3) shm_unlink 引用归零才真释放——所有进程 munmap 后对象消失。
Example:服务器创建 /myqueue 后立即 mq_unlink("/myqueue")——客户端按名字 mq_open 仍能连上;服务器退出后对象消失。
51.3 权限、umask 与 ACL
What:POSIX IPC 对象权限位同文件(9 位 rwx);execute 无意义;mode 与进程 umask AND。
Why:与文件权限模型一致——可继承现有 UNIX 安全机制。
How:
| 操作 | 权限规则 |
|---|---|
`shm_open("/x", O_CREAT |
O_RDWR, 0660)` |
创建权限 = 0660 & ~umask;所有者 = fsuid;组 = fsgid |
|
同 shm_open;execute 位被忽略 |
ACL(Linux 2.6.19+) |
shm + 命名 sem 支持;mq 不支持 |
Sticky 位 |
When:(1) 共享内存要给其他用户读 → 设为 0664 或 0660;(2) 生产者-消费者模式 → 同组读写;(3) ACL 控制更细粒度——用 setfacl 设置。
Example:shm_open("/data", O_CREAT|O_RDWR, 0660) 创建组内可读写;其他用户拒绝。
51.4 与 SysV IPC 的对比
What:POSIX IPC 设计目标——简化 API、统一文件模型、引用计数删除;代价——可移植性差、SUSv3 中可选。
Why:理解 SysV vs POSIX 的取舍——决定项目用哪一套。
How:
| 维度 | SysV IPC | POSIX IPC |
|---|---|---|
API 风格 |
key + id;shmget/msgget/semget |
名字 + open/close/unlink |
句柄 |
int(id) |
mqd_t / sem_t* / int fd |
删除 |
显式 IPC_RMID;最后一个用户负责 |
引用计数;unlink 后引用归零销毁 |
对象可见性 |
ipcs/ipcrm |
ls/rm(在 Linux shmfs/mqueuefs) |
持久性 |
内核持久(显式删或重启) |
内核持久(unlink 或重启) |
可移植性 |
SUSv3 强制;几乎所有 UNIX |
SUSv3 可选;Linux 2.4+ 完整 |
编译 |
无需特殊库 |
需 -lrt |
When:(1) 新项目、Linux 为主 → POSIX IPC 更简洁;(2) 多 UNIX 平台、需要历史兼容 → SysV 更安全;(3) 大型遗留代码 → 维持现状;(4) POSIX 信号量与 pthread 配合更好——线程间同步首选 pthread mutex,进程间首选 POSIX sem。
Example:第 45-48 章 SysV IPC(msgget/semget/shmget + ipcctl)vs 第 52-54 章 POSIX IPC(mq_open/sem_open/shm_open + unlink)——同一应用两种实现思路对比。
51.5 编译与命令行列出
What:Linux 用 POSIX IPC 必须 -lrt;POSIX IPC 对象在 shmfs/mqueuefs 虚拟文件系统——可用 ls、rm 操作。
Why:编译选项与文件路径是日常使用的高频点。
How:
| 操作 | 命令 / 选项 |
|---|---|
编译 |
|
查看 POSIX shm |
|
查看 POSIX mq |
|
删除 |
|
查看 POSIX sem |
|
When:(1) gcc -lrt 链接 librt;(2) 调试时 ls /dev/shm/ 查共享内存残留;(3) mount -t mqueue 挂载 mqueuefs 才能 ls;(4) ipcs 查 SysV;ls /dev/shm/ 查 POSIX shm。
Example:ls /dev/shm/ 看到 myqueue——说明有人创建了 shm_open("/myqueue", …);rm /dev/shm/myqueue 等价于 shm_unlink("/myqueue")。
51.6 跨章衔接
What:本章是后续三章的总览——第 52 章消息队列、第 53 章信号量、第 54 章共享内存。
Why:建立统一心智模型后,逐一学习具体 API。
How:
| 章节 | 主题 |
|---|---|
第 52 章 |
POSIX 消息队列(mq_open/mq_send/mq_receive;优先级;通知) |
第 53 章 |
POSIX 信号量(命名 vs 匿名;sem_post/sem_wait;与 pthread 配合) |
第 54 章 |
POSIX 共享内存(shm_open + mmap;与 SysV shm 对比) |
第 45-48 章 |
SysV 消息队列 / 信号量 / 共享内存 |
When:(1) 进程间消息传递——首选 POSIX mq 或 SysV mq;(2) 进程间同步——首选 POSIX sem;(3) 进程间大数据共享——首选 POSIX shm 或 mmap file;(4) 线程间同步——首选 pthread mutex/cond(不需要 IPC)。
Example:现代 Linux 应用推荐组合——shm_open + mmap(共享数据)+ sem_open(同步)+ mq_open(消息通知)。
三、关键图表
|
非可视化条目(API / 命令)
|
四、思维导图
mindmap
root((第 51 章 POSIX IPC 概述))
三件套
消息队列 mq
信号量 sem
共享内存 shm
POSIX.1b 设计
替代 SysV IPC
API 模型
名字 加 斜杠
open close unlink
文件语义
mqd_t sem_t fd
引用计数
close 减引用
unlink 删名字
归零才销毁
避免最后用户负责
权限 ACL
同文件 9 位
umask 屏蔽
2.6.19 ACL
shm sem 支持 mq 不支持
SysV 对比
POSIX API 简洁
SysV 可移植强
POSIX 可选组件
SysV 强制标准
编译 命令行
链接 librt
dev shm 虚拟文件
mqueue 挂载
ls rm 操作
五、重点与易错点
-
POSIX IPC 三件套都是 SUSv3 可选组件——可移植代码必须
#ifdef检查MQ_OPEN_MAX、_POSIX_SEMAPHORES、_POSIX_SHARED_MEMORY_OBJECTS。 -
Linux 完整支持需 2.6+——shm 2.4+、sem 2.6+、mq 2.6.6+;旧内核可能部分缺失。
-
编译必须
-lrt——POSIX mq 与 shm 在 librt;不链接会 undefined reference。 -
对象名字必须以
/开头——SUSv3 要求;可移植代码统一前缀;Tru64 等系统把名字当作文件路径。 -
Linux 名字长度限制——shm/mq NAME_MAX = 255 字符;sem 少 4 字符(内核前缀
sem.)。 -
shm_open 返回 fd 但不是普通文件——必须
ftruncate设大小后才能 mmap;不能用普通文件操作(read/write/lseek)直接访问。 -
引用计数删除 vs SysV IPC_RMID——POSIX unlink 后引用归零才销毁;SysV 必须显式 IPC_RMID,最后一个用户负责。
-
POSIX IPC 内核持久——unlink 前一直存在;进程退出后对象仍可用——这是 IPC 而非「父子共享」。
-
ACL 仅 shm 与 sem——mq 不支持 ACL;权限只能用 mode 位。
-
执行权限无意义——mode 中的 x 位被忽略;不要试图用 x 位控制。
-
sticky bit 在 IPC 父目录——Linux 把 POSIX IPC 对象放在 sticky 目录——非特权只能 unlink 自己的对象。
-
消息队列支持优先级——SysV 用 type 字段;POSIX mq 用 mq_send 的 priority 参数。
-
POSIX 信号量有两种——命名(sem_open)与匿名(sem_init);第 53 章详解。
-
POSIX 共享内存必须 mmap——shm_open 返回 fd;要 mmap MAP_SHARED 才能用;不像 SysV shmat。
-
跨章衔接:第 52 章 mq_open + mq_send/mq_receive + mq_notify;第 53 章 sem_open + sem_post/sem_wait + sem_init(匿名);第 54 章 shm_open + mmap + shm_unlink;第 45-48 章对照 SysV 实现。