Linux 进程通信介绍:内存隔离与 IPC 分类

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 通常使用写时复制:

  1. 刚执行 fork 时,父子暂时共享只读物理页。
  2. 子进程准备修改 number 时触发缺页处理。
  3. 内核复制相关内存页。
  4. 子进程修改自己的副本,父进程仍看到原值。

所以普通全局变量可以用于线程间共享,却不能直接用于进程间共享。

课程录音使用 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 三条使用规则

  1. 只有函数返回值明确表示失败后,才检查 errno。
  2. 其他函数调用可能改变 errno;需要稍后使用时先保存。
  3. 不要背错误编号。编号可能随平台变化,应使用 ENOENT、EACCES、EISDIR 等宏。

perror("open") 会把下面内容写到标准错误,并自动补换行:

text 复制代码
open: No such file or directory

现代线程库环境中,errno 通常实现为线程局部状态,不能把它简单理解成所有线程共同修改的普通全局变量。

13. 转写与表述校正

  1. 录音中的"进程监控中心"等词,多数是"进程间通信"的语音识别错误。
  2. 子进程修改全局变量后父进程不变,原因是独立虚拟地址空间与写时复制。
  3. sleep 只能让实验输出看起来有顺序,不能作为可靠同步;应使用 waitpid。
  4. 进程通信通常由内核提供机制,但共享内存映射后不是每次读写都经系统调用复制。
  5. 共享内存负责共享数据,不自动提供互斥;通常要配合同步机制。
  6. 信号量主要负责同步,不是用来传递大块业务数据的"信号消息"。
  7. System V IPC 与 POSIX IPC 都仍可使用,不能笼统地说 POSIX 一定性能更高。
  8. 错误号 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. 本节最低掌握标准

学完后能够回答:

  1. 为什么两个进程不能通过普通全局变量直接通信?
  2. 匿名管道和 FIFO 的使用对象有什么区别?
  3. 共享内存为什么快,为什么还要同步?
  4. 消息队列与管道的"消息边界"有什么不同?
  5. 信号量解决的是数据传输还是同步问题?
  6. System V IPC 包含哪三类机制?
  7. 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

进一步核对资料:

相关推荐
码完就睡1 小时前
Linux——计算机网络基础概论
linux·计算机网络
别动我齐刘海1 小时前
ROS2 Jazzy + C++ 时间系统——Time / Clock / Duration
linux·c++·人工智能·学习·机器学习·机器人·自动驾驶
机核研创社1 小时前
polo 长袖短裤套装自动化产线:八台机器排队接力
android·java·自动化
cuijiecheng20182 小时前
MobaXterm上传大文件到rk3588板子失败
linux
Roadinforest2 小时前
Spring Boot 4 六步入门实录:从 Hello World 到一个能交付的 Todo API
java·开发语言·spring boot·spring
半仙白桑2 小时前
内核篇第十三讲:system-V消息队列
java·linux·网络
xlq223222 小时前
服务器复习3
服务器
今天的砖头有点烫手啊2 小时前
GC 算法与垃圾收集器演进:从 Serial 到 ZGC
java·jvm
牧瀬クリスだ2 小时前
Mybatis-Plus:让CRUD快如闪电
java·spring boot·spring·tomcat·mybatis·yml