Linux 消息队列的使用方法

文章目录

  • 1.概念
  • 2. 创建消息队列
  • 3. 发送消息
  • 4. 接收消息
  • 5. 消息结构体
  • 6. 消息队列控制(删除、获取队列状态)
  • 消息队列是否存在
  • 7. 使用场景
  • 8. 注意事项
  • 使用例子
  • 判断消息队列是否存在的代码
  • 获取队列空间大小

1.概念

  1. 消息队列是一种进程间通信 (IPC) 机制,允许进程之间交换数据。
  2. 每个消息队列都有一个唯一的标识符 (msqid),通过该标识符来访问消息队列。
  3. 消息队列可以存储多个消息,每个消息包含消息类型和消息内容。

2. 创建消息队列

  1. 使用 msgget() 函数创建消息队列。
  2. msgget() 函数需要两个参数:
    int msgget(key_t key, int msgflag)
    key_t key: 消息队列的键值,用于标识消息队列。
    int msgflg: 控制消息队列的创建方式,例如 IPC_CREAT 用于创建新的消息队列,IPC_EXCL 用于检查消息队列是否存在。
  3. 成功创建或访问消息队列,返回正整数 或 为0消息队列标识符(ID)。
    失败返回 < 0

使用 IPC_PRIVATE 创建的每个消息队列都是唯一的,即使是同一个进程多次创建,它们之间也是相互独立的,不会共享数据。

  • IPC_PRIVATE 用于创建一个私有的消息队列,它只对创建它的进程可见,其他进程无法访问。并且当创建者进程终止时,该 IPC 对象也会被自动销毁
  • 当一个进程使用 IPC_PRIVATE 创建消息队列时,系统会分配一个唯一的标识符(即消息队列 ID)给它。
  • 即使同一个进程多次使用 IPC_PRIVATE 创建消息队列,每次分配的标识符也会是不同的。
    因此,即使是同一个进程,每次使用 IPC_PRIVATE 创建的队列都是独立的、不同的消息队列。
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>int main() {int msqid1 = msgget(IPC_PRIVATE, IPC_CREAT | 0666);if (msqid1 == -1) {perror("msgget 1");exit(1);}printf("Message queue ID 1: %d\n", msqid1);int msqid2 = msgget(IPC_PRIVATE, IPC_CREAT | 0666);if (msqid2 == -1) {perror("msgget 2");exit(1);}printf("Message queue ID 2: %d\n", msqid2);// msqid1 和 msqid2 不同,指向不同的消息队列if (msqid1 == msqid2) {printf("Error: msqid1 and msqid2 are the same!\n");} else {printf("msqid1 and msqid2 are different, as expected.\n");}// 删除消息队列msgctl(msqid1, IPC_RMID, NULL);msgctl(msqid2, IPC_RMID, NULL);return 0;
}

3. 发送消息

  1. 使用 msgsnd() 函数发送消息到消息队列。

  2. msgsnd() 函数需要四个参数:
    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    int msqid: 消息队列标识符。
    const void *msgp: 指向消息结构体的指针。
    size_t msgsz: 消息结构体的大小。
    int msgflg: 控制消息发送方式,例如 IPC_NOWAIT 用于非阻塞发送,填0则用于阻塞发送。

  3. 返回值是 0 表示发送成功,-1 表示发送失败。

4. 接收消息

  1. 使用 msgrcv() 函数从消息队列接收消息。

  2. msgrcv() 函数需要五个参数:
    ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
    int msqid: 消息队列标识符。
    void *msgp: 指向消息结构体的指针。
    size_t msgsz: 消息结构体的大小。
    long msgtyp: 消息类型,用于过滤接收的消息。
    int msgflg: 控制消息接收方式,例如 IPC_NOWAIT 用于非阻塞接收。填0则用于阻塞接受。

  3. 返回值是接收到的消息大小,如果接收失败,则返回 -1。(若使用了 IPC_NOWAIT这个标志,在无消息时也是返回-1)

