SystemV消息队列揭秘:原理与实战

目录

一、消息队列的基本原理

1、基本概念

2、基本原理

3、消息类型的关键作用

4、重要特性总结

5、生命周期管理

6、典型应用场景

二、System V 消息队列的内核数据结构

1、消息队列的管理结构 msqid_ds(消息队列标识符结构)

关键字段解析

2、权限控制结构 ipc_perm

权限控制字段解析

3、消息结构

4、消息队列与共享内存的对比

5、关键系统限制

6、总结

三、消息队列的创建:msgget() 函数

1、函数原型

2、参数解析

key 的生成方式

msgflg 的组成

1. 权限位(低 9 位)

2. 选项标志(高位)

3、返回值

4、典型使用场景

(1) 创建新队列

(2) 获取已有队列

(3) 创建独占队列(检测是否已存在)

5、与共享内存 (shmget) 的对比

6、注意事项

7、完整示例

输出示例

说明

验证

8、总结

四、 消息队列的释放与管理:msgctl() 函数

1、函数原型

2、参数解析

cmd 的常用选项

3、返回值

4、典型使用场景

(1) 删除消息队列(释放资源)

(2) 查询队列状态

(3) 修改队列属性

5、与共享内存 (shmctl) 的对比

6、注意事项

7、完整示例

分析: 

8、总结

五、 向消息队列发送数据:msgsnd() 函数

1、函数原型

2、参数解析

消息结构体 msgbuf

msgflg 的常用选项

3、返回值

4、使用示例

(1) 定义消息结构

(2) 发送消息(阻塞模式)

(3) 发送消息(非阻塞模式)

5、关键注意事项

6、完整代码示例

代码逻辑分析

7、总结

六、从消息队列获取数据:msgrcv() 函数详解

1、函数原型

2、参数解析

消息结构体 msgbuf

msgtyp 的接收规则

msgflg 的常用选项

3、返回值

4、使用示例

(1) 定义消息结构

(2) 接收特定类型的消息(阻塞模式)

(3) 接收任意消息(非阻塞模式)

(4) 处理长消息(自动截断)

5、关键注意事项

6、完整代码示例

关键点说明:

注意:

7、总结


一、消息队列的基本原理

        System V消息队列是Unix/Linux系统中一种进程间通信(IPC)机制,允许不相关的进程通过消息队列交换数据。

1、基本概念

System V消息队列是一种内核维护的消息链表,具有以下特点:

  • 消息队列由唯一的标识符(队列ID)标识

  • 消息是类型化的,每个消息都有一个长整型的类型字段

  • 消息可以按照类型顺序读取,而不一定是先进先出(后面会具体介绍)

  • 消息队列是持久的,会持续到系统重启或显式删除

2、基本原理

        消息队列是System V IPC机制中的一种进程间通信方式,其本质是在内核空间维护的一个链表结构。这个链表中的每个节点都是一个独立的数据块,每个数据块包含两个主要部分:

  • 消息类型:一个正整数,用于标识消息的类别

  • 消息内容:实际传输的数据

通信双方通过以下机制实现交互:

  1. 队列共享:不同进程通过唯一的key访问同一个消息队列

  2. 写入机制:发送方总是在队列尾部添加新的消息

  3. 读取机制接收方可以按照特定规则从队列中获取消息(不一定先进先出)

3、消息类型的关键作用

消息类型决定了消息的归属和读取方式:

  • 发送方标识:通过指定不同类型的值来区分消息的接收者

  • 选择性接收:接收方可以指定只接收特定类型的消息

  • 优先级控制:类型值也可以用于实现消息优先级(数值越小优先级越高)

4、重要特性总结

  1. 消息队列提供了一个从一个进程向另一个进程发送数据块的方法。
  2. 结构化通信:支持类型化数据块(每个数据块都被认为是有一个类型的,接收者进程接收的数据块可以有不同的类型值)的传输,比管道等字节流通信更结构化

  3. 异步通信:发送和接收进程不需要同时存在

  4. 多路复用:单个队列可支持多个进程间的多种消息交换

  5. 内核持久性:消息会一直保留在队列中直到被读取(和共享内存一样,消息队列的资源也必须自行删除,否则不会自动清除,因为system V IPC资源的生命周期是随内核的)

  6. 容量限制:受系统配置参数限制(如MSGMAX, MSGMNB等)

5、生命周期管理

与System V IPC的其他资源相同:

  • 显式删除:必须通过msgctl(..., IPC_RMID,...)主动删除

  • 查看命令:使用ipcs -q查看现有消息队列

  • 清理命令:可通过ipcrm -q删除指定的队列

6、典型应用场景

  • 生产者-消费者模型

  • 多进程事件通知

  • 跨进程的任务分发系统

  • 需要保证消息边界的通信场景

补充说明:

        虽然POSIX也定义了消息队列接口(mq_*系列函数),但System V消息队列在现有系统中仍广泛使用,两者在实现和特性上有显著差异。


