进程间通信(IPC)总览
进程间通信(IPC,Inter-Process Communication)是操作系统为相互隔离的进程提供的数据交换与事件通知机制族,按"经过内核拷贝几次"与"是否需要同步"分为不同代价档位。
一句话定义
进程间通信(IPC,Inter-Process Communication)是操作系统为相互隔离的进程提供的数据交换与事件通知机制族,按"经过内核拷贝几次"与"是否需要同步"分为不同代价档位。
为什么重要
隔离(独立地址空间)与协作(交换数据)天然矛盾,IPC 就是这对矛盾的官方解法。微服务间的 RPC 底层是套接字,Shell 的管道是 Unix 哲学的化身,数据库的 WAL 与共享内存方案直接决定吞吐。选错 IPC 档位是很多"莫名慢"的根因。
前置知识
kp-005(进程隔离与文件描述符);kp-003(系统调用)。
核心概念
- 匿名管道(pipe):单向字节流,仅限父子进程,Shell 的
|即它。 - 命名管道(FIFO):有名字的管道,无亲缘关系的进程可用。
- 消息队列:内核维护的离散消息链表,有边界、可选优先级。
- 共享内存(shm/memfd):同一块物理内存映射进多个进程,速度最快但需自行同步(配合 kp-013 信号量)。
- 信号(signal):异步事件通知,带编号与默认动作,不是可靠的数据通道。
- 套接字(socket):跨主机与本地(Unix domain socket)通吃的通用接口。
原理与机制
按代价与语义对比选型:
| 机制 | 数据边界 | 拷贝次数 | 同步 | 典型场景 |
|---|---|---|---|---|
| 管道 | 无(字节流) | 2 次(用户到内核到用户) | 阻塞读写自带同步 | 命令行组合 |
| 消息队列 | 有(消息) | 2 次 | 可选 | 任务分发 |
| 共享内存 | 无 | 0 次(映射后直接访问) | 需信号量/锁自建 | 高频大数据量 |
| 信号 | 无 | 0 | 异步打断 | 事件通知 |
| Unix 套接字 | 流或报文 | 2 | 有连接语义 | 本机服务间 RPC |
管道的实现要点:内核用环形缓冲页承载字节流,读端写端各持一个文件描述符;写端全部关闭后读端读到 EOF,这是 cat file | head 能优雅终止(SIGPIPE)的机制基础。
图示
共享内存路径: 进程A --映射--> [同一物理页] <--映射-- 进程B (0 次拷贝, 需自配锁)
管道路径: 进程A --write--> [内核缓冲] --read--> 进程B (2 次拷贝, 内核管同步)
直观类比
管道像食堂传菜口:单向、排队、口子关了就知道没菜了。共享内存像两家人共用同一个冰箱:放取最快,但必须自定规矩(锁)防止同时伸手。信号像火警铃:只能告诉你"有事",传递不了详细信息。
实例或案例
who | wc -l # 匿名管道:ps 的输出成为 wc 的输入
mkfifo /tmp/myfifo # 命名管道实验:
( cat /tmp/myfifo & ) ; echo hello > /tmp/myfifo
strace -e trace=pipe,pipe2,read,write sh -c 'echo hi | cat' # 观察 pipe2 与读写序列
ipcs -m # 查看系统共享内存段(System V 接口)
常见误区
- 认为共享内存自动同步:它只提供内存,读写并发保护必须自己加锁或信号量。
- 把信号当可靠队列:同类未决信号会合并,处理函数里还只能用异步信号安全函数。
- 忽略 SIGPIPE:向已关闭的管道写会触发默认终止进程,网络服务必须显式忽略并处理 EPIPE。
与其他知识点的关系
管道与套接字的阻塞/非阻塞语义延伸到 kp-028 的 I/O 多路复用;共享内存的同步问题正是 kp-012、kp-013 的应用场景。
延伸阅读
《UNIX 环境高级编程》(APUE)第 15、16 章;man 7 pipe、man 7 sigevent。
自测题
- 为什么共享内存最快却不能到处用?
答:它省掉了内核拷贝,但同步责任完全交给应用,稍有不慎就引入竞态;低频小数据用管道或套接字更简单可靠。
- Shell 管道
a | b中,a 退出后 b 如何知道数据结束?
答:a 的写端描述符随进程退出被关闭,b 读到 EOF(read 返回 0),这就是字节流的结束信号。
- 信号与消息队列最本质的差别?
答:信号是异步通知(无数据边界、会合并、打断执行流),消息队列是同步读取的数据通道(消息有边界、按序排队)。