5. 消息结构体

  1. 消息结构体包含消息类型 (mtype) 和消息内容 (mtext)。
  2. mtype 用于区分不同类型的消息。这个类型一点是
  3. mtext 可以存储任意数据。
// 定义消息结构体
struct msgbuf {long mtype;    // 消息类型,一定要放在结构体前面,而且是long类型char mtext[256]; // 消息内容
};

6. 消息队列控制(删除、获取队列状态)

  1. 使用 msgctl() 函数控制消息队列。
  2. msgctl() 函数需要三个参数:
    int msgctl(int msqid,int cmd,struct msqid_ds *buf)
    int msqid: 消息队列标识符。
    int cmd: 控制命令,例如 IPC_STAT 用于获取消息队列状态,IPC_RMID 用于删除消息队列。
  3. struct msqid_ds *buf: 指向消息队列控制结构体的指针,用于获取或设置消息队列属性。

不使用 IPC_PRIVATE 创建消息队列创建后,如果不删除,会一直存在于系统中,直到系统重启。

消息队列是否存在

在这里插入图片描述

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <errno.h>int main() {key_t key = ftok(".", 'a'); // 获取消息队列键值int msgid = msgget(key, 0); // 尝试获取现有消息队列if (msgid == -1) {if (errno == ENOENT) {printf("Message queue with key %d does not exist.\n", key);} else {perror("msgget failed");}return 1;} else {printf("Message queue with key %d exists.\n", key);return 0;}
}

7. 使用场景

进程间通信:不同进程之间传递数据。
多线程通信:不同线程之间传递数据,但需要考虑线程同步问题。

8. 注意事项

  1. 消息队列的创建和使用需要权限控制。
  2. 消息队列需要使用 msgctl() 函数删除,否则会导致资源泄漏。
  3. 在多线程环境下使用消息队列时,需要考虑线程同步问题,避免数据竞争。
  4. 注意创建消息队列的 KEY,不能用相同的KEY获取消息队列,否则将会导致消息无法传输

总结:

Linux 消息队列是一种简单易用的进程间通信机制,可以用于进程之间或线程之间交换数据。它提供了灵活的消息类型和内容存储,但需要谨慎处理线程同步问题,并注意资源释放。

使用例子

=注意:struct msgbuf 中的long mtype一定是long类型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>#define MSG_KEY 12345// 定义消息结构体
struct msgbuf {long mtype;    // 消息类型char mtext[256]; // 消息内容
};int main() {int msqid;struct msgbuf message;// 创建消息队列msqid = msgget(MSG_KEY, IPC_CREAT | 0666);if (msqid == -1) {perror("msgget failed");exit(1);}// 进程A: 发送消息if (fork() == 0) { // 子进程printf("Process A: Sending message...\n");// 准备消息结构体message.mtype = 1;strcpy(message.mtext, "Hello from Process A!");// 发送消息if (msgsnd(msqid, &message, strlen(message.mtext) + 1, 0) == -1) {perror("msgsnd failed");exit(1);}printf("Process A: Message sent.\n");exit(0);} else { // 父进程// 等待子进程发送消息sleep(1);// 进程B: 接收消息printf("Process B: Receiving message...\n");// 接收消息if (msgrcv(msqid, &message, sizeof(message.mtext), 1, 0) == -1) {perror("msgrcv failed");exit(1);}printf("Process B: Received message: %s\n", message.mtext);}// 清理消息队列if (msgctl(msqid, IPC_RMID, NULL) == -1) {perror("msgctl failed");exit(1);}return 0;
}
#include <pthread.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>#define MSG_KEY 12345// 定义消息结构体
struct msgbuf {long mtype;    // 消息类型char mtext[256]; // 消息内容
};// 线程函数: 发送消息
void *sender(void *arg) {int msqid;struct msgbuf message;// 获取消息队列标识符msqid = *(int *)arg;while (1) {// 准备消息结构体message.mtype = 1;sprintf(message.mtext, "Message from sender thread: %ld", getpid());// 发送消息if (msgsnd(msqid, &message, strlen(message.mtext) + 1, 0) == -1) {perror("msgsnd failed");exit(1);}printf("Sender thread: Message sent: %s\n", message.mtext);sleep(1);}return NULL;
}// 线程函数: 接收消息
void *receiver(void *arg) {int msqid;struct msgbuf message;// 获取消息队列标识符msqid = *(int *)arg;while (1) {// 接收消息if (msgrcv(msqid, &message, sizeof(message.mtext), 1, 0) == -1) {perror("msgrcv failed");exit(1);}printf("Receiver thread: Received message: %s\n", message.mtext);}return NULL;
}int main() {int msqid;pthread_t sender_thread, receiver_thread;// 创建消息队列msqid = msgget(MSG_KEY, IPC_CREAT | 0666);if (msqid == -1) {perror("msgget failed");exit(1);}// 创建发送线程if (pthread_create(&sender_thread, NULL, sender, &msqid) != 0) {perror("pthread_create failed");exit(1);}// 创建接收线程if (pthread_create(&receiver_thread, NULL, receiver, &msqid) != 0) {perror("pthread_create failed");exit(1);}// 等待线程结束pthread_join(sender_thread, NULL);pthread_join(receiver_thread, NULL);// 清理消息队列if (msgctl(msqid, IPC_RMID, NULL) == -1) {perror("msgctl failed");exit(1);}return 0;
}