二、System V 消息队列的内核数据结构

1、消息队列的管理结构 msqid_ds(消息队列标识符结构

        System V 消息队列在内核中通过 msqid_ds 结构体进行管理(内核为每个消息队列维护一个msqid_ds结构),该结构体存储了消息队列的所有关键信息,定义在 <linux/msg.h> 中:

struct msqid_ds {struct ipc_perm msg_perm;   // 权限控制结构struct msg *msg_first;      // 指向队列中的第一个消息(内核内部使用)struct msg *msg_last;       // 指向队列中的最后一个消息(内核内部使用)__kernel_time_t msg_stime;  // 最后一次调用 `msgsnd()` 的时间(发送时间)__kernel_time_t msg_rtime;  // 最后一次调用 `msgrcv()` 的时间(接收时间)__kernel_time_t msg_ctime;  // 最后一次修改队列的时间(如权限变更)unsigned long msg_lcbytes;  // 保留字段(32位兼容)unsigned long msg_lqbytes;  // 保留字段(32位兼容)unsigned short msg_cbytes;  // 当前队列中的总字节数unsigned short msg_qnum;    // 当前队列中的消息数量unsigned short msg_qbytes;  // 队列允许的最大字节数(`msg_qbytes ≤ MSGMNB`)__kernel_ipc_pid_t msg_lspid; // 最后一个执行 `msgsnd()` 的进程PID__kernel_ipc_pid_t msg_lrpid; // 最后一个执行 `msgrcv()` 的进程PID
};

关键字段解析

  • msg_perm:权限控制结构,决定哪些进程可以访问该消息队列。

  • msg_first / msg_last:内核内部使用,指向消息链表(用户层不可见)。

  • 时间戳msg_stime / msg_rtime / msg_ctime):记录消息队列的关键操作时间,可用于监控和调试。

  • msg_qnum 和 msg_cbytes

    • msg_qnum:当前队列中的消息数量。

    • msg_cbytes:当前队列占用的总字节数(所有消息大小之和)。

  • msg_qbytes:队列的最大容量(字节数),可通过 msgctl(IPC_SET) 调整,但不得超过系统限制(MSGMNB)。

  • msg_lspid / msg_lrpid:记录最后一个发送(msgsnd)和接收(msgrcv)消息的进程PID,便于调试。

2、权限控制结构 ipc_perm

        可以看到消息队列数据结构的第一个成员是msg_perm,它和shm_perm是同一个类型的结构体变量。ipc_perm 是 System V IPC 机制的通用权限控制结构,定义在 <linux/ipc.h> 中,用于管理消息队列、共享内存和信号量的访问权限:

struct ipc_perm {__kernel_key_t key;    // 创建 IPC 资源时指定的 key(`ftok()` 生成)__kernel_uid_t uid;    // 所有者的用户ID__kernel_gid_t gid;    // 所有者的组ID__kernel_uid_t cuid;   // 创建者的用户ID__kernel_gid_t cgid;   // 创建者的组ID__kernel_mode_t mode;  // 访问权限(如 0644)unsigned short seq;    // 序列号(内核用于管理 IPC 标识符)
};

权限控制字段解析

  • key:用于生成 IPC 标识符(msgget() 的第一个参数),通常由 ftok() 生成。

  • uid / gid:资源当前的所有者(可通过 msgctl(IPC_SET) 修改)。

  • cuid / cgid:资源的创建者(不可修改)。

  • mode

    • 访问权限位,格式类似文件权限(如 0600 表示仅所有者可读写)。

    • 可通过 msgctl(IPC_SET) 修改。

3、消息结构

发送和接收消息时使用的结构:

struct msgbuf {long mtype;     // 消息类型,必须>0char mtext[1];  // 消息数据(实际长度可变)
};

4、消息队列与共享内存的对比

特性消息队列 (msqid_ds)共享内存 (shmid_ds)
数据结构链表结构,存储消息块线性内存区域
访问方式类型化消息(msgrcv 按类型读取)直接内存读写
同步需求自带消息边界,无需额外同步通常需要信号量同步
内核持久性消息保留直到被读取内存段持久,直到显式删除
适用场景结构化通信、异步通知大数据量、高性能共享

5、关键系统限制

  • MSGMAX:单个消息的最大长度(字节),可通过 sysctl kernel.msgmax 查看。

  • MSGMNB:单个消息队列的最大容量(字节),可通过 sysctl kernel.msgmnb 查看。

  • MSGMNI:系统范围内最大消息队列数量,可通过 sysctl kernel.msgmni 查看。

6、总结

  • System V 消息队列通过 msqid_ds 结构管理,包含队列状态、时间戳、权限等信息。

  • ipc_perm 控制访问权限,确保安全性。

  • 消息队列适用于结构化、异步的进程间通信,但需注意手动释放资源(msgctl(IPC_RMID))。

  • 与共享内存相比,消息队列自带消息边界,但性能较低,适用于中小数据量通信。


三、消息队列的创建:msgget() 函数

