μC/OS-Ⅱ源码学习(3)---事件模型
快速回顾
μC/OS-Ⅱ中的多任务
μC/OS-Ⅱ源码学习(1)---多任务系统的实现
μC/OS-Ⅱ源码学习(2)---多任务系统的实现(下)
本文开始,进入事件源码的学习。
事件模型
在一个多任务系统里,各个任务在系统的统筹下相继执行,由于执行速度极快,就好像在一段时间内同时执行多个任务的代码一样,在更高级复杂的操作系统里,这叫做并发。
如果各个任务相互独立,没有资源的依赖和耦合,就可以凭借优先级的来决定先后执行的顺序,这是一种简单的多任务系统模型。但对于一块单片机来说,片上的资源(内存以及各种外设)是紧缺的,在完成需求的前提,可用的资源余量通常不多,这还只是平均余量,在复杂的应用场景下可能接近满载,此时各个任务就不能随心所欲的使用资源了,而是要按顺序依次排队等候使用(道理就是,即便空载也要保持这种编写习惯)。
这种资源不仅是指硬件外设的使用,还体现在内存资源的使用上,一个简单的例子,当我们已经打开了某个文件时,若此时尝试在修改该文件的名称,会弹窗警示我们”该文件已被打开,请先关闭“。这是出于安全考虑,不希望多个进程同时对一个资源进行修改,而要有顺序地获取控制权。
另一方面,各个任务之间还存在依赖关系,比如一个简单的热水器,包含ADC采样计算和PID逻辑输出两个任务(实际可能一个任务就行,这里仅作说明需要),那么后者一定会依赖前者提供实时数据进行新的运算,否则只能延续之前的输出。
再比如一些任务,需要外设完成工作后产生中断,从而告知任务的运行(不能在中断内大量处理应用逻辑),这也是一种事件。
从上面的各种案例场景分析,就知道需要各种各样的事件来协调大家的运行,才能在多任务环境下有条不紊的先后执行,减少应用出错的概率。在μC/OSⅡ中提供了五种不同的事件(EVENT)供用户使用(我归类为广义事件。另有系统定时器类型,但不归为事件):
//ucos_ii.h
#define OS_EVENT_TYPE_UNUSED 0u
#define OS_EVENT_TYPE_MBOX 1u //邮箱
#define OS_EVENT_TYPE_Q 2u //队列
#define OS_EVENT_TYPE_SEM 3u //信号量
#define OS_EVENT_TYPE_MUTEX 4u //互斥信号量
#define OS_EVENT_TYPE_FLAG 5u //事件标志组
#define OS_TMR_TYPE 100u //定时器
事件有专门的事件控制块记录信息,其中邮箱(MBOX)、队列(Q)、信号量(SEM)、互斥信号量(MUTEX)为狭义事件,其事件控制块类型为OS_EVENT,队列还有额外的控制块OS_Q。事件标志组(FLAG)则是一类特殊的事件,有自己的控制块类型OS_FLAG_GRP。
它们分别装载在下面的全局变量中:
//ucos_ii.h
#if (OS_EVENT_EN) && (OS_MAX_EVENTS > 0u)
OS_EXT OS_EVENT *OSEventFreeList; /* 空白事件控制块链表 */
OS_EXT OS_EVENT OSEventTbl[OS_MAX_EVENTS]; /* 事件控制块数组 */
#endif#if (OS_Q_EN > 0u) && (OS_MAX_QS > 0u)
OS_EXT OS_Q *OSQFreeList; /* 空白队列控制块链表 */
OS_EXT OS_Q OSQTbl[OS_MAX_QS]; /* 队列控制块数组 */
#endif#if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)
OS_EXT OS_FLAG_GRP OSFlagTbl[OS_MAX_FLAGS]; /* 事件标志组数组 */
OS_EXT OS_FLAG_GRP *OSFlagFreeList; /* 空白的事件标志组链表 */
#endif
由OS_EVENT_EN的定义也可以对事件进行分类了:
#define OS_EVENT_EN (((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)) || (OS_MBOX_EN > 0u) || (OS_SEM_EN > 0u) || (OS_MUTEX_EN > 0u))
事件控制块类型
从上一节描述可知,μC/OSⅡ共有三种事件控制块,分别是OS_EVENT、OS_Q、OS_FLAG_GRP,其中OS_EVENT是用的最多的,这里先以它为例解读,后续讲到对应事件时再解析其它两种。
//ucos_ii.h
typedef struct os_event {INT8U OSEventType; /* 事件类型,有六种(其中一种是UNUSED) */void *OSEventPtr; /* OSEventPtr是一个多用途的指针,当作为链表时,可以指向下一个控制块;当作为具体的事件控制块时,指向具体的事件结构,如OS_Q */ INT16U OSEventCnt; /* 信号量计数器,其它事件类型不使用该成员 */OS_PRIO OSEventGrp; /* 等待信号的任务优先级组 */OS_PRIO OSEventTbl[OS_EVENT_TBL_SIZE]; /* 等待信号的组内优先级 */#if OS_EVENT_NAME_EN > 0uINT8U *OSEventName; //事件名称
#endif
} OS_EVENT;
其中,成员OSEventGrp和OSEventTbl[OS_EVENT_TBL_SIZE]和多任务的就绪优先级逻辑类似,都是为了快速定位到最高优先级的就绪任务,为了之后对比和区分,之前多任务的部分我们称为”优先级就绪表“,在这里我们可以为其取名为”事件等待表“。
事件的初始化
在执行OSInit()时,会对事件链表进行初始化,与任务控制块不同的是,事件链表为单相链表,具体函数为OS_InitEventList():
//os_core.c
static void OS_InitEventList (void)
{
#if (OS_EVENT_EN) && (OS_MAX_EVENTS > 0u)
#if (OS_MAX_EVENTS > 1u)INT16U ix;INT16U ix_next;OS_EVENT *pevent1;OS_EVENT *pevent2;OS_MemClr((INT8U *)&OSEventTbl[0], sizeof(OSEventTbl)); /* 清空事件控制块数组 */for (ix = 0u; ix < (OS_MAX_EVENTS - 1u); ix++) { /* 将OSEventTbl数组元素组成链表 */ix_next = ix + 1u;pevent1 = &OSEventTbl[ix];pevent2 = &OSEventTbl[ix_next];pevent1->OSEventType = OS_EVENT_TYPE_UNUSED; //初始化事件类型为UNUSEDpevent1->OSEventPtr = pevent2;
#if OS_EVENT_NAME_EN > 0upevent1->OSEventName = (INT8U *)(void *)"?"; /* 初始化事件名称"?" */
#endif}pevent1 = &OSEventTbl[ix];pevent1->OSEventType = OS_EVENT_TYPE_UNUSED;pevent1->OSEventPtr = (OS_EVENT *)0; //链表尾的下一个指针指向0
#if OS_EVENT_NAME_EN > 0upevent1->OSEventName = (INT8U *)(void *)"?"; /* Unknown name */
#endifOSEventFreeList = &OSEventTbl[0]; //将空白链表头指向刚创建的链表
#elseOSEventFreeList = &OSEventTbl[0]; /* 只定义一个事件时,则链表也只有一个元素 */OSEventFreeList->OSEventType = OS_EVENT_TYPE_UNUSED;OSEventFreeList->OSEventPtr = (OS_EVENT *)0;
#if OS_EVENT_NAME_EN > 0uOSEventFreeList->OSEventName = (INT8U *)"?"; /* 初始化事件名称"?" */
#endif
#endif
#endif
}
还有事件标志组的初始化函数OS_FlagInit(),其内容和OS_InitEventList()基本一致,只是操作对象变了:
//os_flag.c
void OS_FlagInit (void)
{
#if OS_MAX_FLAGS == 1u /* 只设定一个事件标志组,则只需将OSFlagTbl首元素当作链表头 */OSFlagFreeList = (OS_FLAG_GRP *)&OSFlagTbl[0];OSFlagFreeList->OSFlagType = OS_EVENT_TYPE_UNUSED; //事件类型初始化为UNUSEDOSFlagFreeList->OSFlagWaitList = (void *)0;OSFlagFreeList->OSFlagFlags = (OS_FLAGS)0;
#if OS_FLAG_NAME_EN > 0uOSFlagFreeList->OSFlagName = (INT8U *)"?";
#endif
#endif#if OS_MAX_FLAGS >= 2uINT16U ix;INT16U ix_next;OS_FLAG_GRP *pgrp1;OS_FLAG_GRP *pgrp2;OS_MemClr((INT8U *)&OSFlagTbl[0], sizeof(OSFlagTbl)); /* 清空事件标志组控制块 */for (ix = 0u; ix < (OS_MAX_FLAGS - 1u); ix++) { /* 初始化控制块并组成链表结构 */ix_next = ix + 1u;pgrp1 = &OSFlagTbl[ix];pgrp2 = &OSFlagTbl[ix_next];pgrp1->OSFlagType = OS_EVENT_TYPE_UNUSED; //事件类型初始化为UNUSEDpgrp1->OSFlagWaitList = (void *)pgrp2;
#if OS_FLAG_NAME_EN > 0upgrp1->OSFlagName = (INT8U *)(void *)"?"; /* 事件标志组名称 */
#endif}pgrp1 = &OSFlagTbl[ix]; pgrp1->OSFlagType = OS_EVENT_TYPE_UNUSED;pgrp1->OSFlagWaitList = (void *)0; //最后一个元素的下一个指针指向0
#if OS_FLAG_NAME_EN > 0upgrp1->OSFlagName = (INT8U *)(void *)"?";
#endifOSFlagFreeList = &OSFlagTbl[0];
#endif
除此之外,还有队列控制块的初始化OS_QInit():
//os_q.c
void OS_QInit (void)
{
#if OS_MAX_QS == 1u /* 只设定一个队列时 */OSQFreeList = &OSQTbl[0]; OSQFreeList->OSQPtr = (OS_Q *)0;
#endif#if OS_MAX_QS >= 2uINT16U ix;INT16U ix_next;OS_Q *pq1;OS_Q *pq2;OS_MemClr((INT8U *)&OSQTbl[0], sizeof(OSQTbl)); /* 清空队列控制块 */for (ix = 0u; ix < (OS_MAX_QS - 1u); ix++) { /* 初始化队列控制块,并组成链表 */ix_next = ix + 1u;pq1 = &OSQTbl[ix];pq2 = &OSQTbl[ix_next];pq1->OSQPtr = pq2;}pq1 = &OSQTbl[ix];pq1->OSQPtr = (OS_Q *)0;OSQFreeList = &OSQTbl[0];
#endif
}
接下来将具体解析各种事件的生命周期函数源码。
相关文章:
μC/OS-Ⅱ源码学习(3)---事件模型
快速回顾 μC/OS-Ⅱ中的多任务 μC/OS-Ⅱ源码学习(1)---多任务系统的实现 μC/OS-Ⅱ源码学习(2)---多任务系统的实现(下) 本文开始,进入事件源码的学习。 事件模型 在一个多任务系统里,各个任务在系统的统筹下相继执行,由于执行速度极快&a…...

Jmeter进阶篇(30)深入探索 JMeter 监听器
前言 在性能测试领域里,Apache JMeter 是一款经典而强大的工具,而其中的监听器(Listeners)组件更是发挥着不可或缺的关键作用。 监听器就像敏锐的观察者,默默记录测试执行过程中的各种数据,作为系统性能分析的数据依据。 本文将带你全方位走进 JMeter 监听器的奇妙世界,…...
虚幻引擎的工程目录结构
虚幻引擎的工程目录结构如下: .idea/.vs:用于IDE(如IntelliJ IDEA或Visual Studio)的项目配置文件,包含工程设置和解决方案文件。 Binaries:存放编译后的可执行文件和相关的动态链接库(DLL&…...
深度学习中的yield
以下为例: def data_iter(batch_size, features, labels):num_examples len(features)indices list(range(num_examples))# 这些样本是随机读取的,没有特定的顺序random.shuffle(indices)for i in range(0, num_examples, batch_size):batch_indices …...

数据库数据恢复—ORACLE常见故障有哪些?如何恢复数据?
Oracle数据库常见故障表现: 1、ORACLE数据库无法启动或无法正常工作。 2、ORACLE ASM存储破坏。 3、ORACLE数据文件丢失。 4、ORACLE数据文件部分损坏。 5、ORACLE DUMP文件损坏。 Oracle数据库数据恢复方案: 1、检测存放数据库的服务器/存储设备是否存…...

使用JavaScrip和HTML搭建一个简单的博客网站系统
搭建一个简单的博客网站系统,我们需要创建几个基本的页面和功能:登录、注册、文章发布等。这里我们先实现一个基础版本,包括用户登录、注册以及文章发布的功能。由于这是一个简化版的示例,我们将所有逻辑集成在一个HTML文件中&…...

算法-字符串-76.最小覆盖子串
一、题目 二、思路解析 1.思路: 滑动窗口!!! 2.常用方法: 无 3.核心逻辑: 1.特殊情况:s或t是否为空字符串 if(snull||tnull)return ""; 2.声明一个字符数组——用于记录对应字符出现…...

Python爬虫之Selenium的应用
【1】Selenium基础介绍 1.什么是selenium? (1)Selenium是一个用于Web应用程序测试的工具。 (2)Selenium 测试直接运行在浏览器中,就像真正的用户在操作一样。 (3)支持通过各种driv…...

粉丝生产力与开源 AI 智能名片 2+1 链动模式商城小程序的融合创新与价值拓展
摘要:本文聚焦于粉丝生产力在当代文化与商业语境中的独特作用,并深入探讨其与开源 AI 智能名片 21 链动模式商城小程序的有机结合。通过剖析粉丝生产力的多元表现形式、内在驱动机制以及开源 AI 智能名片 21 链动模式商城小程序的功能特性与商业潜力&…...

红黑树(Red-Black Tree)
一、概念 红黑树(Red Black Tree)是一种自平衡的二叉搜索树,通过添加颜色信息来确保在进行插入和删除操作时,树的高度保持在对数级别,从而保证了查找、插入和删除操作的时间复杂度为 O(log n)。这种树可以很好地解决普…...

Cocos 资源加载(以Json为例)
resources 通常我们会把项目中需要动态加载的资源放在 resources 目录下,配合 resources.load 等接口动态加载。你只要传入相对 resources 的路径即可,并且路径的结尾处 不能 包含文件扩展名。 resources.load("Inf", JsonAsset, (error, ass…...

解决 IntelliJ IDEA 启动错误:插件冲突处理
引言 在使用 IntelliJ IDEA 进行开发时,我们可能会遇到各种启动错误。本文将详细介绍一种常见的错误:插件冲突,并提供解决方案。 错误背景 最近,有用户在启动 IntelliJ IDEA 时遇到了一个错误,提示信息为:…...

SQL——DQL分组聚合
分组聚合: 格式: select 聚合函数1(聚合的列),聚合函数2(聚合的列) from 表名 group by 标识列; ###若想方便分辨聚合后数据可在聚合函数前加上标识列(以标识列进行分组) 常见的聚合函数: sum(列名):求和函数 avg(列名)…...

Ripro V5日主题 v8.3 开心授权版 wordpress主题虚拟资源下载站首选主题模板
RiPro主题全新V5版本,是一个优秀且功能强大、易于管理、现代化的WordPress虚拟资源商城主题。支持首页模块化布局和WP原生小工具模块化首页可拖拽设置,让您的网站设计体验更加舒适。同时支持了高级筛选、自带会员生态系统、超全支付接口等众多功能&#…...

分布式搜索引擎之elasticsearch基本使用2
分布式搜索引擎之elasticsearch基本使用2 在分布式搜索引擎之elasticsearch基本使用1中,我们已经导入了大量数据到elasticsearch中,实现了elasticsearch的数据存储功能。但elasticsearch最擅长的还是搜索和数据分析。 所以j接下来,我们研究下…...
java学习-第十五章-IO流(java.io包中)
一、理解 1. 简单而言:流就是内存与存储设备之间传输数据的通道、管道。 2. 分类: (1) 按方向(以JVM虚拟机为参照物)【重点】 输入流:将中的内容读入到中。 输出流:将中的内容写入到中。 (2) 按单位: 字节流…...

企业如何实现数据从源端到消费端的全链路加工逻辑可视化?
要想实现数据加工链路的可视化,血缘图谱无疑是一个有效的工具。血缘图谱能够清晰地展示数据从产生、流转、加工到最终消费的每一个环节,帮助企业直观地理解数据之间的关联和依赖关系,轻松追溯数据来源和去向,并在数据出现问题时快…...

Toxicity of the Commons: Curating Open-Source Pre-Training Data
基本信息 📝 原文链接: https://arxiv.org/abs/2410.22587👥 作者: Catherine Arnett, Eliot Jones, Ivan P. Yamshchikov, Pierre-Carl Langlais🏷️ 关键词: toxicity filtering, language models, data curation📚 分类: 机器…...
Python 单例模式工厂模式和classmethod装饰器
前言: Python作为面向对象的语言,显然支持基本的设计模式。也具备面向对象的语言的基本封装方法:属性、方法、继承、多态等。但是,做为强大的和逐渐发展的语言,python也有很多高级的变种方法,以适应更多的…...

计算机键盘简史 | 键盘按键功能和指法
注:本篇为 “计算机键盘简史 | 键盘按键功能和指法” 相关文章合辑。 英文部分机翻未校。 The Evolution of Keyboards: From Typewriters to Tech Marvels 键盘的演变:从打字机到技术奇迹 Introduction 介绍 The keyboard has journeyed from a humb…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...

srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...

Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...