当前位置: 首页 > news >正文

【事件驱动框架OSAL】二.消息的管理机制

OSAL消息管理机制

    • 二、消息管理机制
      • 2.1 消息的数据结构
      • 2.2 消息内存分配
      • 2.3 消息的接收和销毁
      • 2.3 源码链接地址

二、消息管理机制

在上一篇文中提到,系统消息事件(SYS_EVENT_MSG)用于任务间传递数据,而消息队列是这种机制的基础,它允许任务通过异步消息传递复杂数据(如网络数据包、控制命令等),而不仅仅是简单的事件通知。

消息队列,即用来保存消息的队列数据结构,本文结合OSAL的源码,简述OSAL中消息的实现和管理,包括消息结构、消息内存管理,发送和接收等流程。

2.1 消息的数据结构

typedef struct
{uint8 event;					// 消息事件类型,可以从0到255,用于区分不同的消息,和前面的事件集没有关系uint8 status;					// 状态,作为备用
} osal_event_hdr_t;					// 统一的消息事件头typedef struct
{osal_event_hdr_t hdr;			// 第一个成员必须为osal_event_hdr_tuint8* Data_t;					// 变量指针,指向用户定义的数据或消息体,这里可以随便定义,定义其他结构体也行。
} osal_sys_msg_t;                    //使用默认系统消息结构体osal_sys_msg_t做用户消息结构体

注意:消息结构体的第一个成员必须是osal_msg_hdr_t,否则无法正确解析。在32位单片机中,sizeof(osal_sys_msg_t) = 8 字节,sizeof(uint8_t ) = 4字节 。

OSAL的消息队列传递的每条消息必须包含一个消息头osal_msg_hdr_t),用于标识消息类型和基本元数据。用户可根据需求扩展自定义数据字段。指向结构体的指针,经过强制转换,可以指向该结构体的首个成员,因为结构体变量的地址和首个成员地址是同一个。

在用户消息定义的结构体中,除了第一个成员变量必须为osal_event_hdr_t 结构外,之后的数据类型,可以随便定义。

例如:定义一个温度数据

// 用户自定义消息示例(温度数据)
typedef struct {osal_msg_hdr_t  hdr;    // 必须包含消息头uint16          temp;   // 温度值uint8           sensor_id; // 传感器ID
} TempMessage_t;

在本文中,使用指针变量Data_t来解释代码执行流程。

osal_sys_msg_t消息结构体,在内存中示意图如下:

2.2 消息内存分配

消息内存通过 osal_msg_allocate() 动态分配,需指定消息总长度(包括消息头和数据);接收任务处理完消息后,必须手动调用osal_msg_deallocate()释放内存,避免泄漏。

注意osal_msg_allocate() osal_mem_alloc() 内存分配函数,osal_msg_allocate() 要完成消息队列结点的构造。
源码实现如下:

uint8 * osal_msg_allocate(uint16 len)
{osal_msg_hdr_t *hdr;if(len == 0){return (NULL);}// 分配一块内存空间,并强制转换为 osal_msg_hdr_t*. 相当于为用户消息体osal_sys_msg_t增加了一个包头osal_msg_hdr_thdr = (osal_msg_hdr_t *) osal_mem_alloc((short)(len + sizeof(osal_msg_hdr_t)));if(hdr){hdr->next = NULL;hdr->len = len;hdr->dest_id = TASK_NO_TASK;return ((uint8 *)(hdr + 1));	// 返回用户消息的osal_sys_msg_t变量地址}else{return (NULL);}
}

而 osal_msg_hdr_t 结构为:

typedef struct
{void   *next;uint16 len;uint8  dest_id;
} osal_msg_hdr_t;

是队列的结点,用于形成队列数据结构。

经过这一步之后,一个完整的队列结点在内存中的分布示意如下 :

在这里,用户数据是一个int变量,用于保存打印次数count。

重新回到发送消息的任务处理函数中:

