Appearance
进程间通信
2026 大纲 二(一)5 进程间通信,补充说明点名共享内存、消息传递、管道、信号四种方式。
两个进程被隔开了,那它们怎么说话
前面几节一直在强调进程之间是隔开的:各有各的地址空间、 各有各的 PCB、一个进程改不了另一个进程的内存。这是保护的基础。
可实际系统里进程又必须协作——浏览器的渲染进程要把画面交给合成进程, ls 的输出要交给 grep。隔离是前提,通信是需求,两者天生冲突。
解决办法只有一条路:由内核在中间开一个受控的口子。 而口子怎么开,决定了四种方式的全部性质:
共享存储——内核把同一批物理页框映射进两个进程的页表, 之后内核就退出数据通路了,两边直接读写内存。 这一条同时解释了它最快(没有任何拷贝、没有系统调用) 和它必须自己外挂信号量做互斥(内核根本不知道谁在写)。
消息传递——数据打包成消息,send/receive 走内核中转。 内核在通路上,所以同步由它顺带处理了,代价是要拷贝、要陷入内核。
管道——本质是内核里的一个固定大小缓冲区,自带同步(满则写阻塞、 空则读阻塞),但不自带互斥(多个写者仍会乱)。
信号——它压根不传数据,只传"发生了某件事"这个事实。 用位向量记录,因而不计数、不排队、可能丢失。
所以选型判据不是"哪个最快",而是三问:传数据还是传事件? 数据量多大、传多少次?跨不跨机器?
一、共享存储(共享内存)
建立过程:双方通信前先向系统申请共享存储区中的一个分区 → 各自把它附加(映射)到自己的地址空间(页式系统里就是把同一批物理页框填进两个进程的页表,双方用各自不同的逻辑地址经各自页表变换后落到同一批页框上,见 页式管理)→ 此后像读写普通内存一样读写 → 用完把分区归还。
| 形式 | 共享区里放什么 | 谁负责同步 | 层次 |
|---|---|---|---|
| 基于数据结构的共享 | 特定格式的数据结构(如有界缓冲区) | 程序员自己 | 低级通信,只适于传递相对少量的数据,效率低下 |
| 基于存储区的共享 | 任意数据,形式和位置由进程自己决定 | 进程自己 | 高级通信,为传输大量数据而设 |
两者的差别在于是谁规定了共享区里的格式:OS 规定的是前者(所以受限),进程自己说了算的是后者。
二、消息传递
以格式化的消息为单位传递数据。消息由消息头(发送进程标识、接收进程标识、消息类型、消息长度等控制信息,长度固定)与消息体(真正要传的数据)组成。
| 消息格式 | 优点 | 代价 | 适用 |
|---|---|---|---|
| 定长 | 处理和存储开销小 | 长消息要拆成多条 | 单机系统内、发送与接收进程环境相同时;便笺式的短通信 |
| 变长 | 方便用户,长消息不必拆 | 系统在处理和存储两方面都要付出更多开销 | 需要发送较长消息时 |
直接通信在原语里直接指明对方进程。收发双方都指名道姓的叫对称寻址,它的不足是某进程改名后所有引用过这个名字的定义都得跟着改;接收方不指定发送者、只用一个返回参数带回源进程的叫非对称寻址,打印服务这类"要接收任意进程请求"的场合必须用它。
send(B, message) // 对称寻址:进程 A 向进程 B 发送消息
receive(A, message)
send(P, message) // 非对称寻址:接收任意进程的消息
receive(id, message) // id 返回实际发送方间接通信(信箱通信)让消息经信箱中转,双方都不必知道对方是谁。信箱分信箱头(信箱标识符、拥有者、口令、空格数等描述信息)与信箱体(若干信箱格,格数与每格大小在创建信箱时确定)。"信箱头里有空格数"意味着信箱容量有限,满了就发不进去——这正是链路容量的具体形式。
| 链路属性 | 两种取值 | 判据/后果 |
|---|---|---|
| 怎么建链 | 显式建链:发送进程用"建立连接"原语请求系统建链,用完拆除 隐式建链:不必提出请求,系统在它发出 send 时自动建链 | 显式主要用于计算机网络,隐式主要用于单机系统 |
| 方向 | 单向链路 / 双向链路 | 决定要不要建两条链路 |
| 容量 | 无容量 / 有容量(可暂存 | 直接决定 send 会不会阻塞 |
| send / receive 组合 | 行为 | 什么场合用 |
|---|---|---|
| 发送阻塞 + 接收阻塞 | 双方都停下来会合 | 进程之间紧密同步、收发之间无缓冲(无容量链路)时 |
| 发送不阻塞 + 接收阻塞 | 发送方投出去就走;接收方平时阻塞着,来消息才被唤醒 | 应用最广,服务器等待请求就是这个形态 |
| 发送不阻塞 + 接收不阻塞 | 双方各忙各的,仅当某事件让它无法继续时才阻塞自己 | 也较常见 |
第四种组合(发送阻塞 + 接收不阻塞)很少用:接收方不阻塞意味着它得反复轮询,发送方却停下来等,两头的设计意图互相矛盾。
三、管道通信
管道是用于连接一个读进程和一个写进程的一个共享文件(又名 pipe 文件),实体是内存中一个固定大小的环形缓冲区。写进程以字符流形式送入数据,读进程从中读出。
进程A(写入) ───► 【管道缓冲区】───► 进程B(读取)
写端 读端| 必须提供的协调能力 | 具体做什么 | 为什么必须有 |
|---|---|---|
| 互斥 | 一个进程正在对 pipe 执行读/写时,其他进程必须等待 | 环形缓冲区的读写指针是共享变量,并发改动会错乱 |
| 同步 | 写进程写入一定数量数据后便睡眠等待,直到读进程取走才被唤醒;读进程读空 pipe 时也睡眠等待,直到写进程写入才被唤醒 | 缓冲区容量有限,两端速度不匹配时必须有一方停下来 |
| 确定对方是否存在 | 只有确定了对方已存在,才能进行通信 | 对面没人时继续读/写没有意义,必须给出确定的行为 |
| 匿名管道(无名管道) | 命名管道(FIFO) | |
|---|---|---|
| 有没有文件名 | 没有,在文件系统中没有路径 | 有,在文件系统中有一个路径名 |
| 双方怎么找到它 | 只能靠继承:创建管道后 fork,子进程继承父进程打开的文件描述符 | 双方各自按路径名打开即可 |
| 适用范围 | 只能用于有亲缘关系的进程(父子、兄弟) | 任意两个进程,互不相识也可以 |
| 生存期 | 随最后一个持有它的进程结束而消失 | 文件本身持久存在,直到被显式删除 |
四、信号
信号是一种用于通知进程某个事件已发生的异步通信机制。每种系统事件对应不同的信号类型,每类信号有一个序号。进程 PCB 中用一个 n 位向量记录当前待处理的信号(某位为 1 即待处理,处理后清零),另一个 n 位向量记录被阻塞(屏蔽)的信号——被阻塞的信号暂存,直到解除阻塞才处理。
发送有两条来源:内核检测到系统事件时发送(执行非法指令 → SIGILL;向读端已关闭的管道写 → SIGPIPE),或进程调用 kill() 向指定 PID 发送。处理分三种:默认处理(执行 OS 预设操作,如 SIGILL 默认终止进程)、自定义处理(进程为某类信号注册处理函数)、忽略。处理程序执行结束后通常返回原程序被中断的位置继续执行。
信号为什么被称为"软中断":与硬件中断逐项对照(想弄清这个别名的分寸时展开)
| 硬件中断 | 信号 | |
|---|---|---|
| 由谁触发 | 外设或 CPU 内部异常 | 内核检测到系统事件,或另一进程调用 kill() |
| 打断谁 | 打断处理机当前的指令流 | 打断进程的正常执行流 |
| 谁来处理 | 中断处理程序(内核态) | 信号处理程序(多在用户态执行) |
| 能不能屏蔽 | 可用中断屏蔽字屏蔽 | 可用信号屏蔽向量阻塞 |
| 处理完去哪 | 返回被中断的断点 | 通常返回原程序被中断的位置继续执行 |
两者的层次并不相同:硬件中断是处理机级的机制,由硬件递交;信号是进程级的机制,由内核在合适的时机递交给进程。硬件中断的细节见 中断和异常的处理。
五、四种方式的对比与选型
| 方式 | 数据通路 | 速度(及依据) | 内核开销 | 适用数据量 | 同步机制 | 方向 |
|---|---|---|---|---|---|---|
| 共享存储 | 双方页表指向同一批物理页框,数据不经内核 | 最快:零数据拷贝、零系统调用,通信时就是普通访存 | 建立映射时一次性开销,之后为零 | 大,且传输次数越多越划算 | 须自行处理(信号量等) | 双向 |
| 消息传递 | 数据经内核缓冲区中转 | 中等:每条消息 2 次拷贝(发送方→内核,内核→接收方)+ 收发各一次系统调用 | 每条消息都有,与消息条数成正比 | 中小,或跨机器 | 无容量+双阻塞时由 OS 保证会合;其余情形仍需自理 | 双向 |
| 管道 | 数据经内核中的环形缓冲区 | 中等:同样是 2 次拷贝,但接口是字节流,无消息头开销 | 每次读写都有 | 中等,流式数据 | 自动同步(满写阻塞、空读阻塞),但多读/多写仍需互斥 | 半双工 |
| 信号 | 只改 PCB 里的一位 | 快,但不传数据 | 极小 | 不传数据(只有一个信号编号) | 异步通知,不排队、可能丢失 | 单向 |
把"数据量与次数才是判据"算成具体数字:共享存储什么时候反而最慢(想给选型三问里的第②问一个量化标准时展开)
设内存拷贝速率 2 GB/s,一次系统调用 1 µs,一对 P/V 操作 1 µs,建立一次共享内存映射的一次性开销 200 µs。消息传递需 2 次数据拷贝 + 2 次系统调用;共享存储需 1 次数据拷贝(写方写入共享区,读方直接读)+ 一对 P/V。
第一步,把"一次拷贝"折算成时间,这是全篇推算的基本单位:
第二步,按各自真正发生的动作逐笔组装单次开销(不能笼统地说"消息传递慢一倍"):
第三步,传 100 条 1 MB 数据(映射开销只算一次,这正是"一次性"的含义):消息传递
第四步,换成 4 字节的控制信息,拷贝时间降为
只传 1 次:消息传递 2.004 µs,共享存储
共享存储省的是拷贝,付的是建映射。 传一次几个字节的控制信息,用消息传递甚至信号远比开一块共享内存合算。
考点速记
- 共享存储把同一批物理页框映射进两个进程的页表,之后内核不在数据通路上——这一条同时解释了它最快和它必须自己外挂信号量做互斥。
- ⚠️共享内存下"虚拟页号不一定相同,物理页框号一定相同":两个进程各自独立分配虚拟地址空间,同一块共享内存在它们的地址空间里可以落在不同位置;但共享的本质就是指向同一批物理页框,所以页框号必然相同。(与内存映射文件是同一条结论。)
- 消息传递的"同步由 OS 顺带处理"只在无容量 + 双阻塞时完整成立。
- 管道自带同步、不自带互斥:满则写阻塞、空则读阻塞(所以读和写都可能被阻塞),但多个写者仍会互相干扰。
- 管道是半双工的——一个管道只能单向传输,要双向就得开两个。
- 管道的容量是内核里一块固定大小的缓冲区(通常几十 KB),⚠️不是"受磁盘容量限制"——它根本不在磁盘上。
- ⚠️一个管道可以有多个读进程或多个写进程(只是需要额外互斥),不是"只能一个"。
- "管道只能用于父子进程"仅对匿名管道成立,根因是它没有名字——只能靠
fork时继承 fd 来传递;命名管道(FIFO)有路径名,任意进程都能打开。 - 信号用位向量记录,因而不计数、不排队、可能丢失,只传事件不传数据。
- 选型判据是三问,不是"哪个最快":传数据还是传事件?数据量多大、传多少次?跨不跨机器?
这一节在真题里被考过的形式:
ipc 这个标签下挂了 9 道题,但其中 6 道实际考的是信号量 (2010-25、2013-45、2015-45、2020-45、2021-45、2026-26), 在信号量与各经典同步问题里讲—— 本页下方练习区渲染的题比本节内容宽,属正常。真正落在本节的有两道:
- 判断管道通信的四条叙述,选正确的(2014-31)。答进程对管道进行读操作和写操作都可能被阻塞(速记第四条)。⚠️ 另三项各错一处:"一个管道可实现双向数据传输"错——管道半双工,双向要开两个(第五条);"管道的容量仅受磁盘容量限制"错——它是内核里的一块固定大小缓冲区,跟磁盘无关(第六条);"一个管道只能有一个读进程或一个写进程"错——可以有多个,只是需要额外互斥(第七条)。这道题一题包了四个考点。
- 两进程共享 data,问虚拟页号与物理页框号的关系(2023-30)。答页号不一定相等、页框号一定相等。⚠️ 判据就是速记第二条:虚拟地址由各进程独立分配、可以不同;共享的本质是同一批物理页框,所以框号必然相同。四个选项恰好把"两个都一定、两个都不一定、以及两种交叉"摆齐,只要抓住"共享共享的是物理内存"就不会选错。
复习优先级:必须拿满,两道题各记一组判据。 管道那四条性质(半双工、 内核缓冲区、读写都可能阻塞、可多读多写)建议整组记住,2014-31 一题就考了四条。 第二条那个"虚拟页号不一定同、物理页框号一定同"在内存章已经出现过一次,是同一条结论。 第十条那三问的选型判据不会直接设问,但能防止"共享存储最快所以最好"这种误判。
易错:认为一个管道能双向传输。管道是半双工的,双向要开两个。
易错:认为管道容量受磁盘限制。它是内核里的一块固定大小缓冲区,不在磁盘上。
易错:认为管道只能一个读者一个写者。可以有多个,只是需要额外做互斥。
易错:认为只有写管道会阻塞。满则写阻塞、空则读阻塞,两边都会。
易错:认为共享内存下两个进程的虚拟地址一定相同。虚拟页号不一定同,物理页框号一定同。
易错:认为共享存储自带互斥。内核退出了数据通路,互斥必须自己外挂信号量。
易错:认为信号可以传数据、且不会丢。它用位向量记录,不计数、不排队、可能丢失。
易错:把"只能用于父子进程"套到所有管道上。只对匿名管道成立——它没有名字,只能靠继承 fd。
教材出处
- 汤小丹《计算机操作系统》2.6 进程通信、2.6.1 进程通信的类型,印刷 p67–p68:低级通信的两条不足(效率低、对用户不透明)与高级通信的两条特点;四大类高级通信机制;共享存储器系统的两种类型;管道的定义(连接读写进程的共享文件,又名 pipe 文件)与必须提供的三种协调能力(互斥、同步、确定对方是否存在)
- 汤小丹《计算机操作系统》2.6.2 消息传递通信的实现方式,印刷 p70–p71:对称寻址与非对称寻址;消息的格式(定长与变长的取舍);进程同步方式的三种阻塞组合;通信链路的两种建立方式与单向/双向之分
- 汤小丹《计算机操作系统》2.6.2 信箱通信,印刷 p71:信箱的结构——信箱头(含信箱标识符、拥有者、口令、空格数)与信箱体(若干信箱格,格数与格大小创建时确定)
相关知识
进程的组织与控制|信号量与 PV 操作|内存映射文件|中断和异常的处理|调度的基本概念与目标