进程间的通信--IPC(消息队列,共享内存)

目录

前言

[1. 管道:解决"数据流"问题](#1. 管道:解决“数据流”问题)

[2. 信号:解决"事件通知"问题](#2. 信号:解决“事件通知”问题)

[3. 其他IPC(共享内存、消息队列等):解决"高效/灵活通信"问题](#3. 其他IPC(共享内存、消息队列等):解决“高效/灵活通信”问题)

[1.2 基本使用流程](#1.2 基本使用流程)

[一、消息队列(Message Queue)](#一、消息队列(Message Queue))

[1. 概念](#1. 概念)

[2. 常用结构](#2. 常用结构)

[3. 常用函数](#3. 常用函数)

[(1) msgget ------ 创建/获取消息队列](#(1) msgget —— 创建/获取消息队列)

参数解释:

常用写法:

说明:

[(2) msgsnd ------ 发送消息](#(2) msgsnd —— 发送消息)

参数:

示例:

[(3) msgrcv ------ 接收消息](#(3) msgrcv —— 接收消息)

参数:

[msgtyp 规则(重点):](#msgtyp 规则(重点):)

[(4) msgctl ------ 控制/删除](#(4) msgctl —— 控制/删除)

参数:

[常用 cmd:](#常用 cmd:)

[二、共享内存(Shared Memory)](#二、共享内存(Shared Memory))

[1. 概念](#1. 概念)

[2. 常用函数](#2. 常用函数)

[(1) shmget ------ 创建共享内存](#(1) shmget —— 创建共享内存)

参数:

示例:

[(2) shmat ------ 连接共享内存](#(2) shmat —— 连接共享内存)

参数:

示例:

[(3) shmdt ------ 解除映射](#(3) shmdt —— 解除映射)

[(4) shmctl ------ 控制/删除](#(4) shmctl —— 控制/删除)

cmd:

[三、消息队列 vs 共享内存(重点对比)](#三、消息队列 vs 共享内存(重点对比))


前言

1. 管道:解决"数据流"问题

  • 它是什么:内核里的一段临时缓冲区,像个单向水管。

  • 能干嘛:把一个进程输出的字节流,原封不动地灌给另一个进程。

  • 局限性(为什么不够用)

    • 只能单向,数据只能从一个口进,从另一个口出。

    • 无边界,传过去就是一串连续的字节,接收方不知道哪里是头哪里是尾,得自己想办法切分。

    • 依赖亲缘关系,传统管道只能在父子进程间用(有名管道FIFO虽然能突破这个,但其他局限仍在)。

结论:管道只适合"一个进程生产数据,另一个进程消费数据"这种流水线场景。一旦通信不是这种模式,管道就废了。


2. 信号:解决"事件通知"问题

  • 它是什么:一种软件中断,内核给进程发一个编号(比如9、15)。

  • 能干嘛:告诉进程"出事了"或"你该干嘛了",比如"立刻死掉""暂停执行""重新读取配置"。

  • 为什么管道不行

    • 管道传的是数据,不是事件。如果要用管道通知进程退出,进程得一直读管道、解析内容,才知道"哦,这是让我退出",这浪费CPU。

    • 信号是异步的,进程正在执行任何代码时,信号都能随时插入打断(前提是没屏蔽),管道做不到这种"即时打断"能力。

  • 局限性(为什么不够用)

    • 信息量极少,信号只能带一个整数值,传不了复杂结构体。

    • 不可靠,信号可能被丢弃或合并,比如连续发多个同样的信号,进程可能只收到一个。

结论:信号只适合发"简单指令",不适合传数据。


3. 其他IPC(共享内存、消息队列等):解决"高效/灵活通信"问题

  • 共享内存

    • 物理内存里划一块公共区域,多个进程的虚拟地址都映射到它。

    • 为什么管道不行 :管道要经过内核拷贝(进程A→内核→进程B),共享内存直接读写那块区域,零拷贝,速度是管道的好几倍甚至几十倍。

  • 消息队列

    • 内核里维护的一个个消息包,每个包有类型、长度、内容。

    • 为什么管道不行:管道只能顺序读取,而消息队列可以按类型挑着读,而且每个消息有边界,不用自己切分字节流。

System V IPC 包含三种进程间通信机制,有消息队列,信号灯(也叫信号量),共享内存。此外还有 System V IPC 的补充版本 POSIX IPC,这两组 IPC 的通信方法基本一致,本章以 System V IPC 为例介绍 Linux 进程通信 机制。

1.2 基本使用流程

bash 复制代码
// SystemV IPC 包含三种内核对象
1. 消息队列 (Message Queue)
2. 共享内存 (Shared Memory)  
3. 信号量 (Semaphore)

// 查看当前系统的IPC对象
$ ipcs -a

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x12345678 0          root       666        0            0

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x12345679 0          root       666        1024       0

------ Semaphore Arrays --------
key        semid      owner      perms      nsems     
0x1234567a 0          root       666        1

一、消息队列(Message Queue)

1. 概念

shmget(IPC_CREAT) 的行为是:

有就返回已有的,没有才创建,不会重复创建多个同 key 的共享内存。

消息队列是一种内核维护的链式队列

  • 进程 → 发消息 → 内核队列
  • 另一个进程 → 从队列取消息
  • 异步通信
  • 按消息类型过滤

2. 常用结构

复制代码
struct msgbuf {
    long mtype;      // 消息类型(必须 > 0)
    char mtext[128]; // 消息内容
};

3. 常用函数

(1) msgget ------ 创建/获取消息队列

复制代码
int msgget(key_t key, int msgflg);

参数解释:

参数 含义
key IPC key(ftok生成或IPC_PRIVATE)
msgflg 权限 + 创建标志

常用写法:

复制代码
msgget(key, IPC_CREAT | 0666);

说明:

  • IPC_CREAT:不存在就创建
  • 0666:权限(读写)

(2) msgsnd ------ 发送消息

复制代码
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数:

参数 含义
msqid msgget返回的ID
msgp 消息结构体指针
msgsz 消息正文长度(不包括mtype)
msgflg 控制阻塞方式

示例:

复制代码
msgsnd(msqid, &msg, strlen(msg.mtext)+1, 0);

(3) msgrcv ------ 接收消息

复制代码
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz,
               long msgtyp, int msgflg);

参数:

参数 含义
msqid 队列ID
msgp 接收缓冲区
msgsz 最大接收长度
msgtyp 消息类型过滤
msgflg 控制阻塞

msgtyp 规则(重点):

含义
0 接收第一条消息
>0 接收指定 type
<0 接收 ≤ 绝对值的最小 type

(4) msgctl ------ 控制/删除

复制代码
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

参数:

参数 含义
msqid 队列ID
cmd 操作命令
buf 状态结构(可为NULL)

常用 cmd:

命令 含义
IPC_RMID 删除队列
IPC_STAT 获取状态
IPC_SET 修改属性

二、共享内存(Shared Memory)

1. 概念

共享内存 = 最快 IPC

  • 多进程共享同一块物理内存
  • 不需要拷贝数据(零拷贝)
  • 需要自己加同步(信号量)

2. 常用函数


(1) shmget ------ 创建共享内存

复制代码
int shmget(key_t key, size_t size, int shmflg);

参数:

参数 含义
key ftok生成或IPC_PRIVATE
size 共享内存大小
shmflg 权限 + 创建标志

示例:

复制代码
shmget(key, 1024, IPC_CREAT | 0666);

(2) shmat ------ 连接共享内存

复制代码
void *shmat(int shmid, const void *shmaddr, int shmflg);

参数:

参数 含义
shmid shmget返回ID
shmaddr 通常 NULL(系统自动分配)
shmflg 0 或 SHM_RDONLY

示例:

复制代码
复制代码
char *p = shmat(shmid, NULL, 0);

(3) shmdt ------ 解除映射

复制代码
int shmdt(const void *shmaddr);
参数 含义
shmaddr shmat返回的地址

(4) shmctl ------ 控制/删除

复制代码
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

cmd:

命令 含义
IPC_RMID 删除共享内存
IPC_STAT 查看信息
IPC_SET 修改

三、消息队列 vs 共享内存(重点对比)

对比项 消息队列 共享内存
速度 中等(拷贝) 最快(零拷贝)
通信方式 内核转发 直接内存访问
结构 有序消息 原始内存
同步 自动 需要自己做
复杂度 简单 较复杂
使用场景 通信控制 大数据交换