1、函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgget(key_t key, int msgflg);

2、参数解析

参数类型说明
keykey_t消息队列的唯一键值,通常由 ftok() 生成,或使用 IPC_PRIVATE 创建私有队列。
msgflgint控制队列创建的标志位,由权限位(如 0644)和选项标志(如 IPC_CREAT)按位或(|)组合。

key 的生成方式

  • ftok() 生成(推荐)
    通过文件路径和项目ID生成唯一键值:

    key_t key = ftok("/path/to/file", 'A');  // 'A' 是项目ID(1~255)
  • IPC_PRIVATE

    创建一个仅当前进程可访问的私有队列(通常用于父子进程间通信):

    key_t key = IPC_PRIVATE;

msgflg 的组成

        在 System V IPC(如消息队列、共享内存、信号量)中,msgflg 参数是一个整型(int),它由 权限位(低 9 位) 和 选项标志(高位) 组合而成,通过 按位或(| 运算来设置。

1. 权限位(低 9 位)
  • 作用:控制消息队列的访问权限(类似 Linux 文件权限)。

  • 格式:一个 9 位 的八进制数,结构如下:

    位组含义示例
    0700所有者(User)权限rwx(读/写/执行)
    0070组(Group)权限rw-(读/写)
    0007其他用户(Others)权限r--(只读)

    示例

    • 0644 → 所有者可读可写(6 = 110),组和其他用户只读(4 = 100)。

    • 0600 → 仅所有者可读可写,其他用户无权限。

2. 选项标志(高位)
标志作用示例
IPC_CREAT如果队列不存在,则创建;否则直接返回现有队列标识符。IPC_CREAT | 0644
IPC_EXCL与 IPC_CREAT 联用,若队列已存在则返回错误(EEXIST)。IPC_CREAT | IPC_EXCL | 0644
IPC_NOWAIT非阻塞模式,若队列满/空时立即返回错误(ENOMSG)。IPC_CREAT | IPC_NOWAIT | 0644

3、返回值

返回值说明
成功返回消息队列的标识符(非负整数)(一个有效的消息队列标识符(用户层标识符),类似共享内存的shmid),用于后续操作(如 msgsnd/msgrcv)。
失败返回 -1,并设置 errno(如 EACCES 权限不足、ENOENT 队列不存在)。

4、典型使用场景

(1) 创建新队列

key_t key = ftok("/tmp/msgqueue", 65);
int msqid = msgget(key, IPC_CREAT | 0666);
if (msqid == -1) 
{perror("msgget failed");exit(EXIT_FAILURE);
}

(2) 获取已有队列

int msqid = msgget(key, 0666);  // 不指定 IPC_CREAT,若队列不存在则失败

(3) 创建独占队列(检测是否已存在)

int msqid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
if (msqid == -1 && errno == EEXIST) 
{printf("Message queue already exists.\n");
}

5、与共享内存 (shmget) 的对比

特性消息队列 (msgget)共享内存 (shmget)
键值生成同样使用 ftok() 或 IPC_PRIVATE相同
权限控制通过 msgflg 的低9位设置相同
创建标志IPC_CREAT/IPC_EXCL相同
资源类型消息链表结构线性内存段

6、注意事项

  1. 键值冲突:不同进程使用相同的 key 会访问同一队列,需确保 ftok() 的参数唯一。

  2. 权限管理:若权限不足(如 msgflg=0600),其他进程无法访问队列。

  3. 资源泄漏:消息队列不会自动释放,需通过 msgctl(msqid, IPC_RMID, NULL) 手动删除。

  4. 系统限制:队列数量受 MSGMNI 限制,可通过 sysctl kernel.msgmni 查看。

7、完整示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>int main() 
{key_t key = ftok("/tmp/msgqueue", 65);if (key == -1) {perror("ftok failed");exit(EXIT_FAILURE);}int msqid = msgget(key, IPC_CREAT | 0666);if (msqid == -1) {perror("msgget failed");exit(EXIT_FAILURE);}printf("Message queue created with ID: %d\n", msqid);return 0;
}

输出示例

Message queue created with ID: 0
(实际 msqid 由内核动态分配)

说明

  • ftok() 成功

    • 文件 /home/hmz/learn4/demo1.c 存在且可读。

    • key 计算成功(基于文件的 inode 和 proj_id=65)。

  • msgget() 成功

    • 如果消息队列 不存在,则创建新队列,返回其标识符 msqid

    • 如果队列 已存在,则直接返回其 msqid(不会报错,因为未使用 IPC_EXCL)。

验证

运行 ipcs -q 可查看新创建的消息队列:(key 由 ftok() 生成,msqid 由内核分配)

ipcs -q

8、总结

  • msgget() 是创建/访问消息队列的入口函数,依赖 key 和权限标志。

  • 需合理管理队列生命周期,避免资源泄漏。

  • 适用于需要结构化通信的场景(如生产者-消费者模型)。


四、 消息队列的释放与管理:msgctl() 函数

1、函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgctl(int msqid, int cmd, struct msqid_ds *buf);

2、参数解析

参数类型说明
msqidint消息队列标识符,由 msgget() 返回。
cmdint控制命令,决定该操作的类型(如删除、查询或设置队列属性)。
bufstruct msqid_ds*用于存储或修改队列状态信息的缓冲区(部分命令需传入,部分命令可设为 NULL)。

说明一下:
        msgctl函数的参数与释放共享内存时使用的shmctl函数的三个参数相同,只不过msgctl函数的第三个参数传入的是消息队列的相关数据结构。 

cmd 的常用选项

命令作用
IPC_RMID立即删除消息队列,并唤醒所有阻塞的读写进程(errno 设为 EIDRM)。buf 参数可设为 NULL
IPC_STAT获取队列的当前状态信息(如权限、消息数量等),存储到 buf 指向的 msqid_ds 结构体中。
IPC_SET修改队列属性(如权限 msg_perm.mode 或最大容量 msg_qbytes),需通过 buf 传入新值。

3、返回值

返回值说明
成功返回 0
失败返回 -1,并设置 errno(如 EINVAL 无效队列ID、EPERM 权限不足)。

4、典型使用场景

(1) 删除消息队列(释放资源)

if (msgctl(msqid, IPC_RMID, NULL) == -1) 
{perror("msgctl(IPC_RMID) failed");exit(EXIT_FAILURE);
}
printf("Message queue %d deleted.\n", msqid);

(2) 查询队列状态

struct msqid_ds info;
if (msgctl(msqid, IPC_STAT, &info) == -1) 
{perror("msgctl(IPC_STAT) failed");exit(EXIT_FAILURE);
}
printf("Queue stats: %d messages, %d bytes max.\n", info.msg_qnum, info.msg_qbytes);

(3) 修改队列属性

struct msqid_ds info;
msgctl(msqid, IPC_STAT, &info);  // 先获取当前状态
info.msg_perm.mode = 0600;       // 修改权限为仅所有者可读写
info.msg_qbytes = 8192;          // 调整队列最大容量为8KB
if (msgctl(msqid, IPC_SET, &info) == -1) 
{perror("msgctl(IPC_SET) failed");
}

5、与共享内存 (shmctl) 的对比

特性消息队列 (msgctl)共享内存 (shmctl)
删除命令IPC_RMID相同
查询命令IPC_STAT(返回 msqid_ds 结构)IPC_STAT(返回 shmid_ds 结构)
设置命令IPC_SET(修改权限或容量)相同
关键差异管理消息链表结构管理内存段属性

6、注意事项

  1. 权限要求

    • 删除队列(IPC_RMID)需具备所有者或超级用户权限。

    • 修改属性(IPC_SET)需与原权限匹配。

  2. 资源释放时机:即使所有进程退出,消息队列仍会保留,必须显式调用 IPC_RMID 删除。

  3. 阻塞进程的处理:删除队列会唤醒所有阻塞在 msgsnd/msgrcv 的进程,并使其返回错误(EIDRM)。

  4. 内核限制:队列删除后,其标识符可能被后续新建的队列复用,需避免误操作。

7、完整示例

#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#define PATHNAME "/home/hmz/learn4/demo2.c"int main() 
{key_t key = ftok(PATHNAME, 65);int msqid = msgget(key, IPC_CREAT | 0666);if (msqid == -1) {perror("msgget failed");exit(EXIT_FAILURE);}// 删除队列if (msgctl(msqid, IPC_RMID, NULL) == -1)     {perror("msgctl(IPC_RMID) failed");exit(EXIT_FAILURE);}printf("Message queue %d released.\n", msqid);return 0;
}

分析: 

  1. ftok()函数

    • 使用给定的路径名/home/hmz/learn4/demo2.c和项目ID65生成一个唯一的键值(key)。

    • 如果文件存在且可访问,ftok()会成功返回一个key_t类型的键;否则返回-1。

  2. msgget()函数

    • 使用生成的key创建一个新的消息队列或访问已存在的队列。

    • IPC_CREAT | 0666表示如果队列不存在则创建,并设置权限为0666(所有用户可读写)。

    • 成功时返回消息队列标识符(msqid),失败时返回-1。

  3. msgctl()函数

    • 使用IPC_RMID命令删除消息队列。

    • 成功时返回0,失败时返回-1。

  4. 可能的运行结果

    • 如果所有操作都成功,程序将输出:Message queue [队列ID] released.

      其中[队列ID]是系统分配的消息队列标识符。

    • 可能的错误情况:
      a) 如果ftok()失败(如路径不存在),会输出错误并退出
      b) 如果msgget()失败(如权限不足),会输出"msgget failed"并退出
      c) 如果msgctl()失败,会输出"msgctl(IPC_RMID) failed"并退出

  5. 程序特点

    • 这是一个"创建后立即删除"的示例程序,实际使用中通常不会这样操作。

    • 程序没有发送或接收任何实际消息,只是演示了消息队列的创建和删除。

    • 退出时使用了EXIT_FAILURE宏(值为1)表示失败退出。

