Zephyr 消息队列
文章目录
- 简介
- 数据结构
- k_msgq
- 定义消息队列
- 发送消息
- k_msgq_put
- 接收消息
- k_msgq_get
- wait_q 的双重身份
- 清理消息队列
- k_msgq_cleanup
- 重置消息队列
- k_msgq_purge
- 读取数据
- k_msgq_peek
- k_msgq_peek_at
- 缓冲区容量
- k_msgq_num_free_get
- k_msgq_num_used_get
简介
- message queue 用于中断和线程之间进行数据传输的一项服务,其本质是一个环形缓冲区,例如串口驱动中通常会使用环形缓冲区来接收数据,缓冲区中每一项的大小是固定的。
- Zephyr 中的 msgq 是消息队列的具体实现,它需要提供一个缓冲区用于存储数据,缓冲区的大小是缓存项大小的整数倍,与 Zephyr 中的 queue 不同,消息队列写入和读取会拷贝数据。
数据结构
k_msgq
- k_msgq 是 Zephyr 中用于描述 消息队列的一个结构体,其成员变量包含了实现消息队列的必要元素:
struct k_msgq {/** Message queue wait queue */_wait_q_t wait_q;/** Lock */struct k_spinlock lock;/** Message size */size_t msg_size;/** Maximal number of messages */uint32_t max_msgs;/** Start of message buffer */char *buffer_start;/** End of message buffer */char *buffer_end;/** Read pointer */char *read_ptr;/** Write pointer */char *write_ptr;/** Number of used messages */uint32_t used_msgs;_POLL_EVENT;/** Message queue */uint8_t flags;SYS_PORT_TRACING_TRACKING_FIELD(k_msgq)
};
- wait_q
- 保存因缓冲区为空被阻塞的接收线程,或者是因缓冲区满被阻塞的发送线程。
- lock
- 多线程互斥
- msg_size
- 消息队列中每一项的大小
- max_msgs
- 消息队列中能容纳消息的最大数量
- buffer_start
- 缓冲区起始地址
- buffer_end
- 缓冲区结束地址
- read_ptr
- 类似于环形缓冲区中的 head,每读取一次地址向后偏移固定长度。
- write_ptr
- 类似于环形缓冲区中的 tail,每写入一次地址向后偏移固定长度。
- used_msgs
- 已经存放的消息条数,每读取一次减1,写入一次加1。
- flags
- 消息队列支持两种初始化方式,一种是用户提供缓冲区,另一种是由系统分配缓冲区,当选择由系统分配缓冲区时,在消息队列销毁时需要回收内存,为了标记内存来源,因此使用 flags 保存标志,下面是对应的标志位。
#define K_MSGQ_FLAG_ALLOC BIT(0)
定义消息队列
- 消息队列包含3个重要属性,单条消息的大小,缓冲区最大消息容量,缓冲区对齐大小。
- 定义一个静态缓冲区可使用 K_MSGQ_DEFINE 宏
#define K_MSGQ_DEFINE(q_name, q_msg_size, q_max_msgs, q_align) \static char __noinit __aligned(q_align) \_k_fifo_buf_##q_name[(q_max_msgs) * (q_msg_size)]; \STRUCT_SECTION_ITERABLE(k_msgq, q_name) = \Z_MSGQ_INITIALIZER(q_name, _k_fifo_buf_##q_name, \q_msg_size, q_max_msgs)
- 还可通过函数 k_msgq_init 初始化消息队列,该函数需要提供环形缓冲区和一个 k_msgq 对象:
struct msg_item_type { int val;
};static char __aligned(4) s_msgq_buffer[10 * sizeof(struct msg_item_type )];
static struct k_msgq s_msgq;k_msgq_init(&s_msgq, s_msgq_buffer, sizeof(struct msg_item_type ), 10);
- k_msgq_init 的另一个版本是 k_msgq_alloc_init,它可以从系统中动态分配一段空间作为环形缓冲区:
static struct k_msgq s_msgq;
k_msgq_alloc_init(&s_msgq, sizeof(struct msg_item_type ), 10);
发送消息
k_msgq_put
- 消息队列的消息发送函数:
- int k_msgq_put(struct k_msgq *msgq, const void *data, k_timeout_t timeout)
- 该函数的实现函数为 z_impl_k_msgq_put
int z_impl_k_msgq_put(struct k_msgq *msgq, const void *data, k_timeout_t timeout)
{/* 当 k_msgq_put 在中断中调用时,timeout 不等于 K_NO_WAIT 会触发断言 */__ASSERT(!arch_is_in_isr() || K_TIMEOUT_EQ(timeout, K_NO_WAIT), "");struct k_thread *pending_thread;k_spinlock_key_t key;int result;key = k_spin_lock(&msgq->lock);SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_msgq, put, msgq, timeout);/* 消息队列未满时,如果等待队列中存在等待线程,* 可将数据写入 swap_data 指向的内存中(线程在等待时已将缓存首地址放入swap_data),* 反之如果没有线程处于等待状态,将数据放入环形缓冲区中*/if (msgq->used_msgs < msgq->max_msgs) {/* message queue isn't full */pending_thread = z_unpend_first_thread(&msgq->wait_q);if (pending_thread != NULL) {SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_msgq, put, msgq, timeout, 0);/* give message to waiting thread */(void)memcpy(pending_thread->base.swap_data, data,msgq->msg_size);/* wake up waiting thread */arch_thread_return_value_set(pending_thread, 0);z_ready_thread(pending_thread);z_reschedule(&msgq->lock, key);return 0;} else {/* put message in queue */(void)memcpy(msgq->write_ptr, data, msgq->msg_size);msgq->write_ptr += msgq->msg_size;if (msgq->write_ptr == msgq->buffer_end) {msgq->write_ptr = msgq->buffer_start;}msgq->used_msgs++;
#ifdef CONFIG_POLLhandle_poll_events(msgq, K_POLL_STATE_MSGQ_DATA_AVAILABLE);
#endif /* CONFIG_POLL */}result = 0;} /* 队列满且等待时间为0,返回 -ENOMSG */else if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {/* don't wait for message space to become available */result = -ENOMSG;} /* 队列满且等待时间不为0,将当前线程的 swap_data 指向数据首地址,然后将调用线程挂起 */else {SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_msgq, put, msgq, timeout);/* wait for put message success, failure, or timeout */_current->base.swap_data = (void *) data;result = z_pend_curr(&msgq->lock, key, &msgq->wait_q, timeout);SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_msgq, put, msgq, timeout, result);return result;}SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_msgq, put, msgq, timeout, result);k_spin_unlock(&msgq->lock, key);return result;
}
- k_msgq_put 用于将消息写入环形缓冲区,该函数允许在中断中使用,在中断使用时 timeout 必须是0.
- 当等待队列非空时,可将数据直接拷贝至 tcb 中 swap_data 指向的内存中,相比放入队列后再从队列中将数据拷贝到用户空间,这种方式提高了效率。
- 只有当等待队列为空时,才会将数据暂存入环形缓冲区中,等待线程读取。
接收消息
k_msgq_get
- 消息队列的消息接收函数:
- int k_msgq_get(struct k_msgq *msgq, void *data, k_timeout_t timeout)
- 该函数的实现函数为 z_impl_k_msgq_get
int z_impl_k_msgq_get(struct k_msgq *msgq, void *data, k_timeout_t timeout)
{__ASSERT(!arch_is_in_isr() || K_TIMEOUT_EQ(timeout, K_NO_WAIT), "");k_spinlock_key_t key;struct k_thread *pending_thread;int result;key = k_spin_lock(&msgq->lock);SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_msgq, get, msgq, timeout);/* 当环形缓冲区中不为空时,从中读取一条消息 */if (msgq->used_msgs > 0U) {/* take first available message from queue */(void)memcpy(data, msgq->read_ptr, msgq->msg_size);msgq->read_ptr += msgq->msg_size;if (msgq->read_ptr == msgq->buffer_end) {msgq->read_ptr = msgq->buffer_start;}msgq->used_msgs--;/* 从等待队列中获取一个因缓冲区满,无法写入数据而被挂起的线程 */pending_thread = z_unpend_first_thread(&msgq->wait_q);if (pending_thread != NULL) {SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_msgq, get, msgq, timeout);/* 将线程需写入的数据放入缓冲区中 */(void)memcpy(msgq->write_ptr, pending_thread->base.swap_data,msgq->msg_size);msgq->write_ptr += msgq->msg_size;if (msgq->write_ptr == msgq->buffer_end) {msgq->write_ptr = msgq->buffer_start;}msgq->used_msgs++;/* 唤醒该线程 */arch_thread_return_value_set(pending_thread, 0);z_ready_thread(pending_thread);z_reschedule(&msgq->lock, key);SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_msgq, get, msgq, timeout, 0);return 0;}result = 0;}/* 缓冲区中没有数据,且等待时间为0,返回 -ENOMSG */else if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {/* don't wait for a message to become available */result = -ENOMSG;} /* 缓冲区中没有数据,且等待时间不为0,将调用线程添加到等待队列并挂起 */else {SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_msgq, get, msgq, timeout);/* wait for get message success or timeout */_current->base.swap_data = data;result = z_pend_curr(&msgq->lock, key, &msgq->wait_q, timeout);SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_msgq, get, msgq, timeout, result);return result;}SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_msgq, get, msgq, timeout, result);k_spin_unlock(&msgq->lock, key);return result;
}
- k_msgq_get 首先会判断缓冲区中是否有数据,如果缓冲区中有数据,会从中取出一条数据拷贝到用户空间。
- 缓冲区中有数据就意味着等待队列中可能存在因缓冲区满无法写入而被阻塞的线程,此时由于从缓冲区中读出一条数据,可以进行写入,便将被挂起线程中未写入的数据拷贝到缓冲区中,同时解除挂起。
- 如果缓冲区无数据且等待时间为0,返回 -ENOMSG。
- 如果缓冲区无数据且等待时间不为0,将当前线程挂起。
wait_q 的双重身份
- 消息队列与其他 IPC 对象中的 wait_q 有所不同,以Mutex为例,只有当加锁时需要进入等待,因此 wait_q 中存放的一定是因加锁被阻塞的线程。
- 而在消息队列中无论是发送数据还是接收数据都可能进入等待,并且它们使用的是同一个等待队列。
- 首先来看一下进入等待队列的条件:
- 发送线程在缓冲区满,且等待时间不为0时,将被放入等待队列中。
- 接收线程在缓冲区空,且等待时间不为0时,将被放入等待队列中。
- 两种情况不可能同时存在,因此可以共用一个等待队列。
清理消息队列
k_msgq_cleanup
- 清理消息队列主要作用是,回收系统分配的缓冲区。
- 为了安全,在清理时会检查消息队列是否仍然在被使用
int k_msgq_cleanup(struct k_msgq *msgq)
{SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_msgq, cleanup, msgq);CHECKIF(z_waitq_head(&msgq->wait_q) != NULL) {SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_msgq, cleanup, msgq, -EBUSY);return -EBUSY;}if ((msgq->flags & K_MSGQ_FLAG_ALLOC) != 0U) {k_free(msgq->buffer_start);msgq->flags &= ~K_MSGQ_FLAG_ALLOC;}SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_msgq, cleanup, msgq, 0);return 0;
}
重置消息队列
k_msgq_purge
- void k_msgq_purge(struct k_msgq *msgq)
void z_impl_k_msgq_purge(struct k_msgq *msgq)
{k_spinlock_key_t key;struct k_thread *pending_thread;key = k_spin_lock(&msgq->lock);SYS_PORT_TRACING_OBJ_FUNC(k_msgq, purge, msgq);/* 唤醒等待队列中被阻塞的线程,然后返回-ENOMSG */while ((pending_thread = z_unpend_first_thread(&msgq->wait_q)) != NULL) {arch_thread_return_value_set(pending_thread, -ENOMSG);z_ready_thread(pending_thread);}/* 然后将缓冲区中的数据清空 */msgq->used_msgs = 0;msgq->read_ptr = msgq->write_ptr;z_reschedule(&msgq->lock, key);
}
- 该函数用于清空消息队列,如果有线程因数据发送或接收而进入等待,同时会将被挂起的线程唤醒。
读取数据
k_msgq_peek
- int k_msgq_peek(struct k_msgq *msgq, void *data)
- 该函数以先进先出的方式从消息队列中读取一条消息,但是不会修改 read_str 将队列项移出队列,因此该操作无需等待。
k_msgq_peek_at
- int k_msgq_peek_at(struct k_msgq *msgq, void *data, uint32_t idx)
- k_msgq_peek_at 是 k_msgq_peek 的另一个版本,以 read_str 作为第一项,向后偏移 idx 项之后的数据,将其拷贝到 data 指向的内存中。
缓冲区容量
k_msgq_num_free_get
- uint32_t k_msgq_num_free_get(struct k_msgq *msgq)
- k_msgq_num_free_get 用于获取缓冲区中剩余容量,返回值代表可存储消息的数量。
k_msgq_num_used_get
- uint32_t k_msgq_num_used_get(struct k_msgq *msgq)
- k_msgq_num_used_get 用于获取已经使用的容量,即 used_msgs 的值。
相关文章:
Zephyr 消息队列
文章目录 简介数据结构k_msgq 定义消息队列发送消息k_msgq_put 接收消息k_msgq_get wait_q 的双重身份清理消息队列k_msgq_cleanup 重置消息队列k_msgq_purge 读取数据k_msgq_peekk_msgq_peek_at 缓冲区容量k_msgq_num_free_getk_msgq_num_used_get 简介 message queue 用于中…...
Jenkins自动化部署实例讲解
文章目录 前言实例讲解基本环境全局工具配置创建任务任务配置源码管理构建步骤(Build Steps)第一步:调用Maven第二步:执行shell启动容器 后记 前言 你平常在做自己的项目时,是否有过部署项目太麻烦的想法?…...
RK356X 解除UVC摄像头预览分辨率1080P限制
平台 RK3566 Android 11 概述 UVC: USB video class(又称为USB video device class or UVC)就是USB device class视频产品在不需要安装任何的驱动程序下即插即用,包括摄像头、数字摄影机、模拟视频转换器、电视卡及静态视频相机…...
English Learning - L2-14 英音地道语音语调 重音技巧 2023.04.10 周一
English Learning - L2-14 英音地道语音语调 重音技巧 2023.04.10 周一 课前热身重音日常表达节奏单词全部重读的句子间隔时间非重读单词代词和缩约词助动词声临其境语调预习 课前热身 学习目标 重音 重弱突出,重音突出核心表达的意思 重音是落在重读单词上&…...
3.6 n维随机变量
学习目标: 学习n维随机变量需要掌握一定的数学知识,包括多元微积分、线性代数和概率论等。要学习n维随机变量,我会采取以下步骤: 复习相关的数学知识:首先,我会复习多元微积分、线性代数和概率论的基本知…...
JavaSE学习进阶day06_02 Set集合和Set接口
第二章 Set系列集合和Set接口 Set集合概述:前面学习了Collection集合下的List集合,现在继续学习它的另一个分支,Set集合。 set系列集合的特点: Set接口: java.util.Set接口和java.util.List接口一样,同样…...
基于matlab分析卫星星座对通信链路的干扰
一、前言 此示例说明如何分析从中地球轨道 (MEO) 中的卫星星座到位于太平洋的地面站的下行链路上的干扰。干扰星座由低地球轨道(LEO)的40颗卫星组成。此示例确定下行链路闭合的时间、载波噪声加干扰比以及链路裕量。 此示例需要卫…...
Python中的异常——概述和基本语法
Python中的异常——概述和基本语法 摘要:Python中的异常是指在程序运行时发生的错误情况,包括但不限于除数为0、访问未定义变量、数据类型错误等。异常处理机制是Python提供的一种解决这些错误的方法,我们可以使用try/except语句来捕获异常并…...
Tomcat 部署与优化
1. Tomcat概述 Tomcat是Java语言开发的,Tomcat服务器是一个免费的开放源代码的Web应用服务器,是Apache软件基金会的Jakarta项目中的一个核心项目,由Apache、Sun和其他一些公司及个人 共同开发而成。Tomcat属于轻量级应用服务器,在…...
多模态之论文笔记ViLT
文章目录 ViLT: Vision-and-Language Transformer Without Convolution or Region Supervision一. 简介1.1 摘要1.2 文本编码器,图像编码器,特征交互复杂度分析1.2 特征交互方式分析1.3 图像特征提取分析 二. 方法 Vision-and-Language Transformer2.1.方…...
微服务架构下认证和鉴权理解
认证和鉴权 从单体应用到微服务架构,优势很多,但是并不是代表着就没有一点缺点了。 微服务架构,意味着每个服务都是松散耦合的。因此,作为软件工程师和架构师,我们在分布式架构中面临着安全挑战。微服务对外开放的端…...
Qt 网络编程之美:探索 URL、HTTP、服务发现与请求响应
Qt 网络编程之美:探索 URL、HTTP、服务发现与请求响应(The Beauty of Qt Network Programming: Exploring URL, HTTP, Service Discovery, and Request-Response 引言(Introduction)QUrl 类:构建和解析 URL(…...
毕业2年,跳槽到下一个公司就25K了,厉害了···
本人本科就读于某普通院校,毕业后通过同学的原因加入软件测试这个行业,角色也从测试小白到了目前的资深工程师,从功能测试转变为测试开发,并顺利拿下了某二线城市互联网企业的Offer,年薪 30W 。 选择和努力哪个重要&a…...
设计模式 -- 适配器模式
前言 月是一轮明镜,晶莹剔透,代表着一张白纸(啥也不懂) 央是一片海洋,海乃百川,代表着一块海绵(吸纳万物) 泽是一柄利剑,千锤百炼,代表着千百锤炼(输入输出) 月央泽,学习的一种过程,从白纸->吸收各种知识->不断输入输出变成自己的内容 希望大家一起坚持这个过程,也同…...
STM32之增量式编码器电机测速
STM32之增量式编码器电机测速 编码器编码器种类按监测原理分类光电编码器霍尔编码器 按输出信号分类增量式编码器绝对式编码器 编码器参数分辨率精度最大响应频率信号输出形式 编码器倍频 STM32的编码器模式编码器模式编码器的计数方向仅在TI1计数电机正转,向上计数…...
一图看懂 xlsxwriter 模块:用于创建 Excel .xlsx 文件, 资料整理+笔记(大全)
本文由 大侠(AhcaoZhu)原创,转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 xlsxwriter 模块:用于创建 Excel .xlsx 文件, 资料整理笔记(大全) 摘要模块图类关系图模块全展开【xlsxwriter】统计常量模块1 xlsxwrit…...
【社区图书馆】NVMe协议的命令
声明 主页:元存储的博客_CSDN博客 依公开知识及经验整理,如有误请留言。 个人辛苦整理,付费内容,禁止转载。 内容摘要 前言 命令由host提交到内存中的SQ队列中,更新TDBxSQ后,NVMe控制器通过DMA的方式将SQ中的命令(怎么取,如何取,取多少,因设计而异)取到控制器缓冲区…...
Nginx网站服务
Nginx概述 Nginx 是开源、高性能、高可靠、低资源消耗的 Web 和反向代理服务器,而且支持热部署,几乎可以做到 7 * 24 小时不间断运行,即使运行几个月也不需要重新启动,还能在不间断服务的情况下对软件版本进行热更新。对HTTP并发…...
第八篇 Spring 集成JdbcTemplate
《Spring》篇章整体栏目 ————————————————————————————— 【第一章】spring 概念与体系结构 【第二章】spring IoC 的工作原理 【第三章】spring IOC与Bean环境搭建与应用 【第四章】spring bean定义 【第五章】Spring 集合注入、作用域 【第六章】…...
双塔模型:微软DSSM模型浅析
1.背景 DSSM是Deep Structured Semantic Model (深层结构语义模型) 的缩写,即我们通常说的基于深度网络的语义模型,其核心思想是将query和doc映射到到共同维度的语义空间中,通过最大化query和doc语义向量之间的余弦相似度,从而训…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)
设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile,新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