判断消息队列是否存在的代码

msgget() 函数中,将 IPC_EXCLIPC_CREAT 标志位一起使用:

2int msqid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);
key: 消息队列的键值。
IPC_CREAT: 如果消息队列不存在,则创建新的消息队列。
IPC_EXCL: 如果消息队列已存在,则返回错误,并设置 errnoEEXIST
0666: 设置消息队列的访问权限。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>int main() {int msqid;key_t key = 12345;// 第一次创建消息队列,成功msqid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);if (msqid == -1) {perror("msgget failed");exit(1);}printf("Message queue created successfully.\n");// 第二次创建消息队列,失败,因为消息队列已经存在msqid = msgget(key, IPC_CREAT | IPC_EXCL | 0666);if (msqid == -1) {if (errno == EEXIST) {printf("Message queue already exists.\n");} else {perror("msgget failed");exit(1);}}return 0;
}

解释:

  • 第一次调用 msgget() 时,使用 IPC_CREAT | IPC_EXCL 标志位,成功创建消息队列。
  • 第二次调用 msgget() 时,使用相同的键值,由于消息队列已存在,IPC_EXCL 标志位会使 msgget() 返回错误,errno 为 EEXIST。

使用场景:

  • 当需要确保创建的消息队列是新的,并且不允许重复创建时,使用 IPC_EXCL 标志位。
  • 在多进程或多线程环境中,使用 IPC_EXCL 可以防止多个进程或线程同时创建相同的消息队列。

获取队列空间大小

步骤:

  1. 获取消息队列控制结构体:
    使用 msgctl() 函数,并设置 cmd 为 IPC_STAT,将消息队列控制结构体指针作为第三个参数传入。
    在这里插入图片描述

  2. 获取 msg_qbytes 字段:
    msqid_ds 结构体中包含消息队列的各种属性,包括 msg_qbytes 字段,它表示消息队列的最大容量(字节)。

  3. 计算队列大小:
    msg_qbytes 代表的是消息队列的总容量如果需要计算可以容纳的消息数量,需要考虑每个消息的大小
    int max_messages = msqid_ds.msg_qbytes / sizeof(struct msgbuf); // 假设 struct msgbuf是你的消息结构体

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>int main() {int msqid;key_t key = 12345;// 创建消息队列msqid = msgget(key, IPC_CREAT | 0666);if (msqid == -1) {perror("msgget failed");exit(1);}// 获取消息队列控制结构体struct msqid_ds msqid_ds;if (msgctl(msqid, IPC_STAT, &msqid_ds) == -1) {perror("msgctl failed");exit(1);}// 打印消息队列最大容量printf("Message queue size: %ld bytes\n", msqid_ds.msg_qbytes);// 计算可以容纳的消息数量(假设消息结构体大小为 100 字节)int max_messages = msqid_ds.msg_qbytes / 100;printf("Maximum number of messages: %d\n", max_messages);// 删除消息队列if (msgctl(msqid, IPC_RMID, NULL) == -1) {perror("msgctl failed");exit(1);}return 0;
}