uint16 print_task_event_process(uint8 task_id, uint16 task_event)
{// 不接收消息,忽略系统消息事件if(task_event & PRINTF_STR){static int print_count = 0;printf("Print task printing, total memory : %d byte, used memory : %d byte !\n", MAXMEMHEAP, osal_heap_mem_used());print_count++;if(print_count % 5 == 0 && print_count != 0){//向统计任务发送消息general_msg_data_t *msg;msg = (general_msg_data_t*)osal_msg_allocate(sizeof(general_msg_data_t) + sizeof(int));if(msg != NULL){//消息结构体的data数据指针偏移至申请到的内存的数据段//msg->data = (unsigned char*)( msg + sizeof(osal_event_hdr_t) );msg->data = (unsigned char*)(msg + 1);msg->hdr.event = PRINTF_STATISTICS;msg->hdr.status = 0;*((int*)msg->data) = print_count;osal_msg_send(statistics_task_id, (uint8*)msg);}}return task_event ^ PRINTF_STR; //处理完后需要清除事件位}return 0;
}

这里的 general_msg_data_t 和前面提到的osal_sys_msg_t 结构一样。

可以看到,经过消息内存分配之后,osal_msg_allocate 函数返回的是类型为osal_sys_msg_t的消息地址,指向的是用户自定义的结构体内存,之后调用 osal_msg_send(),消息队列入队,然后调用osal_set_event 设置系统消息事件。

uint8 osal_msg_send(uint8 destination_task, uint8 *msg_ptr)
{if(msg_ptr == NULL){return (INVALID_MSG_POINTER);}if(destination_task >= tasksCnt){osal_msg_deallocate(msg_ptr);return (INVALID_TASK);}// Check the message headerif(OSAL_MSG_NEXT(msg_ptr) != NULL ||OSAL_MSG_ID(msg_ptr) != TASK_NO_TASK){osal_msg_deallocate(msg_ptr);return (INVALID_MSG_POINTER);}OSAL_MSG_ID(msg_ptr) = destination_task;// queue messageosal_msg_enqueue(&osal_qHead, msg_ptr);// Signal the task that a message is waitingosal_set_event(destination_task, SYS_EVENT_MSG);return (SUCCESS);
}

这一段代码中,需要注意的是这两个宏定义:

#define OSAL_MSG_NEXT(msg_ptr) 	((osal_msg_hdr_t *) (msg_ptr) - 1)->next
#define OSAL_MSG_ID(msg_ptr) 	((osal_msg_hdr_t *) (msg_ptr) - 1)->dest_id

结合前面提到的,队列结点在内存中的分布示意图可知,msg_ptr 是指向用户消息体的指针,将其强制转换为 osal_msg_hdr_t*,再减 1 操作,即向低地址前移sizeof(osal_msg_hdr_t)个字节,便可以回到队列结点首地址。

下面是调试过程中的截图
按照函数执行的顺序来说明。
一、首先在内存中分配20个字节,从0x20000784字节开始,分配20个字节空间。前8个字节用于保存 osal_msg_hdr_t 数据,返回用户消息体的地址。

二、然后再把返回的指针转换为指向用户消息体的指针,红色部分表示 osal_sys_msg_t 结构体内存块,data指针中保存的是地址,该地址中保存的是打印次数的整型值。由于分配的内存块是连续的,因此data指针变量的下一个地址,就是数据单元。

2.3 消息的接收和销毁

本例子中,在统计任务中接收其他任务发送过来的消息。有消息事件时,会进入系统消息事件中。接收任务处理完消息后,必须手动调用osal_msg_deallocate()释放内存,避免泄漏。

/*** @brief 当前任务的事件回调处理函数* @param task_id       [任务ID]* @param task_event    [收到的本任务事件]* @return uint16       [未处理的事件]*/
uint16 statistics_task_event_process(uint8 task_id, uint16 task_event)
{if(task_event & SYS_EVENT_MSG)       //判断是否为系统消息事件{osal_sys_msg_t *msg_pkt;msg_pkt = (osal_sys_msg_t *)osal_msg_receive(task_id);      //从消息队列获取一条消息while(msg_pkt){switch(msg_pkt->hdr.event)      //判断该消息事件类型{case PRINTF_STATISTICS:{int count = *(int*)(((general_msg_data_t*)msg_pkt)->data);printf("Statistics task receive print task printf count : %d\n", count);break;}default:break;}osal_msg_deallocate((uint8 *)msg_pkt);                  //释放消息内存msg_pkt = (osal_sys_msg_t *)osal_msg_receive(task_id);  //读取下一条消息}// return unprocessed eventsreturn (task_event ^ SYS_EVENT_MSG);}return 0;
}

