Linux消息队列 & 共享内存 笔记

属于 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:

  1. IPC_RMID删除内核中的共享内存对象。只要还有进程挂载,内核不会立刻销毁,等全部进程shmdt之后销毁。
  2. IPC_STAT:获取共享内存状态信息。
  3. IPC_SET:修改权限等属性。

3. 共享内存特点

  1. 速度最快,直接访问物理内存,无数据拷贝。
  2. 没有自带同步,必须自己加锁/信号量,否则多进程读写会乱数据。
  3. 生命周期随内核:进程退出,只要不调用shmctl(IPC_RMID),共享内存还存在内核。
  4. 多个进程可以读写,数据写完一直保留在共享内存,不会自动清除。

4. 使用流程

  1. shmget() 创建或者获取共享内存ID
  2. shmat() 挂载得到用户空间地址,读写内存
  3. shmdt() 使用完毕解除映射
  4. 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. 消息队列特点

  1. 以消息块为单位通信,每条消息自带类型,可以按类型选择性接收。
  2. 自带同步:队列空读阻塞,队列满写阻塞(也可以设置非阻塞)。
  3. 数据存在内核,进程退出消息不会消失;消息读完就从队列删除。
  4. 生命周期随内核,不调用msgctl(IPC_RMID),消息队列一直存在。
  5. 有消息大小、队列总字节数上限。

4. 使用流程

  1. msgget() 创建/获取消息队列id
  2. 发送方:构造消息结构体 → msgsnd()发送消息
  3. 接收方:msgrcv()按类型接收消息
  4. 不再使用:msgctl(IPC_RMID) 删除消息队列

三、共享内存 vs 消息队列对比

对比项 共享内存 消息队列
传输效率 极高,直接访问内存,无拷贝 相对低,需要内核拷贝消息
同步互斥 无自带同步,需要外部信号量/锁 自带阻塞同步机制
数据形式 一块连续内存,字节流 一条条带类型的消息块
数据生命周期 数据保留,需要手动覆盖清除 消息读取之后自动删除
适用场景 大数据量高速传输 离散消息传递,多类型消息区分

查看系统IPC命令:

复制代码
ipcs -m   #查看共享内存
ipcs -q   #查看消息队列
ipcrm -m shmid   #删除共享内存
ipcrm -q msqid   #删除消息队列
相关推荐
mounter62535 分钟前
突破零停机演进:Linux 内核 Live Update Orchestrator (LUO) 架构设计与热升级演进
linux·服务器·linux kernel·kernel·kho·luo
波多利希最高礼仪的握力器1 小时前
安全复制SCP
linux·服务器·安全
xiaoye-duck1 小时前
《Linux网络编程·加餐》进程间关系与守护进程
linux·网络
GeW1 小时前
RHCE高频考题精讲,精准突击快速拿证
linux
2401_862880822 小时前
Linux应用层开发 --- UDP&TCP
linux·运维·tcp/ip·udp
xie0510_3 小时前
TCP socket
linux·网络协议·tcp/ip
aningx3 小时前
# EasyTier 开机自启动部署教程
linux
DeeplyMind3 小时前
Linux 内核模块加载路径与 initramfs 生效机制分析
linux·initramfs·depmod·modprobe
Horn Still Sounds4 小时前
Linux网络编程|UDP与TCP传输层协议深度梳理
linux·网络·tcp/ip·udp