注意:每次运行程序时,系统可能会分配不同的消息队列标识符,所以输出的数字可能会变化。

8、总结

  • msgctl() 是管理消息队列的核心函数,支持删除、查询和配置操作。

  • IPC_RMID 是释放资源的唯一方式,需主动调用以避免内存泄漏。

  • 通过 IPC_STAT/IPC_SET 可实现精细化的队列监控与权限控制。

  • 与共享内存的管理接口高度相似,但操作对象为消息链表而非内存段。


五、 向消息队列发送数据:msgsnd() 函数

1、函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

2、参数解析

参数类型说明
msqidint消息队列标识符,由 msgget() 返回。
msgpconst void*指向消息缓冲区的指针,必须符合 msgbuf 结构。
msgszsize_t消息数据的实际长度(字节数),不包括 mtype 字段
msgflgint发送标志位,控制发送行为(如非阻塞模式)。
  • 第一个参数msqid,表示消息队列的用户级标识符。
  • 第二个参数msgp,表示待发送的数据块。
  • 第三个参数msgsz,表示所发送数据块的大小
  • 第四个参数msgflg,表示发送数据块的方式,一般默认为0即可。

消息结构体 msgbuf

struct msgbuf {long mtype;      // 消息类型(必须 > 0)char mtext[1];   // 消息数据(柔性数组,实际长度由用户定义)
};
  • mtype:消息类型,接收方可通过该字段筛选消息(值必须为正整数)。

  • mtext:实际消息数据,定义时可扩展为任意长度(如 char mtext[100])。

