Linux 进程通信介绍:内存隔离与 IPC 分类
课程:尚硅谷《嵌入式 Linux 应用层开发》第 3 章进程间通信
依据:2026-09-23 19:56 录音转写;课程 PDF 的 PDF 第 85 页。
本节边界:介绍进程通信的必要性、常见 IPC 类型、System V IPC 与 POSIX IPC,并通过
errno/perror和fork后全局变量实验做课前准备;具体管道编程留到下一节。
1. 为什么需要进程间通信
每个进程拥有独立的虚拟地址空间。进程 A 中普通变量的地址和值,不能被进程 B 直接当作自己的变量访问。
#mermaid-svg-ePCjZ5AAtaXwbzRG{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ePCjZ5AAtaXwbzRG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ePCjZ5AAtaXwbzRG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ePCjZ5AAtaXwbzRG .error-icon{fill:#552222;}#mermaid-svg-ePCjZ5AAtaXwbzRG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ePCjZ5AAtaXwbzRG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ePCjZ5AAtaXwbzRG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ePCjZ5AAtaXwbzRG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ePCjZ5AAtaXwbzRG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ePCjZ5AAtaXwbzRG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ePCjZ5AAtaXwbzRG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ePCjZ5AAtaXwbzRG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ePCjZ5AAtaXwbzRG .marker.cross{stroke:#333333;}#mermaid-svg-ePCjZ5AAtaXwbzRG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ePCjZ5AAtaXwbzRG p{margin:0;}#mermaid-svg-ePCjZ5AAtaXwbzRG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ePCjZ5AAtaXwbzRG .cluster-label text{fill:#333;}#mermaid-svg-ePCjZ5AAtaXwbzRG .cluster-label span{color:#333;}#mermaid-svg-ePCjZ5AAtaXwbzRG .cluster-label span p{background-color:transparent;}#mermaid-svg-ePCjZ5AAtaXwbzRG .label text,#mermaid-svg-ePCjZ5AAtaXwbzRG span{fill:#333;color:#333;}#mermaid-svg-ePCjZ5AAtaXwbzRG .node rect,#mermaid-svg-ePCjZ5AAtaXwbzRG .node circle,#mermaid-svg-ePCjZ5AAtaXwbzRG .node ellipse,#mermaid-svg-ePCjZ5AAtaXwbzRG .node polygon,#mermaid-svg-ePCjZ5AAtaXwbzRG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ePCjZ5AAtaXwbzRG .rough-node .label text,#mermaid-svg-ePCjZ5AAtaXwbzRG .node .label text,#mermaid-svg-ePCjZ5AAtaXwbzRG .image-shape .label,#mermaid-svg-ePCjZ5AAtaXwbzRG .icon-shape .label{text-anchor:middle;}#mermaid-svg-ePCjZ5AAtaXwbzRG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ePCjZ5AAtaXwbzRG .rough-node .label,#mermaid-svg-ePCjZ5AAtaXwbzRG .node .label,#mermaid-svg-ePCjZ5AAtaXwbzRG .image-shape .label,#mermaid-svg-ePCjZ5AAtaXwbzRG .icon-shape .label{text-align:center;}#mermaid-svg-ePCjZ5AAtaXwbzRG .node.clickable{cursor:pointer;}#mermaid-svg-ePCjZ5AAtaXwbzRG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ePCjZ5AAtaXwbzRG .arrowheadPath{fill:#333333;}#mermaid-svg-ePCjZ5AAtaXwbzRG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ePCjZ5AAtaXwbzRG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ePCjZ5AAtaXwbzRG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ePCjZ5AAtaXwbzRG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ePCjZ5AAtaXwbzRG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ePCjZ5AAtaXwbzRG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ePCjZ5AAtaXwbzRG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ePCjZ5AAtaXwbzRG .cluster text{fill:#333;}#mermaid-svg-ePCjZ5AAtaXwbzRG .cluster span{color:#333;}#mermaid-svg-ePCjZ5AAtaXwbzRG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ePCjZ5AAtaXwbzRG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ePCjZ5AAtaXwbzRG rect.text{fill:none;stroke-width:0;}#mermaid-svg-ePCjZ5AAtaXwbzRG .icon-shape,#mermaid-svg-ePCjZ5AAtaXwbzRG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ePCjZ5AAtaXwbzRG .icon-shape p,#mermaid-svg-ePCjZ5AAtaXwbzRG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ePCjZ5AAtaXwbzRG .icon-shape .label rect,#mermaid-svg-ePCjZ5AAtaXwbzRG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ePCjZ5AAtaXwbzRG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ePCjZ5AAtaXwbzRG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ePCjZ5AAtaXwbzRG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 进程 B 的虚拟地址空间
进程 A 的虚拟地址空间
普通变量不能直接跨进程传递
变量 number = 1
变量 number = 0
内核提供的 IPC 对象
或双方共同映射的共享内存
这意味着:
- 子进程修改自己的全局变量,父进程的同名变量不会跟着变化;
execve加载新程序后,旧程序的普通变量、堆和栈都会被替换;- 多个进程若要交换数据或协调先后顺序,必须使用明确的 IPC 机制。
IPC 是 Inter-Process Communication,即进程间通信。
2. fork 后的内存隔离实验
课程通过一个全局变量说明父子进程不能直接用普通变量通信:
c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
static int number = 0;
int main(void)
{
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return EXIT_FAILURE;
}
if (pid == 0) {
number = 1;
printf("子进程:number=%d\n", number);
return EXIT_SUCCESS;
}
int status;
while (waitpid(pid, &status, 0) == -1) {
if (errno != EINTR) {
perror("waitpid");
return EXIT_FAILURE;
}
}
printf("父进程:number=%d\n", number);
return EXIT_SUCCESS;
}
预期结果:
text
子进程:number=1
父进程:number=0
2.1 为什么子进程改了,父进程没变
fork 时,父子进程的内存内容在逻辑上相同,但它们属于两个独立地址空间。Linux 通常使用写时复制:
- 刚执行
fork时,父子暂时共享只读物理页。 - 子进程准备修改
number时触发缺页处理。 - 内核复制相关内存页。
- 子进程修改自己的副本,父进程仍看到原值。
所以普通全局变量可以用于线程间共享,却不能直接用于进程间共享。
课程录音使用 sleep 强制子进程先打印。上面的版本改用 waitpid,能可靠保证父进程在子进程结束后再打印。
3. 常见 IPC 方法总览
PDF 第 85 页列出五类常用方式:
| IPC 方式 | 数据形态 | 适合场景 | 主要特点 |
|---|---|---|---|
| Unix Domain Socket | 字节流或报文 | 同一台主机上的客户端/服务器 | 双向、接口接近网络 Socket,可传递凭据或文件描述符 |
| 匿名管道 | 无结构字节流 | 有亲缘关系的进程 | 简单、单向、依赖继承的 fd |
| 有名管道 FIFO | 无结构字节流 | 无亲缘关系的本机进程 | 文件系统中有名字,仍由内核传输数据 |
| 共享内存 | 一块共同映射的内存 | 大数据量、高吞吐共享 | 数据复制少,但必须另外解决同步问题 |
| 消息队列 | 有边界的消息 | 异步发送、按条接收 | 内核保存消息,天然保留消息边界 |
| 信号量 | 计数或同步状态 | 互斥、资源计数、执行顺序控制 | 主要用于同步,通常不负责传递业务数据 |
PDF 把有名、匿名管道合并为一项,所以页面上显示五类;拆开讲时会看到六行。
3.1 选择 IPC 的基本思路
#mermaid-svg-Uyp2031hry35Jo2C{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Uyp2031hry35Jo2C .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Uyp2031hry35Jo2C .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Uyp2031hry35Jo2C .error-icon{fill:#552222;}#mermaid-svg-Uyp2031hry35Jo2C .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Uyp2031hry35Jo2C .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Uyp2031hry35Jo2C .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Uyp2031hry35Jo2C .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Uyp2031hry35Jo2C .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Uyp2031hry35Jo2C .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Uyp2031hry35Jo2C .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Uyp2031hry35Jo2C .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Uyp2031hry35Jo2C .marker.cross{stroke:#333333;}#mermaid-svg-Uyp2031hry35Jo2C svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Uyp2031hry35Jo2C p{margin:0;}#mermaid-svg-Uyp2031hry35Jo2C .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Uyp2031hry35Jo2C .cluster-label text{fill:#333;}#mermaid-svg-Uyp2031hry35Jo2C .cluster-label span{color:#333;}#mermaid-svg-Uyp2031hry35Jo2C .cluster-label span p{background-color:transparent;}#mermaid-svg-Uyp2031hry35Jo2C .label text,#mermaid-svg-Uyp2031hry35Jo2C span{fill:#333;color:#333;}#mermaid-svg-Uyp2031hry35Jo2C .node rect,#mermaid-svg-Uyp2031hry35Jo2C .node circle,#mermaid-svg-Uyp2031hry35Jo2C .node ellipse,#mermaid-svg-Uyp2031hry35Jo2C .node polygon,#mermaid-svg-Uyp2031hry35Jo2C .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Uyp2031hry35Jo2C .rough-node .label text,#mermaid-svg-Uyp2031hry35Jo2C .node .label text,#mermaid-svg-Uyp2031hry35Jo2C .image-shape .label,#mermaid-svg-Uyp2031hry35Jo2C .icon-shape .label{text-anchor:middle;}#mermaid-svg-Uyp2031hry35Jo2C .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Uyp2031hry35Jo2C .rough-node .label,#mermaid-svg-Uyp2031hry35Jo2C .node .label,#mermaid-svg-Uyp2031hry35Jo2C .image-shape .label,#mermaid-svg-Uyp2031hry35Jo2C .icon-shape .label{text-align:center;}#mermaid-svg-Uyp2031hry35Jo2C .node.clickable{cursor:pointer;}#mermaid-svg-Uyp2031hry35Jo2C .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Uyp2031hry35Jo2C .arrowheadPath{fill:#333333;}#mermaid-svg-Uyp2031hry35Jo2C .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Uyp2031hry35Jo2C .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Uyp2031hry35Jo2C .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Uyp2031hry35Jo2C .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Uyp2031hry35Jo2C .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Uyp2031hry35Jo2C .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Uyp2031hry35Jo2C .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Uyp2031hry35Jo2C .cluster text{fill:#333;}#mermaid-svg-Uyp2031hry35Jo2C .cluster span{color:#333;}#mermaid-svg-Uyp2031hry35Jo2C div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Uyp2031hry35Jo2C .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Uyp2031hry35Jo2C rect.text{fill:none;stroke-width:0;}#mermaid-svg-Uyp2031hry35Jo2C .icon-shape,#mermaid-svg-Uyp2031hry35Jo2C .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Uyp2031hry35Jo2C .icon-shape p,#mermaid-svg-Uyp2031hry35Jo2C .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Uyp2031hry35Jo2C .icon-shape .label rect,#mermaid-svg-Uyp2031hry35Jo2C .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Uyp2031hry35Jo2C .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Uyp2031hry35Jo2C .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Uyp2031hry35Jo2C :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 简单字节流
本机双向客户端服务器
大量数据、低复制
需要消息边界和排队
只需同步或互斥
需要进程协作
主要需求是什么
管道或 FIFO
Unix Domain Socket
共享内存
消息队列
信号量
实际程序经常组合使用,例如"共享内存传数据,信号量负责通知和互斥"。
4. 内核在 IPC 中的作用
不同进程不能直接访问对方的私有地址空间,因此 IPC 的建立、权限和生命周期通常由内核管理。
不过,"所有数据每次都必须通过内核复制"并不准确:
- 管道、Socket、消息队列的数据通常进入内核缓冲区,再交给接收进程。
- 共享内存由内核建立和管理映射;映射成功后,进程可直接读写共同物理页,不需要每次访问都调用系统调用。
- 信号量的等待和唤醒需要内核协调,但它主要传递同步状态,不搬运大块业务数据。
这也是共享内存吞吐量通常较高、但编程复杂度也更高的原因。
5. 管道
5.1 匿名管道
c
int pipefd[2];
pipe(pipefd);
pipefd[0]:读端。pipefd[1]:写端。- 常在
fork前创建,父子进程继承描述符后分别关闭不用的一端。 - 默认是单向字节流,需要双向通信时通常建立两条管道或选择 Socket。
text
写进程 --write(pipefd[1])--> 内核管道缓冲区 --read(pipefd[0])--> 读进程
5.2 有名管道 FIFO
FIFO 在文件系统中有路径,可以让没有父子关系的进程通过同一个名字打开并通信。路径只是入口,实际数据仍在内核管道缓冲区中流动。
管道没有消息边界。一次 write 的数据,接收方可能分多次 read;应用仍需自己设计记录格式。
6. Unix Domain Socket
Unix Domain Socket 使用 Socket 风格接口在同一主机通信:
text
socket → bind → listen → accept → read/write
socket → connect → read/write
它与 TCP Socket 的共同点是支持客户端/服务器模型;区别是通信范围在本机,可通过文件系统路径或 Linux 抽象地址命名,不经过 IP 路由。
常见用途:
- 本机服务与客户端通信;
- 桌面组件、数据库和容器工具的控制接口;
- 通过
SCM_RIGHTS在进程间传递已打开的文件描述符。
课程说明这一部分会放到后面的 Socket 章节学习。
7. 共享内存
共享内存让多个进程把同一个内核对象映射到各自地址空间:
#mermaid-svg-KUGLqMplJGIzvcwW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-KUGLqMplJGIzvcwW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KUGLqMplJGIzvcwW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KUGLqMplJGIzvcwW .error-icon{fill:#552222;}#mermaid-svg-KUGLqMplJGIzvcwW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KUGLqMplJGIzvcwW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KUGLqMplJGIzvcwW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KUGLqMplJGIzvcwW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KUGLqMplJGIzvcwW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KUGLqMplJGIzvcwW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KUGLqMplJGIzvcwW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KUGLqMplJGIzvcwW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KUGLqMplJGIzvcwW .marker.cross{stroke:#333333;}#mermaid-svg-KUGLqMplJGIzvcwW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KUGLqMplJGIzvcwW p{margin:0;}#mermaid-svg-KUGLqMplJGIzvcwW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KUGLqMplJGIzvcwW .cluster-label text{fill:#333;}#mermaid-svg-KUGLqMplJGIzvcwW .cluster-label span{color:#333;}#mermaid-svg-KUGLqMplJGIzvcwW .cluster-label span p{background-color:transparent;}#mermaid-svg-KUGLqMplJGIzvcwW .label text,#mermaid-svg-KUGLqMplJGIzvcwW span{fill:#333;color:#333;}#mermaid-svg-KUGLqMplJGIzvcwW .node rect,#mermaid-svg-KUGLqMplJGIzvcwW .node circle,#mermaid-svg-KUGLqMplJGIzvcwW .node ellipse,#mermaid-svg-KUGLqMplJGIzvcwW .node polygon,#mermaid-svg-KUGLqMplJGIzvcwW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KUGLqMplJGIzvcwW .rough-node .label text,#mermaid-svg-KUGLqMplJGIzvcwW .node .label text,#mermaid-svg-KUGLqMplJGIzvcwW .image-shape .label,#mermaid-svg-KUGLqMplJGIzvcwW .icon-shape .label{text-anchor:middle;}#mermaid-svg-KUGLqMplJGIzvcwW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KUGLqMplJGIzvcwW .rough-node .label,#mermaid-svg-KUGLqMplJGIzvcwW .node .label,#mermaid-svg-KUGLqMplJGIzvcwW .image-shape .label,#mermaid-svg-KUGLqMplJGIzvcwW .icon-shape .label{text-align:center;}#mermaid-svg-KUGLqMplJGIzvcwW .node.clickable{cursor:pointer;}#mermaid-svg-KUGLqMplJGIzvcwW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KUGLqMplJGIzvcwW .arrowheadPath{fill:#333333;}#mermaid-svg-KUGLqMplJGIzvcwW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KUGLqMplJGIzvcwW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KUGLqMplJGIzvcwW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KUGLqMplJGIzvcwW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KUGLqMplJGIzvcwW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KUGLqMplJGIzvcwW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KUGLqMplJGIzvcwW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KUGLqMplJGIzvcwW .cluster text{fill:#333;}#mermaid-svg-KUGLqMplJGIzvcwW .cluster span{color:#333;}#mermaid-svg-KUGLqMplJGIzvcwW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-KUGLqMplJGIzvcwW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KUGLqMplJGIzvcwW rect.text{fill:none;stroke-width:0;}#mermaid-svg-KUGLqMplJGIzvcwW .icon-shape,#mermaid-svg-KUGLqMplJGIzvcwW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KUGLqMplJGIzvcwW .icon-shape p,#mermaid-svg-KUGLqMplJGIzvcwW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KUGLqMplJGIzvcwW .icon-shape .label rect,#mermaid-svg-KUGLqMplJGIzvcwW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KUGLqMplJGIzvcwW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KUGLqMplJGIzvcwW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KUGLqMplJGIzvcwW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 进程 A 虚拟地址
共同物理内存页
进程 B 虚拟地址
一方写入后,另一方可以从自己的映射地址读取。但共享内存本身不保证:
- 谁先读、谁先写;
- 数据写到一半是否被另一方看到;
- 多个写进程会不会互相覆盖;
- 对方是否已经准备好。
因此通常还要结合信号量、进程共享互斥锁或其他通知机制。
8. 消息队列
消息队列由内核保存一条条消息。与管道的字节流不同,接收方通常能按消息单位读取。
适合:
- 生产者和消费者速度不同;
- 发送方不要求接收方同时运行在读取位置;
- 需要消息优先级或消息类型;
- 数据量适中、希望接口比共享内存简单。
消息队列容量有限,不适合无节制地堆积大量数据。
9. 信号量
信号量可看作由内核维护的计数器,用于表示可用资源数量或控制进入临界区的进程数。
text
wait / P 操作:计数可用时减一;不可用时等待
post / V 操作:计数加一,并可能唤醒等待者
它主要解决"什么时候可以访问"和"同时允许几个进程访问",不用于承载传感器数据、文本或文件内容。
10. System V IPC
System V(读作 System Five)是 UNIX 发展史上的重要分支。System V IPC 在 Linux 中包括三组机制:
| 机制 | 常用接口 | 作用 |
|---|---|---|
| 消息队列 | msgget/msgsnd/msgrcv/msgctl |
按消息交换数据 |
| 信号量集合 | semget/semop/semctl |
同步与资源计数 |
| 共享内存 | shmget/shmat/shmdt/shmctl |
共享内存区域 |
System V IPC 对象通常通过 key 创建或查找,系统返回数字 ID。对象可能在创建进程退出后继续存在,需要显式删除;调试时可使用:
bash
ipcs
ipcs -q # 消息队列
ipcs -m # 共享内存
ipcs -s # 信号量
ipcrm # 删除指定 IPC 对象
11. POSIX IPC
POSIX 提供了另一组消息队列、信号量和共享内存接口:
| 机制 | 常用接口 | 常见命名方式 |
|---|---|---|
| POSIX 消息队列 | mq_open/mq_send/mq_receive/mq_unlink |
如 /sensor_queue |
| POSIX 信号量 | sem_open/sem_wait/sem_post/sem_unlink |
如 /sensor_sem |
| POSIX 共享内存 | shm_open/ftruncate/mmap/shm_unlink |
如 /sensor_shm |
POSIX 命名对象通常使用以 / 开头的名称,并通过类似文件描述符或指针的接口管理。
11.1 System V 与 POSIX 怎样选择
| 对比 | System V IPC | POSIX IPC |
|---|---|---|
| 历史 | 较早、传统 UNIX 广泛支持 | 标准化较晚,接口通常更接近现代 POSIX 风格 |
| 标识 | key 与数字 ID | 名称、描述符或 sem_t |
| 生命周期 | 常需控制命令显式删除 | 使用 *_unlink 管理命名对象 |
| 常见场景 | 维护旧系统、已有项目接口 | 新项目、POSIX 风格程序 |
不能简单断言 POSIX IPC 在所有场景下一定更快。性能取决于具体机制、内核实现、数据大小、同步方式和访问模式。项目已经采用哪套接口、目标系统支持情况以及团队维护成本,通常比"新旧"标签更重要。
12. errno、perror 与 strerror
录音在进入管道前复习了错误报告,这是后续 IPC 调试的必要基础。
系统调用或库函数失败时,通常通过返回值表示失败,并设置 errno:
c
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int fd = open("missing.txt", O_RDONLY);
if (fd == -1) {
int saved_errno = errno;
perror("open missing.txt");
fprintf(stderr, "errno=%d, message=%s\n",
saved_errno, strerror(saved_errno));
}
12.1 三条使用规则
- 只有函数返回值明确表示失败后,才检查
errno。 - 其他函数调用可能改变
errno;需要稍后使用时先保存。 - 不要背错误编号。编号可能随平台变化,应使用
ENOENT、EACCES、EISDIR等宏。
perror("open") 会把下面内容写到标准错误,并自动补换行:
text
open: No such file or directory
现代线程库环境中,errno 通常实现为线程局部状态,不能把它简单理解成所有线程共同修改的普通全局变量。
13. 转写与表述校正
- 录音中的"进程监控中心"等词,多数是"进程间通信"的语音识别错误。
- 子进程修改全局变量后父进程不变,原因是独立虚拟地址空间与写时复制。
sleep只能让实验输出看起来有顺序,不能作为可靠同步;应使用waitpid。- 进程通信通常由内核提供机制,但共享内存映射后不是每次读写都经系统调用复制。
- 共享内存负责共享数据,不自动提供互斥;通常要配合同步机制。
- 信号量主要负责同步,不是用来传递大块业务数据的"信号消息"。
- System V IPC 与 POSIX IPC 都仍可使用,不能笼统地说 POSIX 一定性能更高。
- 错误号 21 在课程机器的实验中表示
EISDIR,应用应判断错误宏或打印说明,不应依赖数字 21。
14. 与测试 A 的关系
测试 A 的 TCP Server 和三个 Client 本身属于进程之间通过 Socket 通信。测试 A 采用网络 TCP Socket,而本节列出的 Unix Domain Socket 主要用于同一台 Linux 主机。
对测试 A 当前最有用的认识是:
- Client 与 Server 内存隔离,必须制定消息协议;
- Server 收到的只是字节,需明确命令类型、长度和数据格式;
- 多个 Server 线程共享同一进程内存,需要互斥锁;
- 多个独立进程之间则要使用 Socket、管道、共享内存等 IPC;
- 所有系统调用都要检查返回值,并使用
perror或日志记录失败原因。
本节不要求你现在掌握全部 IPC。按课程继续先学管道,再学习消息队列和共享内存即可。
15. 本节最低掌握标准
学完后能够回答:
- 为什么两个进程不能通过普通全局变量直接通信?
- 匿名管道和 FIFO 的使用对象有什么区别?
- 共享内存为什么快,为什么还要同步?
- 消息队列与管道的"消息边界"有什么不同?
- 信号量解决的是数据传输还是同步问题?
- System V IPC 包含哪三类机制?
errno应在什么时候读取,perror输出什么?
最简记忆:
text
普通变量:只能在本进程地址空间中直接使用
管道/Socket/队列:通过内核传递数据
共享内存:多个进程映射同一内存,另加同步
信号量:负责同步和资源计数
16. 资料页码与录音时间索引
| 主题 | PDF 页码 | 录音时间 |
|---|---|---|
| 进程内存隔离与 IPC 必要性 | 85 | 00:05---01:20 |
| 常见 IPC 类型 | 85 | 00:32---01:20 |
| System V 与 POSIX IPC 介绍 | 85及录音补充 | 01:21---02:50 |
errno/perror 复习 |
录音补充 | 02:51---07:29 |
fork 后全局变量隔离实验 |
录音补充 | 07:30---10:15 |
进一步核对资料: