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语义向量之间的余弦相似度,从而训…...

DAY 44 Apache网页优化
Apache网页优化 概述 在企业中,部署Apache后只采用默认的配置参数,会引发网站很多问题,换言之默认配置是针对以前较低的服务器配置的,以前的配置已经不适用当今互联网时代 为了适应企业需求,就需要考虑如何提升Apach…...

移动端手机网页适配iPad与折叠屏设备
采用的网页适配方案:移动端页面px布局适配方案(viewport) 产生此问题的原因 由于手机与平板等设备宽高比差异导致页面展示不全或者功能按钮展示在视口之外点击不到。 简单来说就是我们的页面都是瘦长(即高大于宽)的,而折叠屏等设…...

深入剖析 Qt QMap:原理、应用与技巧
目录标题 引言:QMap 的重要性与基本概念QMap 简介:基本使用方法(QMap Basics: Concepts and Usage)QMap 迭代器:遍历与操作键值对(QMap Iterators: Traversing and Manipulating Key-Value Pairs࿰…...

SpringBoot使用Hbase
SpringBoot使用Hbase 文章目录 SpringBoot使用Hbase一,引入依赖二,配置文件添加自己的属性三,配置类注入HBASE配置四,配置Hbase连接池五,配置操作服务类 一,引入依赖 <dependency><groupId>org…...

SQL优化总结
SQL优化总结 1. MySQL层优化五个原则2. SQL优化策略2.1 避免不走索引的场景 3. SELECT语句其他优化3.1 避免出现select *3.2 避免出现不确定结果的函数3.3 多表关联查询时,小表在前,大表在后。3.4 使用表的别名3.5 调整Where字句中的连接顺序 附录 1. My…...

【python学习】基础篇-字典的基本操作 获取当前日期时间
1.字典的定义与创建 定义字典时,每个元素都包含两个部分“键”和“值”,在“键”和“值”之间使用冒号(:)分隔,相邻两个元素使用逗号分隔,所有元素放在一个大括号“{}”中。语法格式如下: dictionary (‘key1’:‘value1’, &quo…...

Python FreeCAD.Vector方法代码示例
Python FreeCAD.Vector方法代码示例 本文整理汇总了Python中FreeCAD.Vector方法的典型用法代码示例。如果您正苦于以下问题:Python FreeCAD.Vector方法的具体用法?Python FreeCAD.Vector怎么用?Python FreeCAD.Vector使用的例子?那…...

HDFS 梳理
HDFS客户端 客户端作用 管理文件目录文件系统操作读写 客户端生成 配置项 配置 客户端状态 缓冲相关参数,读写缓冲 失败切换操作 推测执行?? NN引用 NNProxy 客户端关闭 关闭IO流 修改状态 关闭RPC连接 是否有多个RPC连接? HDFS读 打开文件构…...

ChatGPT团队中,3个清华学霸,1个北大学霸,共9位华人
众所周知,美国硅谷其实有着众多的华人,哪怕是芯片领域,华为也有着一席之地,比如AMD 的 CEO 苏姿丰、Nvidia 的 CEO 黄仁勋 都是华人。 还有更多的美国著名的科技企业中,都有着华人的身影,这些华人ÿ…...

通过工具生成指定 类型 大小 文件
今天给大家介绍一个神器 首先 大家在开发过程中或许经常需要涉及到文件上传类的功能 需要测试文件过大 空文件等等清空 不同大小的文件 而这种文件大小是比较不好控制的 但大家可以下载我的资源 文件生成工具(可生成指定大小 类型文件) 下载下来里面就有一个 fileGeneration…...