2.3 源码链接地址

OSAL github 链接地址

相关文章:

【事件驱动框架OSAL】二.消息的管理机制

OSAL消息管理机制 二、消息管理机制2.1 消息的数据结构2.2 消息内存分配2.3 消息的接收和销毁2.3 源码链接地址 二、消息管理机制 在上一篇文中提到,系统消息事件(SYS_EVENT_MSG)用于任务间传递数据,而消息队列是这种机制的基础&…...

《论多源数据集成及应用》审题技巧 - 系统架构设计师

论多源数据集成及应用写作框架 一、考点概述 本论题“论多源数据集成及应用”主要考察的是计算机软件测试工程师在数据管理和集成方面的专业知识与实践能力。论题聚焦于信息爆炸时代企业、组织和个人所面临的数据挑战,特别是如何有效地收集、整理和清洗来自不同渠…...

【企业微信开发工具,获取位置】

微信开发者工具获取位置失败 报错原因解决方案 报错原因 getLocation:fail, the permission value is offline verifying解决方案 在开发工具栏输入链接,进行位置获取获取成功后,重新进入调用获取位置的页面即可如下图:...

HTML之JavaScript DOM编程获取元素的方式

HTML之JavaScript DOM编程获取元素的方式 1.获得document DOM树window.document(是window的属性)2.从document中获取要操作的元素1.直接获取var aaa document.getElementById("username") // 根据元素的id值获取页面上的唯一一个元素,有同名的则返回找到的第一个var…...

如何安装vm和centos

