System V 消息队列总结
System V 消息队列总结:
前言
消息队列是一种面向消息的ipc机制,即内核会保证进程对消息队列IO时数据块(消息)的整体性。且其是双工的,任何进程都可以往里放消息,同时任何进程都可以往里读消息(甚至读到其自己的)。
1. msgget
- 创建消息队列/获取已有消息队列的标识符
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
• 参数:
• key
:消息队列的唯一键值(通常由 ftok
生成),或使用 IPC_PRIVATE
创建私有队列。
• msgflg
:标志位(如 IPC_CREAT
创建队列,IPC_EXCL
配合 IPC_CREAT
确保队列不存在时新建)。
• 返回值:成功返回消息队列标识符(msqid
),失败返回 -1
。
2. msgsnd
- 发送消息
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
• 参数:
• msqid
:消息队列标识符。
• msgp
:指向消息结构体的指针(需自定义,见下文)。
• msgsz
:消息内容(mtext
)的字节数(不包括 mtype
字段)。
• msgflg
:标志位(如 IPC_NOWAIT
非阻塞发送,队列满时立即返回错误)。
• 返回值:成功返回 0
,失败返回 -1
。
注意:就算多进程/线程同时非阻塞地调用该函数,内核依旧会保证消息之间地独立性。因为内核并不是依靠其阻塞来保证独立性的。
3. msgrcv
- 接收消息
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
• 参数:
• msqid
:消息队列标识符。
• msgp
:接收消息的缓冲区指针(需自定义消息结构体)。
• msgsz
:缓冲区中 mtext
部分的最大容量。
• msgtyp
:指定接收消息的类型(见下文规则)。
• msgflg
:标志位(如 IPC_NOWAIT
非阻塞接收,MSG_NOERROR
允许截断过长消息)。
• 返回值:成功返回实际接收的 mtext
字节数,失败返回 -1
。
4. msgctl
- 控制消息队列
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
• 参数:
• msqid
:消息队列标识符。
• cmd
:控制命令(常用值):
◦ **`IPC_STAT`**:获取队列元数据到 `buf`。
◦ **`IPC_SET`**:通过 `buf` 修改队列元数据(如权限、容量限制)。
◦ **`IPC_RMID`**:立即删除队列(`buf` 可设为 `NULL`)。
• buf
:指向 struct msqid_ds
的指针(用于读写元数据)。
• 返回值:成功返回 0
,失败返回 -1
。
关键数据结构
消息结构体(需自定义)
struct msgbuf {
long mtype; // 消息类型(必须 > 0)
char mtext[]; // 消息内容(实际长度由 `msgsz` 指定,这里的字符数组表示就是一块内存,可以是各种数据的组合)
};
队列元数据结构体(struct msqid_ds
)
提取自内核维护的msg_ids数据结构的对应entries指针指向的结构。
struct msqid_ds {
struct ipc_perm msg_perm; // 权限信息(所有者、读写权限等)
time_t msg_stime; // 最后发送时间
time_t msg_rtime; // 最后接收时间
time_t msg_ctime; // 最后修改时间
unsigned long msg_cbytes; // 当前队列中的字节数
msgqnum_t msg_qnum; // 当前队列中的消息数
msglen_t msg_qbytes; // 队列最大容量(字节数)
pid_t msg_lspid; // 最后发送消息的进程PID
pid_t msg_lrpid; // 最后接收消息的进程PID
};
核心原理
- 消息类型(
mtype
):
• 用于分类消息,接收时可指定类型过滤(如msgtyp > 0
匹配特定类型,msgtyp = 0
接收任意类型,msgtyp < 0
接受mtype小于msgtyp绝对值的mtype最小的消息)。
• 类型为long
类型,必须为正整数。 - 内核管理:
• 消息队列以内核对象形式存在,独立于进程生命周期(需显式调用msgctl(IPC_RMID)
删除)。
• 消息以链表形式存储,保证先进先出(FIFO),但可通过msgtyp
实现优先级读取。 - 同步与原子性:
•msgsnd
和msgrcv
是原子操作:单次调用发送或接收一条完整消息。
• 若队列满或空,默认阻塞进程(除非指定IPC_NOWAIT
)。
示例代码片段
// 定义消息结构体(实际使用需动态分配 mtext 大小)
struct my_msg {
long mtype;
char mtext[100];
};
// 发送消息
struct my_msg msg_send = {1, "Hello"};
msgsnd(msqid, &msg_send, strlen(msg_send.mtext), 0);
// 接收消息
struct my_msg msg_recv;
msgrcv(msqid, &msg_recv, sizeof(msg_recv.mtext), 1, 0);
总结
• 适用场景:需要类型过滤、原子性操作或异步通信的进程间交互(如任务分发、事件通知)。
• 缺点:依赖内核资源,需手动管理队列生命周期;跨平台支持较弱(推荐优先考虑 POSIX 消息队列或套接字)。