msgflg 的常用选项

标志作用
0默认阻塞模式,若队列已满则阻塞,直到空间可用。
IPC_NOWAIT非阻塞模式,若队列满则立即返回 -1,并设置 errno 为 EAGAIN

3、返回值

返回值说明
成功返回 0,消息被添加到队列尾部。
失败返回 -1,并设置 errno(如 EINVAL 无效参数、EIDRM 队列被删除、EACCES 权限不足)。

4、使用示例

(1) 定义消息结构

#define MAX_MSG_SIZE 1024struct my_msg {long mtype;char mtext[MAX_MSG_SIZE];
};

(2) 发送消息(阻塞模式)

struct my_msg msg;
msg.mtype = 1;  // 设置消息类型
strcpy(msg.mtext, "Hello, Message Queue!");if (msgsnd(msqid, &msg, strlen(msg.mtext) + 1, 0) == -1) {perror("msgsnd failed");exit(EXIT_FAILURE);
}

(3) 发送消息(非阻塞模式)

if (msgsnd(msqid, &msg, strlen(msg.mtext) + 1, IPC_NOWAIT) == -1) {if (errno == EAGAIN) {printf("Queue is full. Try later.\n");} else {perror("msgsnd failed");}
}

5、关键注意事项

  1. 消息大小限制

    • 单条消息的长度(msgsz)不能超过系统限制 MSGMAX(可通过 cat /proc/sys/kernel/msgmax 查看)。

    • 队列总容量受 MSGMNB 限制(所有消息的 msgsz 之和 ≤ msg_qbytes)。

  2. 内存对齐问题:msgbuf 结构中的 mtext 字段建议定义为柔性数组(C99)或足够大的静态数组,避免内存越界。

  3. 消息类型必须为正数:若 mtype ≤ 0msgsnd() 会返回 EINVAL 错误。

  4. 资源竞争处理:多进程同时发送消息时,需通过额外同步机制(如信号量)避免数据混乱。

6、完整代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
#include <unistd.h>#define MAX_MSG_SIZE 1024
#define PATHNAME "/home/hmz/learn4/demo4.c"struct my_msg
{long mtype;char mtext[MAX_MSG_SIZE];
};int main()
{key_t key = ftok(PATHNAME, 65);int msqid = msgget(key, IPC_CREAT | 0666);  // 队列不存在时自动创建if (msqid == -1){perror("msgget failed");exit(EXIT_FAILURE);}struct my_msg msg;msg.mtype = 1; // 消息类型snprintf(msg.mtext, MAX_MSG_SIZE, "PID %d: Hello, World!", getpid());if (msgsnd(msqid, &msg, strlen(msg.mtext) + 1, 0) == -1){perror("msgsnd failed");exit(EXIT_FAILURE);}printf("Message sent to queue %d.\n", msqid);return 0;
}

