linux多线(进)程编程——(7)消息队列
前言
现在修真界大家的沟通手段已经越来越丰富了,有了匿名管道,命名管道,共享内存等多种方式。但是随着深入使用人们逐渐发现了这些传音术的局限性。
匿名管道:只能在有血缘关系的修真者(进程)间使用,局限性大。
命名管道:需要创建出一个文件,并通过文件方式沟通,很不方便。
共享内存:这个就更可怕了,传递速度太快,大量无关信息也可能会涌入脑海,而且两个人如果想法有冲突就会把各自的思路相互覆盖。(这个涉及到到进程间同步的一些知识,后续解决。)
无数心怀远大抱负的修士期待着能解决这些问题,从而一句成名。很快,有人成功了,他提出了一种新的沟通方式(消息队列)。
消息队列
消息队列,消息是定语,修饰队列,也就是装消息的队列,本质上是一个链表(这里涉及到队列的两种实现形式:数组环形实现和链表实现)。
为什么使用链表实现消息队列呢?因为这个队列并不是十分纯粹的先入先出操作。偶尔会涉及到消息的筛选。所以使用链表来实现。
消息队列由进程创建,但是它位于内核空间,由内核管理,用于在不同进程间传递数据。

创建一个消息队列 / 获取一个已经存在的消息队列id
int msgget(key_t key, int msgflg);
其中各个参数的含义是:
key:键值,操作系统中唯一
msgflg:标志位,用于设置用户权限
返回值:消息队列的id,用来表示这个消息队列。
当消息队列已经存在时,我们会字节根据键值索引得到这个消息队列的id。之后我们操作消息队列都是使用这个id而不是键值。
这里插一句,大家可以发现,在linux中经常有这种一个id代表一个对象的管理手段,无论是文件,还是共享内存,还是今天学的消息队列都是这样。未来这种手段我们还会见到更多,大家要学会把他们联系起来。
先创建一个消息队列:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int main() {int id = msgget(1, IPC_CREAT);return 0;
}
创建成功后我们使用ipcs指令查看系统中的消息队列(这个我指令在共享内存中讲过)。从结果中我们可以看到现在我们的系统上有一个key值为1的消息队列,他的id是0。
lol@hyl:~/work/linux_study/msgque$ ipcs------ Message Queues --------
key msqid owner perms used-bytes messages
0x00000001 0 hyl 0 0 0 ------ Shared Memory Segments --------
key shmid owner perms bytes nattch status ------ Semaphore Arrays --------
key semid owner perms nsems
大家可以发现,在我输入ipcs指令时我的程序应该已经停止运行了,但是我们的消息队列仍然存在,这个与之前将的共享内存类似。因此如果我们在程序中创建了消息队列而不及时释放,会引起严重的内存泄漏。这种内存泄漏更加严重,因为即使进程停止了它也不会被系统回收。
在这里我们手动删除消息队列,输入ipcrm -q 0(这个指令也是我们之前讲过的,-q代表消息对了 -m代表共享内存。)
lol@hyl:~/work/linux_study/msgque$ ipcrm -q 0
lol@hyl:~/work/linux_study/msgque$ ipcs------ Message Queues --------
key msqid owner perms used-bytes messages ------ Shared Memory Segments --------
key shmid owner perms bytes nattch status ------ Semaphore Arrays --------
key semid owner perms nsems
创建好了消息队列后我们如何实现进程间通信呢?
发送消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
参数的意义:
msqid:消息队列的id
msgp:存放消息的缓冲区,类型为struct msgbuf,定义如下:
#define DATA_SIZE 128
// 在源文件中仿造这个格式自行定义即可
struct msgbuf{long mtype;//消息类型(>0)char mtext[DATA_SIZE];//消息文本
};
msgsz:发送数据的大小,这里是msgp结构体中mtext的大小DATA_SIZE。
msgflg:是否阻塞,为0表示阻塞发送
接收消息:
在struct msgbuf类型的结构体中,mtype成员的作用主要是在接受数据时设置优先级,接收消息的函数定义为:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
其余参数与上面发送函数相同,主要是:
msgtyp:表示接收数据的类型。
当msgtyp = 0时,则返回队头数据,暨最先进入队列的数据。
当msgtyp > 0时,返回队列中消息类型(暨msgp->mtype)等于msgtyp 的数据。
当msgtyp < 0时,返回其类型小于或等于mtype参数的绝对值的最小的一个消息。
有了这个参数我们就可以实现关键消息优先处理的机制,越关键的消息我们就将他的消息类型设置的越小,这样我们就可以使用msgtyp < 0来优先处理高优先级的消息。
关于消息队列的阻塞与非阻塞
对于接收来说,阻塞是指消息队列为空或者没有需要的消息类型是,程序停在msgrcv处等待消息的到来。
对于发送来说,阻塞是指消息队列内部容量满了之后,程序停在msgsnd处等待消息队列内部的位置空出。
以消息接收为例(进程p1):
int main() {int id = msgget(1, 0666 | IPC_CREAT);printf( "id = : %d\n", id );struct msgbuf* buf = (struct msgbuf *)malloc(sizeof(struct msgbuf));msgrcv(id, buf, 128, 0, 0);return 0;
}
对于这段代码,我创建消息队列后直接调用msgrcv接收消息队列,由于此时消息队列为空,因此系统会被阻塞,无法顺利退出,直到其他进程向消息队列内部发送数据。
lol@hyl:~/work/linux_study/msgque$ ./p1
id = : 0# 这里没有输出,因为程序一直被阻塞
这时我们尝试使用另一个进程向该进程发送数据(进程p2)
int main() {int id = msgget(1, 0666 | IPC_CREAT);printf( "id = : %d\n", id );struct msgbuf* buf = (struct msgbuf *)malloc(sizeof(struct msgbuf));strcpy(buf->data, "hello, world!");msgsnd(id, buf, 128, 0);return 0;
}
后台运行p1,这里显示p1正在运行,pid为134070
lol@hyl:~/work/linux_study/msgque$ ./p1&
[1] 134070
id = : 0
运行p2,终端输出了[1]进程运行完毕,证明了进程1确实被阻塞,等待消息队列内的消息
lol@hyl:~/work/linux_study/msgque$ ./p1&
[1] 134070
id = : 0
lol@hyl:~/work/linux_study/msgque$ ./p2
id = : 0
hello, world![1]+ Done ./p1
当我们将p1的msgrcv更改为:msgrcv(id, buf, 128, 0, IPC_NOWAIT);
int main() {int id = msgget(1, 0666 | IPC_CREAT);printf( "id = : %d\n", id );struct msgbuf* buf = (struct msgbuf *)malloc(sizeof(struct msgbuf));msgrcv(id, buf, 128, 0, IPC_NOWAIT);printf("%s", buf->data);return 0;
}
运行程序可以看到终端直接再次进入了等待模式,证明消息队列没有阻塞。
lol@hyl:~/work/linux_study/msgque$ ./p1
id = : 0
lol@hyl:~/work/linux_study/msgque$
控制消息队列
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
这个接口的最常见的用法作用就是用于删除消息队列,当cmd = IPC_RMID时:
msgctl(msgid, IPC_RMID, NULL);//将队列从系统内核中删除
完整代码
proc1.c:进程1阻塞接收数据
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>struct msgbuf {int type;char data[128];
};int main() {int id = msgget(1, 0666 | IPC_CREAT);printf( "id = : %d\n", id );struct msgbuf* buf = (struct msgbuf *)malloc(sizeof(struct msgbuf));msgrcv(id, buf, 128, 0, 0); // 阻塞等待printf("%s", buf->data); // 打印数据msgctl(id, IPC_RMID, NULL); // 删除消息队列return 0;
}
proc2.c进程2
小结
本节我们讲解了消息队列的用法。以及如何使用消息队列实现进程间通信。
结束语
随着消息队列的诞生,修士们的沟通越来越安全,越来越方便。
相关文章:
linux多线(进)程编程——(7)消息队列
前言 现在修真界大家的沟通手段已经越来越丰富了,有了匿名管道,命名管道,共享内存等多种方式。但是随着深入使用人们逐渐发现了这些传音术的局限性。 匿名管道:只能在有血缘关系的修真者(进程)间使用&…...
WinForm真入门(14)——ListView控件详解
一、ListView 控件核心概念与功能 ListView 是 WinForm 中用于展示结构化数据的多功能列表控件,支持多列、多视图模式及复杂交互,常用于文件资源管理器、数据报表等场景。 核心特点: 支持 5种视图模式:Details&…...
Python + Playwright:规避常见的UI自动化测试反模式
Python + Playwright:规避常见的UI自动化测试反模式 前言反模式一:整体式页面对象(POM)反模式二:具有逻辑的页面对象 - POM 的“越界”行为反模式三:基于 UI 的测试设置 - 缓慢且脆弱的“舞台搭建”反模式四:功能测试过载 - “试图覆盖一切”的测试反模式之间的关联与核…...
从服务器多线程批量下载文件到本地
1、客户端安装 aria2 下载地址:aria2 解压文件,然后将文件目录添加到系统环境变量Path中,然后打开cmd,输入:aria2c 文件地址,就可以下载文件了 2、服务端配置nginx文件服务器 server {listen 8080…...
循环神经网络 - 深层循环神经网络
如果将深度定义为网络中信息传递路径长度的话,循环神经网络可以看作既“深”又“浅”的网络。 一方面来说,如果我们把循环网络按时间展开,长时间间隔的状态之间的路径很长,循环网络可以看作一个非常深的网络。 从另一方面来 说&…...
linux运维篇-Ubuntu(debian)系操作系统创建源仓库
适用范围 适用于Ubuntu(Debian)及其衍生版本的linux系统 例如,国产化操作系统kylin-desktop-v10 简介 先来看下我们需要创建出来的仓库目录结构 Deb_conf_test apt源的主目录 conf 配置文件存放目录 conf目录下存放两个配置文件&…...
深度学习之微积分
2.4.1 导数和微分 2.4.2 偏导数 安装步骤 参考 ubuntu2018 安装 vcs2018 安装该…...
Express中间件(Middleware)详解:从零开始掌握(3)
实用中间件模式25例 1. 基础增强模式 请求属性扩展 function extendRequest() {return (req, res, next) > {req.getClientLanguage () > {return req.headers[accept-language]?.split(,)[0] || en;};next();}; } 响应时间头 function responseTime() {return (r…...
深入理解微信小程序开发:架构、组件化与进阶实战
📘博文正文: 深入理解微信小程序开发:架构、组件化与进阶实战 微信小程序已成为移动互联网的重要入口。随着业务复杂度提升,仅靠入门知识已无法应对日常开发需求。本文将深入剖析小程序开发架构、组件化模式、状态管理、网络封装…...
逆向|中国产业政策大数据平台|请求体加密
2025-04-11 逆向地址:aHR0cDovL3poZW5nY2UuMmIuY24v 打开开发者工具出现debugger,直接注入脚本过掉无限debugger let aaa Function.prototype.constructor; Function.prototype.constructor function (params) { if(params ‘debugger’){ console.log(params); return null…...
在SpringBoot中访问 static 与 templates 目录下的内容
目录 步骤一:添加 Thymeleaf 依赖 (处理 Templates 目录)步骤二:配置静态资源路径 (可选但建议了解)步骤三:访问不同目录下的 HTML 文件访问 static 目录下的 HTML 文件访问 templates 目录下的 HTML 文件 总结 在使用 Spring Boot 开发 Web …...
游戏引擎学习第226天
引言,计划 我们目前的目标是开始构建“元游戏”结构。所谓元游戏,指的是不直接属于核心玩法本身,但又是游戏体验不可或缺的一部分,比如主菜单、标题画面、存档选择、选项设置、过场动画等。我们正在慢慢将这些系统结构搭建起来。…...
青少年编程与数学 02-016 Python数据结构与算法 22课题、并行算法
青少年编程与数学 02-016 Python数据结构与算法 22课题、并行算法 一、GPU并行计算矩阵乘法示例 二、MPI并行计算allgather操作示例 三、Python中的并行计算多线程并行计算多进程并行计算 四、SIMD并行计算SIMD并行计算示例 总结 课题摘要: 并行算法是通过同时执行多个任务或操…...
Ubuntu系统18.04更新驱动解决方法
原始是:ubuntu18.04里面的驱动是470,对应cuda11.4 现在需要更新为525,对应cuda为12.0 实现: 1、打开终端 Ctrl Alt T2、使用 lspci 命令(快速查看显卡型号) lspci | grep -i vga3、终端输入 ubuntu-d…...
Notepad++安装Markdown实时预览插件
具体操作 打开notepad -> 插件 -> 插件管理 -> 可用 -> “Markdown Panel” -> 安装,安装完成后工具栏点击"Markdown Panel"按钮。 注意:由于网络等原因可能安装失败 导致工具栏没出现""Markdown Panel"按钮&am…...
Mysql-视图和存储过程
视图 1.介绍 视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。 通俗的讲,视图只保存了查询的SQL逻辑,不保存查询结果。所以我们在创建视图的时候,主要的工作就落在创建这条…...
FreeRTOS入门与工程实践-基于STM32F103(二)(互斥量,事件组,任务通知,软件定时器,中断管理,资源管理,调试与优化)
互斥量 一、互斥量(Mutex):解决多任务 “抢资源” 的问题 1. 是什么? 互斥量是一种 “任务间互斥访问资源” 的工具,本质是一个 只能被锁定(0)或释放(1)的二进制信号量…...
stm32面试
数据结构相关问题 stm32面试 数据结构相关问题 目录基础数据结构树与图排序与查找算法 Linux相关问题Linux系统基础Linux命令与脚本Linux网络与服务 操作系统相关问题操作系统基础概念操作系统调度算法操作系统同步与通信 STM32相关问题STM32硬件基础STM32编程与开发STM32应用与…...
202524 | 分布式事务
分布式事务(Distributed Transaction) 分布式事务是指跨多个数据库、服务或系统节点的事务操作,要求所有参与方要么全部成功提交,要么全部回滚,保证数据一致性。 1. 为什么需要分布式事务? 在单体应用中&…...
Python 企业级架构实战(上篇)
深入企业级系统设计与高可用架构,掌握构建可扩展 Python 系统的核心技能。 41. 微服务架构设计与 FastAPI 实现 多服务协同开发示例 # 用户服务 (user_service/main.py) from fastapi import FastAPI app = FastAPI() users_db = { 1: {"id": 1, "name&…...
在 macOS 上修改 最大文件描述符限制(Too many open files) 和 网络端口相关参数 需要调整系统级配置的详细步骤
在 macOS 上修改 最大文件描述符限制(Too many open files) 和 网络端口相关参数 需要调整系统级配置。以下是详细步骤: 在 macOS 上修改 最大文件描述符限制(Too many open files) 和 网络端口相关参数 需要调整系统级…...
Python 文本和字节序列(字符问题)
本章将讨论下述话题: 字符、码位和字节表述 bytes、bytearray 和 memoryview 等二进制序列的独特特性 全部 Unicode 和陈旧字符集的编解码器 避免和处理编码错误 处理文本文件的最佳实践 默认编码的陷阱和标准 I/O 的问题 规范化 Unicode 文本,进行安全的…...
通过Arduino IDE向闪存文件系统上传文件
注意:适用于Arduino IDE 2.0版本以上。对于Arduino IDE版本在2.0以下的请参考太极创客的教程:http://www.taichi-maker.com/homepage/esp8266-nodemcu-iot/iot-c/spiffs/upload-files/。 1. 下载脚本文件 下载地址:https://github.com/earl…...
leetcode 121. Best Time to Buy and Sell Stock
题目描述 本题属于动态规划类问题。 dp数组的含义 dp[i][0]表示从第0天到第i天为止,处于持有股票的状态下,账户里的最大金额。 dp[i][1]表示从第0天到第i天为止,处于不持有股票的状态下,账户里的最大金额。 按照这个定义dp[n-…...
【Docker-13】Docker Container容器
Docker Container(容器) 一、什么是容器? 通俗地讲,容器是镜像的运行实体。镜像是静态的只读文件,而容器带有运行时需要的可写文件层,并且容器中的进程属于运行状态。即容器运行着真正的应用进程。容器有…...
LoadableTransportInfo函数分析之RPCRT4!LOADABLE_TRANSPORT::LOADABLE_TRANSPORT初始化过程
LoadableTransportInfo函数分析 第一部分: RPC_STATUS LoadableTransportInfo ( IN RPC_CHAR * DllName, IN RPC_CHAR PAPI * RpcProtocolSequence, OUT TRANS_INFO * PAPI *pTransInfo ) { 。。。。。。。 pTransportInterface (*TransportLo…...
