多进程/多线程并发编程,C/C++Unix/Linux应用层编程基本原理

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
};

核心原理

  1. 消息类型(mtype
    • 用于分类消息,接收时可指定类型过滤(如 msgtyp > 0 匹配特定类型,msgtyp = 0 接收任意类型,msgtyp < 0 接受mtype小于msgtyp绝对值的mtype最小的消息)。
    • 类型为 long 类型,必须为正整数。
  2. 内核管理
    • 消息队列以内核对象形式存在,独立于进程生命周期(需显式调用 msgctl(IPC_RMID) 删除)。
    • 消息以链表形式存储,保证先进先出(FIFO),但可通过 msgtyp 实现优先级读取。
  3. 同步与原子性
    msgsndmsgrcv 是原子操作:单次调用发送或接收一条完整消息。
    • 若队列满或空,默认阻塞进程(除非指定 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 消息队列或套接字)。

回复

This is just a placeholder img.