代码逻辑分析

  1. 生成键值 (ftok)

    • 使用文件路径 /home/hmz/learn4/demo4.c 和项目 ID 65 生成唯一的 key

    • 如果文件不存在或不可访问,ftok 会失败,返回 -1

  2. 创建/获取消息队列 (msgget)

    • IPC_CREAT | 0666 表示:

      • 如果队列不存在,则创建新队列;

      • 权限设置为 0666(所有用户可读写)。

    • 如果失败(如权限不足),返回 -1

  3. 构造并发送消息 (msgsnd)

    • 消息类型 mtype = 1(可用于接收端筛选消息)。

    • 消息内容为字符串 "PID [进程ID]: Hello, World!"(例如 "PID 1234: Hello, World!")。

    • strlen(msg.mtext) + 1 表示发送消息长度(包括终止符 \0)。

    • 如果队列已满或权限不足,msgsnd 会失败。

7、总结

  • msgsnd() 用于向消息队列发送结构化数据,需通过 msgbuf 指定类型和内容。

  • 消息长度需明确指定(不包括 mtype),且不能超过系统限制。

  • 支持阻塞和非阻塞模式,适用于不同实时性要求的场景。

  • 务必检查返回值,确保消息成功入队。


六、从消息队列获取数据:msgrcv() 函数详解

1、函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

2、参数解析

参数类型说明
msqidint消息队列标识符,由 msgget() 返回。
msgpvoid*输出型参数,指向接收消息的缓冲区,需符合 msgbuf 结构(见下文)。
msgszsize_t缓冲区 mtext 的最大容量(字节数),不包括 mtype 字段
msgtyplong指定接收的消息类型,决定消息筛选规则(见下文)。
msgflgint接收标志位,控制接收行为(如非阻塞模式、截断长消息等)。

消息结构体 msgbuf

struct msgbuf {long mtype;      // 消息类型(由发送方指定)char mtext[1];   // 消息数据(柔性数组,实际长度由用户定义)
};

msgtyp 的接收规则

msgtyp 值接收行为
> 0读取队列中 第一条类型等于 msgtyp 的消息。
= 0读取队列中的 第一条消息(无论类型)。
< 0读取队列中 类型值 ≤ |msgtyp| 的最小类型的消息(如 -3 接收类型为 12 或 3 的消息)。

msgflg 的常用选项

标志作用
IPC_NOWAIT非阻塞模式,若队列无匹配消息则立即返回 -1,并设置 errno 为 ENOMSG
MSG_NOERROR若消息实际长度 > msgsz,则截断消息(不返回错误);未设置时,长消息会触发 E2BIG 错误。
MSG_EXCEPT当 msgtyp > 0 时,接收 类型不等于 msgtyp 的第一条消息(Linux 特有扩展)。

3、返回值

返回值说明
成功返回实际接收到的 mtext 的字节数(不包括 mtype)。
失败返回 -1,并设置 errno(如 EINVAL 无效参数、EIDRM 队列被删除、EACCES 权限不足)。

4、使用示例

(1) 定义消息结构

#define MAX_MSG_SIZE 1024struct my_msg {long mtype;char mtext[MAX_MSG_SIZE];
};

(2) 接收特定类型的消息(阻塞模式)

struct my_msg msg;
ssize_t bytes = msgrcv(msqid, &msg, MAX_MSG_SIZE, 1, 0);  // 接收类型为1的消息
if (bytes == -1) {perror("msgrcv failed");exit(EXIT_FAILURE);
}
printf("Received: %s\n", msg.mtext);

(3) 接收任意消息(非阻塞模式)

ssize_t bytes = msgrcv(msqid, &msg, MAX_MSG_SIZE, 0, IPC_NOWAIT);
if (bytes == -1) {if (errno == ENOMSG) {printf("No message available.\n");} else {perror("msgrcv failed");}
}

(4) 处理长消息(自动截断)

ssize_t bytes = msgrcv(msqid, &msg, 100, 0, MSG_NOERROR);  // 最多接收100字节
if (bytes == -1) {perror("msgrcv failed");
} else if (bytes == 100) {printf("Message truncated to 100 bytes.\n");
}

5、关键注意事项

  1. 缓冲区大小

    • msgsz 必须 ≥ 消息的实际长度(除非使用 MSG_NOERROR)。

    • 建议将 mtext 定义为足够大的数组(如 char mtext[4096])。

  2. 消息类型筛选

    • 使用 msgtyp 可实现优先级消息处理(如类型值越小优先级越高)。

    • msgtyp < 0 时,内核会遍历队列寻找匹配的最小类型消息。

  3. 阻塞与非阻塞

    • 默认阻塞模式下,若队列无匹配消息,进程会休眠等待。

    • IPC_NOWAIT 适用于需要轮询的场景。

  4. 队列删除处理:若队列在阻塞期间被删除(IPC_RMID),msgrcv() 会立即返回 -1,并设置 errno 为 EIDRM