在这里插入图片描述

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

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

相关文章

低代码可视化-转盘小游戏可视化-代码生成器

转盘小程序是一种互动工具&#xff0c;它通过模拟真实的转盘抽奖或决策体验&#xff0c;为用户提供了一种有趣且公平的选择方式。以下是对转盘小程序的详细介绍&#xff1a; 转盘小程序的应用场景 日常决策&#xff1a;转盘小程序可以帮助用户解决日常生活中的选择困难问题&a…

【Prometheus】Prometheus如何监控Haproxy

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

仅仅4M!windows系统适用,免费无限制使用!

软件介绍 在日常生活里&#xff0c;我们经常会碰到电脑运行迟缓、网速卡顿的现象&#xff0c;却又不清楚是哪个程序在占用过多资源。这种时候&#xff0c;一款能实时监测网络和系统状态的工具就变得非常关键了。今天呢&#xff0c;就给大家介绍一个小巧又实用的监控工具——「T…

计算机毕业设计hadoop+spark+hive图书推荐系统 豆瓣图书数据分析可视化大屏 豆瓣图书爬虫 知识图谱 图书大数据 大数据毕业设计 机器学习

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

Harmony Next 支持创建分身

应用分身能实现在一个设备上安装多个相同的应用&#xff0c;实现多个账号同时登录使用和运行并且互不影响。主要应用场景有社交账号双开、游戏大小号双开等&#xff0c;无需账号切换&#xff0c;从而省去频繁登录的繁琐。 Harmony Next 很容易就能让 App 支持创建分身。 官方文…

Linux下 date时间应该与系统的 RTC(硬件时钟)同步

发现客户服务器时间与真实时间不同&#xff0c;并且服务器没有网络。 解决办法&#xff1a;时间应该与系统的 RTC&#xff08;硬件时钟&#xff09;同步 手动设置系统时间 使用 date 命令将系统时间设置为 2025年01月21日 14:12:00&#xff1a; sudo date --set"2025-01…

XX污水处理厂基于RK3576核心板应用(四)——人员倒地智能识别系统方案

通过 SAIL-RK3576核心板 支撑的 人员倒地识别系统&#xff0c;污水处理厂能够在广阔、复杂的区域内实时监控人员安全&#xff0c;实现意外倒地等事故的秒级响应与干预。搭配多元人形动态监测机制&#xff0c;还可进一步拓展对其他异常动作或不安全行为的识别&#xff0c;持续保…

【IEEE Fellow 主讲报告| EI检索稳定】第五届机器学习与智能系统工程国际学术会议(MLISE 2025)

重要信息 会议时间地点&#xff1a;2025年6月13-15日 中国深圳 会议官网&#xff1a;http://mlise.org EI Compendex/Scopus稳定检索 会议简介 第五届机器学习与智能系统工程国际学术会议将于6月13-15日在中国深圳隆重召开。本次会议旨在搭建一个顶尖的学术交流平台&#xf…

css粘性定位超出指定宽度失效问题

展示效果 解决办法&#xff1a;外层容器添加display:grid即可 完整代码 <template><div class"box"><div class"line" v-for"items in 10"><div class"item" v-for"item in 8">drgg</div>&…

随机变量的变量替换——归一化流和直方图规定化的数学基础

变量替换是一种在统计学和数学中广泛应用的技术&#xff0c;它通过定义新的变量来简化问题&#xff0c;使得原本复杂的随机变量变得更加容易分析。 变量替换的公式&#xff0c;用于将一个随机变量 X X X 的概率密度函数 f X f_X fX​ 转换为其经过函数 g g g 变换后的随机变…

