第 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 模型

      名字以 / 开头,后跟非 / 字符(如 /myobject);mq_open/sem_open/shm_open 三个 open 调用;close + unlink 对应文件语义

      §51.1;Linux 限制 NAME_MAX(shm/mq 255 字符;sem 少 4 字符——内核前缀 sem.

      句柄类型对照

      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):

      操作 消息队列 信号量 共享内存

      头文件

      <mqueue.h>

      <semaphore.h>

      <sys/mman.h>

      句柄

      mqd_t

      sem_t *

      int(fd)

      创建/打开

      mq_open()

      sem_open()

      shm_open() + mmap()

      关闭

      mq_close()

      sem_close()

      munmap()

      删除

      mq_unlink()

      sem_unlink()

      shm_unlink()

      IPC 操作

      mq_send/mq_receive

      sem_post/sem_wait/sem_getvalue

      操作共享区地址

      杂项

      mq_setattr/mq_getattr/mq_notify

      sem_init/sem_destroy(无名)

      (无)

      名字规范: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:生命周期模型:

      操作 文件名 对象

      open 返回引用

      存在

      refcount = N+1

      close

      仍存在

      refcount = N-1

      unlink

      立即删除

      对象保留到 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

      mq_open + O_CREAT

      同 shm_open;execute 位被忽略

      ACL(Linux 2.6.19+)

      shm + 命名 sem 支持;mq 不支持

      Sticky 位

      When:(1) 共享内存要给其他用户读 → 设为 06640660;(2) 生产者-消费者模式 → 同组读写;(3) ACL 控制更细粒度——用 setfacl 设置。

      Exampleshm_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 虚拟文件系统——可用 lsrm 操作。

      Why:编译选项与文件路径是日常使用的高频点。

      How

      操作 命令 / 选项

      编译

      cc -o prog prog.c -lrt

      查看 POSIX shm

      ls /dev/shm/

      查看 POSIX mq

      mount -t mqueue mqueue /dev/mqueue; ls /dev/mqueue/

      删除

      rm /dev/shm/myobject(unprivileged 只能删自己的)

      查看 POSIX sem

      ls /dev/shm/sem.*(semaphore 在 shmfs 下)

      When:(1) gcc -lrt 链接 librt;(2) 调试时 ls /dev/shm/ 查共享内存残留;(3) mount -t mqueue 挂载 mqueuefs 才能 ls;(4) ipcs 查 SysV;ls /dev/shm/ 查 POSIX shm。

      Examplels /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 / 命令)
      类别 内容

      三件套

      消息队列 mq_* / 信号量 sem_* / 共享内存 shm_*

      头文件

      <mqueue.h> / <semaphore.h> / <sys/mman.h>

      句柄类型

      mqd_t / sem_t * / int fd

      创建/打开

      mq_open / sem_open / shm_open + mmap

      关闭

      mq_close / sem_close / munmap

      删除

      mq_unlink / sem_unlink / shm_unlink

      名字规范

      / 开头,后跟非 / 字符;NAME_MAX 限制

      引用计数

      close 减引用;unlink 删名字;引用归零销毁

      权限位

      同文件(rwx);execute 无意义;mode & ~umask

      ACL(Linux 2.6.19+)

      shm + 命名 sem 支持;mq 不支持

      Linux 支持内核版本

      shm 2.4+、sem 2.6+、mq 2.6.6+

      编译选项

      -lrt(librt)

      SysV vs POSIX

      POSIX 更简洁;SysV 更可移植

      命令行查看

      ls /dev/shm/(shm);mount -t mqueue + ls /dev/mqueue/(mq)

      四、思维导图

      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 操作

      五、重点与易错点

      1. POSIX IPC 三件套都是 SUSv3 可选组件——可移植代码必须 #ifdef 检查 MQ_OPEN_MAX_POSIX_SEMAPHORES_POSIX_SHARED_MEMORY_OBJECTS

      2. Linux 完整支持需 2.6+——shm 2.4+、sem 2.6+、mq 2.6.6+;旧内核可能部分缺失。

      3. 编译必须 -lrt——POSIX mq 与 shm 在 librt;不链接会 undefined reference。

      4. 对象名字必须以 / 开头——SUSv3 要求;可移植代码统一前缀;Tru64 等系统把名字当作文件路径。

      5. Linux 名字长度限制——shm/mq NAME_MAX = 255 字符;sem 少 4 字符(内核前缀 sem.)。

      6. shm_open 返回 fd 但不是普通文件——必须 ftruncate 设大小后才能 mmap;不能用普通文件操作(read/write/lseek)直接访问。

      7. 引用计数删除 vs SysV IPC_RMID——POSIX unlink 后引用归零才销毁;SysV 必须显式 IPC_RMID,最后一个用户负责。

      8. POSIX IPC 内核持久——unlink 前一直存在;进程退出后对象仍可用——这是 IPC 而非「父子共享」。

      9. ACL 仅 shm 与 sem——mq 不支持 ACL;权限只能用 mode 位。

      10. 执行权限无意义——mode 中的 x 位被忽略;不要试图用 x 位控制。

      11. sticky bit 在 IPC 父目录——Linux 把 POSIX IPC 对象放在 sticky 目录——非特权只能 unlink 自己的对象。

      12. 消息队列支持优先级——SysV 用 type 字段;POSIX mq 用 mq_send 的 priority 参数。

      13. POSIX 信号量有两种——命名(sem_open)与匿名(sem_init);第 53 章详解。

      14. POSIX 共享内存必须 mmap——shm_open 返回 fd;要 mmap MAP_SHARED 才能用;不像 SysV shmat。

      15. 跨章衔接:第 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 实现。