6、完整代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/msg.h>
#include <unistd.h>#define MAX_MSG_SIZE 1024
#define PATHNAME "/home/hmz/learn4/demo4.c"struct my_msg
{long mtype;char mtext[MAX_MSG_SIZE];
};int main()
{key_t key = ftok(PATHNAME, 65);int msqid = msgget(key, 0666);if (msqid == -1){perror("msgget failed");exit(EXIT_FAILURE);}struct my_msg msg;if (msgrcv(msqid, &msg, MAX_MSG_SIZE, 1, 0) == -1) // 接收类型为1的消息{perror("msgrcv failed");exit(EXIT_FAILURE);}printf("Received message: %s\n", msg.mtext);// 可选:删除消息队列// if (msgctl(msqid, IPC_RMID, NULL) == -1) {//     perror("msgctl failed");//     exit(EXIT_FAILURE);// }return 0;
}

        上面第五点的发送代码,消息队列中已经有了,下面是一个对应的接收端代码。这个代码会从消息队列中读取发送的消息:

关键点说明:

  1. 使用相同的PATHNAME和项目ID(65)生成相同的key

  2. 使用msgget获取相同的消息队列ID(不需要IPC_CREAT标志)

  3. 使用msgrcv接收类型为1的消息(与发送端设置的mtype匹配)

  4. 接收的消息会存储在msg结构体中,可以通过msg.mtext访问消息内容

注意:

  • 接收端运行时需要确保消息队列已存在(即发送端已运行过)

  • 如果要删除消息队列,可以取消注释最后的msgctl代码

  • 如果发送端和接收端在不同的终端运行,确保工作目录相同或PATHNAME路径正确

7、总结

  • msgrcv() 是消息队列的核心接收函数,支持按类型筛选和多种接收模式。

  • 通过 msgtyp 和 msgflg 可灵活控制消息选择策略(如优先级、非阻塞等)。

  • 务必检查返回值和错误码,确保消息完整性和程序健壮性。

  • 与 msgsnd() 配合使用,可实现进程间结构化通信(如任务分发、事件通知等)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/916578.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/916578.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

5 分钟上手 Firecrawl

文章目录Firecrawl 是什么&#xff1f;本地部署验证mcp安装palyground&#x1f525; 5 分钟上手 FirecrawlFirecrawl 是什么&#xff1f; 一句话&#xff1a; 开源版的 “最强网页爬虫 清洗引擎” • 自动把任意网页 → 结构化 Markdown / JSON • 支持递归整站抓取、JS 渲染…

算法训练营day31 贪心算法⑤56. 合并区间、738.单调递增的数字 、968.监控二叉树

贪心算法的最后一篇博客&#xff01;前面两道题都是比较简单的思路&#xff0c;重点理解一下最后一道题即可。有一说一&#xff0c;进入到贪心算法这一章节之后&#xff0c;我的博客里和代码注释里的内容明显少了很多&#xff0c;因为很多贪心的题目我觉得不需要很复杂的文字说…

Jenkins流水线部署+webhook2.0

文章目录1. 环境2. 用到的插件3. 流水线部署脚本1. 环境 Centos7Jenkins2.5.0JDKopen17阿里云仓库 注意&#xff1a;这个版本兼容需要特别注意&#xff0c;要不然会很麻烦 2. 用到的插件 Generic Webhook Trigger 3. 流水线部署脚本 兼容钩子部署&#xff08;webhook&…

IDM下载失败排查

网络连接问题排查检查网络连接是否稳定&#xff0c;确保能够正常访问互联网 测试其他下载工具或浏览器是否能够正常下载 尝试关闭防火墙或杀毒软件&#xff0c;排除安全软件拦截的可能性代理和VPN设置检查确认IDM的代理设置是否正确&#xff0c;是否与系统代理一致 检查是否使用…

Anaconda安装时的几个操作

一、安装Anaconda 其实Anaconda的安装比较简单&#xff0c;点击next就好了。在安装中需要注意以下两点&#xff1a; 1、选择安装路径 在安装时&#xff0c;路径最好选择非C盘&#xff0c;且路径中不要出现中文&#xff0c;以免后期运行代码时出现不必要的错误。 我安装时&…

网易易盾、腾讯ACE等主流10款游戏反外挂系统对比

本文将深入对比10款游戏反外挂系统&#xff1a;1.网易易盾&#xff1b;2.Ricochet Anti‑Cheat&#xff1b;3.BattlEye&#xff1b;4.几维安全手游智能反外挂系统&#xff1b;5.伏魔AI反外挂&#xff1b;6.Riot Vanguard&#xff1b;7.Xigncode3&#xff1b;8.盛大GPK&#xff…

wpa_supplicant-2.10交叉编译

参考文章:https://blog.csdn.net/weixin_45783574/article/details/145810790 1、Openssl交叉编译 1.1 下载openssl-1.1.1t.tar.gz 下载网址: https://openssl-library.org/source/old/1.1.1/index.html1.2 编译 sudo tar xvf openssl-1.1.1t.tar.gz cd openssl-1.1

源码解读SpringCloudAlibaba Nacos2.x

Nacos 服务注册 Nacos 服务注册时&#xff0c;客户端会将自己的信息注册到Nicosserver上&#xff0c;形成key-value组合&#xff0c;其中key通常是服务名称&#xff0c;value是实例地址信息。在二点X版本中&#xff0c;客户端通过Spring Boot的扩展机制(例如web_initialized事件…