以下是在VMware中安装CentOS的一般步骤: 一、安装VMware 以下是在 Windows 系统中安装 VMware 软件的详细步骤: 1. 下载 VMware 软件: - 访问 VMware 官方网站(https://www.vmware.com/)。 - 根据您的操作系统选择合…...

docker 安装redis 7.4.2并挂载配置文件以及设置密码

文章目录 docker 安装redis 7.4.2下载 redis如果你喜欢使用最新的版本创建挂载redis 配置文件创建容器 docker 安装redis 7.4.2 截至2025年2月21日,Redis的最新稳定版本是 7.4.2。 下载 redis 如果你想拉取Redis的特定版本(例如最新的稳定版本 7.4.2&a…...

计算机毕业设计SpringBoot+Vue.js在线教育系统(源码+文档+PPT+讲解)

温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...

Linux-C-函数栈-SP寄存器

sp(Stack Pointer,栈指针)是计算机体系结构中一个非常重要的寄存器,下面将详细介绍其作用和原理。 作用 1. 管理栈内存 栈是一种后进先出(LIFO,Last In First Out)的数据结构,在程…...

vi的基本使用

vi 是 Unix/Linux 系统中最常用的文本编辑器之一,功能强大但学习曲线较陡。以下是 vi 的基本使用方法: --- ### **1. vi 的两种模式** - **命令模式(Command Mode)**: - 默认进入的模式,用于执行命令&a…...

clickhouse--表引擎的使用

表引擎决定了如何存储表的数据。包括: 数据的存储方式和位置,写到哪里以及从哪里读取数据。(默认是在安装路径下的 data 路径)支持哪些查询以及如何支持。(有些语法只有在特定的引擎下才能用)并发数据访问。索引的使用&#xff0…...

LeetCode刷题零碎知识点整理

系列博客目录 文章目录 系列博客目录 数组变量有length属性&#xff0c;String类的对象有length()方法。String s; s.split("\\s");不能去除头部空格&#xff0c;需要使用s s.trim();String类的对象有toCharArray()方法&#xff0c;List<>类型有toArray()方法…...

GLTFLoader.js和OrbitControls.js两个 JavaScript 文件都是 Three.js 生态系统中的重要组成部分

GLTFLoader.js和OrbitControls.js两个 JavaScript 文件都是 Three.js 生态系统中的重要组成部分&#xff1a; 1. GLTFLoader.js 作用 GLTFLoader.js 是 Three.js 库中的一个辅助加载器脚本&#xff0c;其主要功能是加载 GLB 或 GLTF 格式的 3D 模型。GLTF&#xff08;GL Tra…...

大厂数据仓库数仓建模面试题及参考答案

目录 什么是数据仓库,和数据库有什么区别? 数据仓库的基本原理是什么? 数据仓库架构是怎样的? 数据仓库分层(层级划分),每层做什么?分层的好处是什么?数据分层是根据什么?数仓分层的原则与思路是什么? 数仓建模常用模型有哪些?区别、优缺点是什么?星型模型和雪…...

angular简易计算器

说明&#xff1a; 用angular实现计算器效果&#xff0c;ui风格为暗黑 效果图&#xff1a; step1: C:\Users\Administrator\WebstormProjects\untitled4\src\app\calnum\calnum.component.ts import { Component } from angular/core;Component({selector: app-calnum,import…...

谈谈 ES 6.8 到 7.10 的功能变迁(3)- 查询方法篇

上一篇咱们了解了 ES 7.10 相较于 ES 6.8 新增的字段类型&#xff0c;这一篇我们继续了解新增的查询方法。 Interval 间隔查询&#xff1a; 功能介绍 Interval 查询&#xff0c;词项间距查询&#xff0c;可以根据匹配词项的顺序、间距和接近度对文档进行排名。主要解决的查询…...

16、Python面试题解析:python中的浅拷贝和深拷贝

在 Python 中&#xff0c;浅拷贝&#xff08;Shallow Copy&#xff09; 和 深拷贝&#xff08;Deep Copy&#xff09; 是处理对象复制的两种重要机制&#xff0c;它们的区别主要体现在对嵌套对象的处理方式上。以下是详细解析&#xff1a; 1. 浅拷贝&#xff08;Shallow Copy&a…...

游戏引擎学习第119天

仓库:https://gitee.com/mrxiao_com/2d_game_3 上一集回顾和今天的议程 如果你们还记得昨天的进展&#xff0c;我们刚刚完成了优化工作&#xff0c;目标是让某个程序能够尽可能快速地运行。我觉得现在可以说它已经快速运行了。虽然可能还没有达到最快的速度&#xff0c;但我们…...

爬虫解析库:Beautiful Soup的详细使用

文章目录 1. 安装 Beautiful Soup2. 基本用法3. 选择元素4. 提取数据5. 遍历元素6. 修改元素7. 搜索元素8. 结合 requests 使用9. 示例&#xff1a;抓取并解析网页10. 注意事项 Beautiful Soup 是一个用于解析 HTML 和 XML 文档的 Python 库&#xff0c;它提供了简单易用的 API…...

OpenHarmony-4.基于dayu800 GPIO 实践(2)

基于dayu800 GPIO 进行开发 1.DAYU800开发板硬件接口 LicheePi 4A 板载 2x10pin 插针&#xff0c;其中有 16 个原生 IO&#xff0c;包括 6 个普通 IO&#xff0c;3 对串口&#xff0c;一个 SPI。TH1520 SOC 具有4个GPIO bank&#xff0c;每个bank最大有32个IO&#xff1a;  …...

【C++设计模式】观察者模式(1/2):从基础到优化实现

1. 引言 在 C 软件与设计系列课程中&#xff0c;观察者模式是一个重要的设计模式。本系列课程旨在深入探讨该模式的实现与优化。在之前的课程里&#xff0c;我们已对观察者模式有了初步认识&#xff0c;本次将在前两次课程的基础上&#xff0c;进一步深入研究&#xff0c;着重…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

Xen Server服务器释放磁盘空间

disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...