属于 System‑V IPC 对象,用于同一台主机上进程间通信,进程空间相互独立,依靠内核对象完成数据交互。
一、共享内存(Shared Memory)
1. 基本概念
共享内存是把内核中的一块内存映射到多个进程的虚拟地址空间 ,多个进程直接读写同一块物理内存,是IPC中速度最快的通信方式。
- 数据不需要在内核缓冲区和用户空间之间来回拷贝。
- 本身不带同步互斥机制,多个进程同时读写会产生竞争,需要搭配信号量、互斥锁做保护。
2. 核心函数
①获取IPC对象的key值:ftok
② 创建/获取共享内存段:shmget
int shmget(key_t key, size_t size, int shmflg);
- key :IPC键值,用来找到同一个共享内存对象;
IPC_PRIVATE代表创建私有对象。 - size:共享内存字节大小,创建时指定,获取已有对象填0。
- shmflg :权限标志,
IPC_CREAT | 0664不存在则创建,设置权限。 - 返回值:成功返回共享内存id(shmid);失败返回‑1。
③ 将共享内存映射到进程用户空间:shmat
void *shmat(int shmid, const void *shmaddr, int shmflg);
- shmid:shmget返回的共享内存ID。
- shmaddr:指定映射地址,一般传
NULL由系统自动分配。 - shmflg:0读写;
SHM_RDONLY只读。 - 返回值:成功返回映射后的虚拟内存首地址;失败返回
(void*)-1。 拿到返回地址后,进程就可以像普通数组/指针一样读写这块内存。
④向用户空间写入/读出数据
⑤解除映射:shmdt
int shmdt(const void *shmaddr);
- shmaddr:shmat得到的映射首地址。
- 作用:断开当前进程和共享内存的映射;不会删除内核中的共享内存对象。
- 返回:成功0,失败‑1。
⑥控制共享内存:shmctl
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
常用cmd:
IPC_RMID:删除内核中的共享内存对象。只要还有进程挂载,内核不会立刻销毁,等全部进程shmdt之后销毁。IPC_STAT:获取共享内存状态信息。IPC_SET:修改权限等属性。
3. 共享内存特点
- 速度最快,直接访问物理内存,无数据拷贝。
- 没有自带同步,必须自己加锁/信号量,否则多进程读写会乱数据。
- 生命周期随内核:进程退出,只要不调用
shmctl(IPC_RMID),共享内存还存在内核。 - 多个进程可以读写,数据写完一直保留在共享内存,不会自动清除。
4. 使用流程
shmget()创建或者获取共享内存IDshmat()挂载得到用户空间地址,读写内存shmdt()使用完毕解除映射shmctl(IPC_RMID)不再使用时删除内核对象(一般由某一个进程负责销毁)
二、消息队列(Message Queue)
1. 基本概念
消息队列是内核维护的消息链表。进程往队列里发送带类型的消息块,别的进程按消息类型读取消息。
- 每条消息包含:消息类型 + 消息数据。
- 消息读取是按类型筛选读取,不一定严格先进先出,可以选择性读某一类消息。
- 属于System‑V IPC对象,生命周期随内核。
2. 核心函数
①获取IPC对象key值:ftok()
key_t ftok(const char *pathname, int proj_id);
- 功能:获取IPC对象的key值
- 参数:
- pathname:路径名
- proj_id:工程id
- 返回值:
- 成功:key值dian
②创建/获取消息队列:msgget
int msgget(key_t key, int msgflg);
- key:IPC键值;
IPC_PRIVATE创建私有消息队列。 - msgflg:
IPC_CREAT | 0664,不存在就创建,设置访问权限。 - 返回:成功返回消息队列ID
msqid;失败‑1。
③发送消息:msgsnd
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
-
msqid:msgget返回id。
-
msgp:自定义消息结构体指针,第一个成员必须是long mtype(消息类型 >0)。 示例消息结构体:
struct msgbuf{
long mtype; //消息类型,必须大于0
char mtext[100]; //消息数据
}; -
msgsz:消息数据部分的字节大小(不包含
long mtype)。 -
msgflg:
0队列满则阻塞;IPC_NOWAIT非阻塞,队列满直接返回错误。 -
返回:成功0;失败‑1。
④ 接收消息:msgrcv
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
- msgtyp:读取消息类型
msgtyp >0:读取队列中类型等于msgtyp的第一条消息。msgtyp =0:读取队列第一条消息(FIFO)。msgtyp <0:读取类型绝对值以内最小类型消息。
- msgflg:
0无对应消息阻塞;IPC_NOWAIT没有消息直接返回‑1。 - 返回值:成功返回读到的数据字节数;失败‑1。 消息被读取之后,会从内核消息队列链表移除。
⑤ 删除消息队列:msgctl
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
常用cmd:
IPC_RMID:删除内核的消息队列对象,立刻销毁,队列内未读消息全部丢弃。IPC_STAT:获取消息队列状态。
3. 消息队列特点
- 以消息块为单位通信,每条消息自带类型,可以按类型选择性接收。
- 自带同步:队列空读阻塞,队列满写阻塞(也可以设置非阻塞)。
- 数据存在内核,进程退出消息不会消失;消息读完就从队列删除。
- 生命周期随内核,不调用
msgctl(IPC_RMID),消息队列一直存在。 - 有消息大小、队列总字节数上限。
4. 使用流程
msgget()创建/获取消息队列id- 发送方:构造消息结构体 →
msgsnd()发送消息 - 接收方:
msgrcv()按类型接收消息 - 不再使用:
msgctl(IPC_RMID)删除消息队列
三、共享内存 vs 消息队列对比
| 对比项 | 共享内存 | 消息队列 |
|---|---|---|
| 传输效率 | 极高,直接访问内存,无拷贝 | 相对低,需要内核拷贝消息 |
| 同步互斥 | 无自带同步,需要外部信号量/锁 | 自带阻塞同步机制 |
| 数据形式 | 一块连续内存,字节流 | 一条条带类型的消息块 |
| 数据生命周期 | 数据保留,需要手动覆盖清除 | 消息读取之后自动删除 |
| 适用场景 | 大数据量高速传输 | 离散消息传递,多类型消息区分 |
查看系统IPC命令:
ipcs -m #查看共享内存
ipcs -q #查看消息队列
ipcrm -m shmid #删除共享内存
ipcrm -q msqid #删除消息队列