Windows 11 下 Anaconda 命令修复指南及常见问题解决

Windows 11 下 Anaconda 命令修复指南及常见问题解决 在使用 Anaconda 过程中&#xff0c;可能会遇到环境损坏、更新失败、包依赖冲突等问题。本文整理了一套通过命令行修复 Anaconda 的完整方案&#xff0c;适用于 Windows 11 系统&#xff0c;同时补充了权威参考链接供深入学…

安宝特案例丨全球连线!安宝特Vuzix与RodsCones共筑实时手术教育平台

安宝特Vuzix与合作伙伴Rods&Cones协作&#xff0c;为Rocamed在布拉格UROSANIT诊所举办的创新型实时手术直播研讨会提供技术赋能。 本次直播通过合作伙伴Rods&Cones软件平台搭载安宝特Vuzix智能眼镜&#xff0c;成功连接来自9国、3大洲、6个时区的27位医生&#xff0c;…

【Spring Boot 快速开发】一、入门

目录Spring Boot 简介Web 入门Spring Boot 快速入门HTTP 协议概述请求协议响应协议解析协议TomcatSpring Boot 简介 Spring Boot 是由 Pivotal 团队&#xff08;后被 VMware 收购&#xff09;开发的基于 Spring 框架的开源项目&#xff0c;于 2014 年首次发布。其核心目标是简…

laravel chunkById导出数据乱序问题

2025年7月28日17:47:29 这几天在做数据导出优化&#xff0c;使用xlswriter作为导出组件&#xff0c;但是发现在 使用 $base->chunkById(2000, function ($list) use ($writer, $sheet1) { 发现导出的数据是乱的&#xff0c;偶尔有些重复&#xff0c;偶尔有些少了&#xff0c…

Spring IOC与DI

spring的两大思想:IOC与AOP一、ioc的概念什么叫控制翻转?之前:对象的使用方,创建对象,对象的控制权,在对象的使用方手中.spring:对象的控制权交给了spring.举个例子:智能驾驶,之前车的使用权在人手中,而现在在ai手中,这就是控制反转.什么叫ioc:之前车企生产车需要做整个车,费事…

【图像处理基石】Segment Anything Model (SAM) 调研

Segment Anything Model (SAM) 是由 Meta AI 开发的革命性图像分割模型,它能够对图像中的任何物体进行分割,无需针对特定类别进行训练。SAM 具有以下特点: 通用性:可以分割任何视觉对象,无论是否见过该类别 灵活性:支持多种输入提示(点、框、掩码或文本) 实时性:在普通…

unisS5800XP-G交换机配置命令之端口篇

一、批量配置端口(1) 进入系统视图。system-view(2) 指定接口范围&#xff0c;并进入接口批量配置视图。¡ 指定一个不带别名的接口列表。interface range { interface-type interface-number [ to interface-type interface-number ] } &<1-24>¡…

MySQL中的 redolog

什么是redo log如果我们只在内存的 Bufer Pool中修改了页面&#xff0c;假设在事务提交后突然发生了某个故障导致内存中的数据都失效了&#xff0c;那么这个已经提交的事务在数据库中所做的更改也就跟着丢失了&#xff0c;这是我们所不能忍受的。那么&#xff0c;如何保证这个持…

数据结构之 【排序】(非递归实现快速排序)

目录 1.引入 2.非递归实现快排的思想 3.非递归实现快排图解 4.完整代码 1.引入 递归不可避免的话题就是防止栈溢出 所以程序员需要具备递归改非递归的能力 &#xff0c;一般来说&#xff0c;抓住递归中变化的量是关键 void QuickSort(int* a, int left, int right){if (left…

CLAP文本-音频基础模型: LEARNING AUDIO CONCEPTS FROM NATURAL LANGUAGE SUPERVISION

一、TL&#xff1b;DR 现在的做法有什么问题&#xff1f;主流范式是 “一个类别标签对应多个录音”&#xff0c;需要提前标注预测预先定义的类别&#xff0c;只能做闭集理解&#xff0c;失去灵活性 我们怎么做&#xff1f;通过两个编码器和对比学习机制建立语言与音频的关联&a…

Flink2.0学习笔记:Stream API 常用转换算子

EC0720/FLINKTASK-TEST-STREAM/demo at master stevensu1/EC0720 先看测试效果&#xff1a;控制台 测试效果&#xff1a;监控服务端 主要的转换算子包括&#xff1a; 转换算子 filter:过滤包含“Flink”的输入 转换算子 map: 将每行数据前添加“Processed: ”并转为大写 转…

一、Python环境、Jupyter与Pycharm

安装Python由于RAG项目中所需要的Python版本必须高于3.8&#xff0c;经过筛选&#xff0c;最终选择了3.10.11这个版本py --version Python 3.10.11安装过程略过&#xff0c;但对于几个基础的命令作个笔记记录where python找到python启动器的位置D:\>where python C:\Users\x…