Scrapy之一个item包含多级页面的处理方案

目标 在实际开发过程中&#xff0c;我们所需要的数据往往需要通过多个页面的数据汇总得到&#xff0c;通过列表获取到的数据只有简单的介绍。站在Scrapy框架的角度来看&#xff0c;实际上就是考虑如何处理一个item包含多级页面数据的问题。本文将以获取叶子猪网站的手游排行榜及…

应用层协议 HTTP 讲解实战:从0实现HTTP 服务器

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Linux 目录 一&#xff1a;&#x1f525; HTTP 协议 &#x1f98b; 认识 URL&#x1f98b; urlencode 和 urldecode 二&#xff1a;&#x1f525; HTTP 协议请求与响应格式 &#x1f98b; HTTP 请求…

Ansys Motor-CAD:IPM 电机实验室 - 扭矩速度曲线

各位电动机迷们&#xff0c;大家好&#xff1a; 在本博客中&#xff0c;我讨论了如何使用 Ansys Motor-CAD 通过 LAB 模块获取扭矩速度曲线。使用每安培最大扭矩电机控制策略&#xff0c;并涵盖恒定扭矩区域和恒定功率、磁通减弱区域。分析了高转子速度如何影响功率输出。 模型…

IPhone16 Pro 设备详情

目录 产品宣传图内部图——前内部图——后设备详细信息 产品宣传图 内部图——前 内部图——后 设备详细信息 信息收集于HubWeb.cn

SQL注入漏洞之基础数据类型注入 字符 数字 搜索 XX 以及靶场实例哟

目录 基础数据类型SQL注入 字符类型注入 单引号双引号解释 案例练习: 数字类型注入 案例 搜索性注入: 案例 XX性注入: 语句 案例 基础SQL注入类型分类 基础数据类型SQL注入 字符类型注入 xxx or 11 # select id,email from member where usernamexx or 11 # --…

【Elasticsearch】腾讯云安装Elasticsearch

Elasticsearch 认识Elasticsearch安装Elasticsearch安装Kibana安装IK分词器分词器的作用是什么&#xff1f;IK分词器有几种模式&#xff1f;IK分词器如何拓展词条&#xff1f;如何停用词条&#xff1f; 认识Elasticsearch Elasticsearch的官方网站如下 Elasticsearch官网 Ela…

【Unity】 HTFramework框架(五十九)快速开发编辑器工具(Assembly Viewer + ILSpy)

更新日期&#xff1a;2025年1月23日。 Github源码&#xff1a;[点我获取源码] Gitee源码&#xff1a;[点我获取源码] 索引 开发编辑器工具MouseRayTarget焦点视角Collider线框Assembly Viewer搜索程序集ILSpy反编译程序集搜索GizmosElement类找到Gizmos菜单找到Gizmos窗口分析A…

计算机网络之网络层

本文章目录结构出自于《王道计算机考研 计算机网络_哔哩哔哩_bilibili》 03 网络层 在网上看到其他人做了相关笔记&#xff0c;就不再多余写了&#xff0c;直接参考着学习吧。 1 详解网络层-网络层概述和编址【王道计算机网络笔记】_wx63088f6683f8f的技术博客_51CTO博客 2 …

Geek Uninstaller,绿色免安装轻量的应用卸载工具!

软件介绍 链接 一个轻量级拥有简洁交互界面、快速卸载电脑安装程序的工具。可快速扫描删除残余文件和注册表&#xff0c;对顽固和损坏的程序可执行强制删除、独立页面管理卸载系统Microsoft Store应用、快速打开程序安装文件夹、快速打开编辑程序注册表位置、将安装程序列表导…

解释 RESTful API,以及如何使用它构建 web 应用程序

RESTful API是一种设计和构建Web服务的架构风格&#xff0c;它遵循一组规范和约定&#xff0c;使客户端能够通过HTTP协议进行与服务器的通信&#xff0c;并进行资源的创建、读取、更新和删除操作。 REST&#xff08;Representational State Transfer&#xff09;表示资源的状态…