手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的任务调度策略(二)

本文主要是介绍手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的任务调度策略(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

继续......

if (ticks > 0u) {                            /* 延时参数是否为0 */OS_ENTER_CRITICAL();            /* 禁止中断 */y            =  OSTCBCur->OSTCBY;OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;if (OSRdyTbl[y] == 0u) {OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;}OSTCBCur->OSTCBDly = ticks;OS_EXIT_CRITICAL();              /* 开启中断 */OS_Sched();}

在上一段代码中,出现了一个陌生的数组: OSRdyTbl[],跟踪这个变量可以找到它的定义,发现它仅仅是一个uint8型的数组,长度是8个。可以明确的告诉大家,这个数组很重要,应该算是任务优先级调度核心参数之一,与下面那个参数OSRdyGrp 合起来便可以作为任务就绪表。

※接下来需要讲UCOSII系统的任务优先级调度策略,这一段有些复杂,需要反复思考,查阅大量的资料。

UCOSII的优先级策略

UCOSII操作系统最大可以管理64个任务(255个的暂时不讨论),每个任务都有唯一的优先级,从0开始到64,数字越小优先级越高,越优先进行系统调用,为了方面管理和调度,系统把64任务的优先级进行了分组。每8个优先级为一组,一共8组。

举例:以前我上幼儿园的时候,我们小班有64个小朋友,每个小朋友都有自己的学号(从0到63),老师为了方面管理,把我们分成了8个小组(从0到7),每组8个小朋友……作为小朋友的我来说,当然不理解这样分组的意义,不过对于老师而言,这样做肯定是有用的。

下面是分组以后小朋友的坐席表:

当有一个名叫波波的小朋友的学号是12的时候,那他属于哪个组?看下图,当然是第1组,而且我的座位号排第5(8,9,10,11,12)

当一个任务的优先级确定了以后,那么它的组号和在组内的坐席号都是确定的,是永远不可能发生改变的,所以在任务创建之时,这些信息都可以完全决定下来,现在再回想那两句代码,意义是不是瞬间就明白了?

当一个任务被创建以后,他所属的组号必然就等于优先级的二级制的高3位,比如……下图:

根据上面那个图,那么这两句代码的意义也就很清楚了:

ptcb->OSTCBY             = (INT8U)(prio >> 3u);
ptcb->OSTCBX             = (INT8U)(prio & 0x07u);

就是根据任务的优先级,计算出组号和组内编号。

至于这两个数据有什么用,分组出来有什么意义?请在小朋友波波的带领下继续往下看。

刚才那个陌生的数组: OSRdyTbl[8]之所以被称为任务就绪表,是因为它里面保存的是当前所有任务的就绪状态,它的长度是8,每一个元素代表一个组,比如 OSRdyTbl[0]代表第0组, OSRdyTbl[1]代表第1组,OSRdyTbl[2]代表第2组……以此类推。由于它的数据类型是数据uint8,所以每一个元素中的每一个位(bit)代表组内的任务的就绪状态(1为就绪,0为未就绪)。

举例:

比如,当优先级为12 的任务就绪时,那么对应的OSRdyTbl[1]的第4位bit,绝对等于1……当整个系统中,当只有优先级为12的任务就绪,其他所有任务都没有就绪时,那么OSRdyTbl[1] 绝对等于0x10。

  再比如,当优先级为0 的任务就绪时,那么对应的OSRdyTbl[0]的第0位bit,绝对等于1……当整个系统中,当只有优先级为0的任务就绪,其他所有任务都没有就绪时,那么OSRdyTbl[0] 绝对等于0x01。

       再再比如,当优先级为63 的任务就绪时,那么对应的OSRdyTbl[7]的第8位bit,绝对等于1……当整个系统中,当只有优先级为63的任务就绪,其他所有任务都没有就绪时,那么OSRdyTbl[7] 绝对等于0x80。

       再再再比如,当优先级为0和1的任务就绪时,那么对应的OSRdyTbl[0]的第0位bit以及第1位bit,都绝对等于1……当整个系统中,当只有优先级为0和1的任务就绪,其他所有任务都没有就绪时,那么OSRdyTbl[0] 绝对等于0x03。

小朋友们,现在理解了吗?上面的话要认真理解,理解完后才可以继续往下看。

当一个任务进入延时函数后,这个任务首先要暂停/休眠(把自己的就绪状态取消),再要把CPU的执行权交给别的任务(把别的任务设置为就绪),这个过程也就是任务的切换。

然后现在我们可以继续读代码了:

if (ticks > 0u) {                            /* 0 means no delay!                                  */OS_ENTER_CRITICAL();y            =  OSTCBCur->OSTCBY;        /* Delay current task                                 */OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;if (OSRdyTbl[y] == 0u) {OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;}OSTCBCur->OSTCBDly = ticks;              /* Load ticks in TCB                                  */OS_EXIT_CRITICAL();OS_Sched();                              /* Find next task to run!                             */}

红色的那句代码所表达的意思,就是把当前任务的就绪表从1设置为0,从而使自身进入休眠状态。

当优先级为12的任务进入此地,那么OSTCBCur->OSTCBY和y必然等于1,OSTCBCur->OSTCBBitX必然等于0x10(他的座位号是第4),OSRdyTbl[1]在执行红色代码之前,肯定是等于0x10(假如当前只有12这一个就绪任务)。经过这样一个取反在相与的计算,直接就把OSRdyTbl[1]的第4位bit的清空了,也就是把就绪状态,设置成了未就绪状态。

if (ticks > 0u) {                            /* 0 means no delay!                                  */OS_ENTER_CRITICAL();y            =  OSTCBCur->OSTCBY;        /* Delay current task                                 */OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;if (OSRdyTbl[y] == 0u) {OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;}OSTCBCur->OSTCBDly = ticks;              /* Load ticks in TCB                                  */OS_EXIT_CRITICAL();OS_Sched();                              /* Find next task to run!                             */}

这一句代码,表示什么意思呢?

  OSRdyTbl[]数组中的每个元素都代表了8个优先级任务的状态,按照字面意思解读,如果OSRdyTbl[x] == 0x00  (二进制00000000),那么就表示,当前这个组里面,没有任何就绪的任务。

  跟踪变量OSRdyGrp ,可以找到它就是一个uint8型的数据,那么OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;这代码本身的意思无需多言,也就是清空这个数据的某个bit。

  OSRdyGrp 这个变量的作用是管理任务就绪组的组号,原理和OSRdyTbl[]数组差不多,这个拥有8个bit的变量,每一个bit代表一个组,只要这个组内有任何一个任务就绪,那么这个变量对应的bit位,就会置为1,只有该组内所有的任务都是未就绪的状态,对应的那个bit才会被清零,逻辑算法上是或的关系。

举例:

比如,系统中只有任务0就绪了,那么OSRdyGrp 便等于 0x01(二进制00000001)。

比如,系统中只有任务12就绪了,那么OSRdyGrp 便等于 0x02(二进制00000010)。

  比如,系统中只有任务63就绪了,那么OSRdyGrp 便等于 0x80(二进制10000000)。

       比如,系统中有任务0和任务1都就绪了,那么OSRdyGrp 便等于 0x01(二进制00000001)。

  比如,系统中有任务0和任务63都就绪了,那么OSRdyGrp 便等于 0x81(二进制10000001)。

回到代码,因为同一个组内的所有任务都是逻辑或的关系,所以在同一个组内的所有任务都没有就绪的情况下,才能把这个变量的对应bit位清空,只要有一个任务就绪,哪怕其余7个任务都没有就绪,也不能清空,这个if实现的就是这个功能。

总结一下这四个变量的意义:

ptcb->OSTCBY    :当前任务优先级所属于的分组(0~7),比如优先级为12的任务,这个变量应该就是1

ptcb->OSTCBX    :当前任务优先级在组内的序号,比如优先级为12的任务,这个变量应该就是4

ptcb->OSTCBBitY     :用于逻辑运算的二进制变量,当前任务优先级所属于的组号,在变量OSRdyGrp 中的所占的bit的偏移,比如优先级为12的任务,组号排在第一个位置,那么这个变量应该就是二进制00000010

ptcb->OSTCBBitX     :用于逻辑运算的二进制变量,当前任务优先级在组内的序号所占的bit偏移,比如优先级为12的任务,排在第五个位置,因此这个变量应该就是二进制00010000

现在理解这些变量的意义了吗?

if (ticks > 0u) {                            /* 0 means no delay!                                  */OS_ENTER_CRITICAL();y            =  OSTCBCur->OSTCBY;        /* Delay current task                                 */OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;if (OSRdyTbl[y] == 0u) {OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;}OSTCBCur->OSTCBDly = ticks;              /* Load ticks in TCB                                  */OS_EXIT_CRITICAL();OS_Sched();                              /* Find next task to run!                             */}

上面那一句代码的意思是,记录当前任务需要的延时时间(后面讲具体的实现机制),比如当我调用delay_ms(5000),那样记录的就是5000,但我调用delay_ms(100)记录的就是100,只不过单位可能不同,我这里写的毫秒,是因为我在系统的配置文件中把系统的节拍设置成了毫秒:

#define OS_TICKS_PER_SEC           1000u   /* Set the number of ticks in one second                        */                    

这个宏定义系统的滴答定时器,每一秒钟发生多少个节拍,我定义的是1000,所以每个节拍就是1ms,因此延时函数也是这个级别(其实这样不太好,因为节拍太密集,系统的负荷会很重,实际肯定会根据需要调整成稍微大一些的数)。

以上,进入延时函数的任务切换中,把当前任务的就绪状态设置为未就绪的流程就讲解完毕了,接下来讲解如何唤醒一个新的任务。

待续......

这篇关于手把手,嘴对嘴,讲解UCOSII嵌入式操作系统的任务调度策略(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/294307

相关文章

Python 中 requests 与 aiohttp 在实际项目中的选择策略详解

《Python中requests与aiohttp在实际项目中的选择策略详解》本文主要介绍了Python爬虫开发中常用的两个库requests和aiohttp的使用方法及其区别,通过实际项目案... 目录一、requests 库二、aiohttp 库三、requests 和 aiohttp 的比较四、requ

Redis过期键删除策略解读

《Redis过期键删除策略解读》Redis通过惰性删除策略和定期删除策略来管理过期键,惰性删除策略在键被访问时检查是否过期并删除,节省CPU开销但可能导致过期键滞留,定期删除策略定期扫描并删除过期键,... 目录1.Redis使用两种不同的策略来删除过期键,分别是惰性删除策略和定期删除策略1.1惰性删除策略

手把手教你idea中创建一个javaweb(webapp)项目详细图文教程

《手把手教你idea中创建一个javaweb(webapp)项目详细图文教程》:本文主要介绍如何使用IntelliJIDEA创建一个Maven项目,并配置Tomcat服务器进行运行,过程包括创建... 1.启动idea2.创建项目模板点击项目-新建项目-选择maven,显示如下页面输入项目名称,选择

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

缓存策略使用总结

缓存是提高系统性能的最简单方法之一。相对而言,数据库(or NoSQL数据库)的速度比较慢,而速度却又是致胜的关键。 如果使用得当,缓存可以减少相应时间、减少数据库负载以及节省成本。本文罗列了几种缓存策略,选择正确的一种会有很大的不同。缓存策略取决于数据和数据访问模式。换句话说,数据是如何写和读的。例如: 系统是写多读少的吗?(例如基于时间的日志)数据是否是只写入一次并被读取多次?(例如用户配

Flink任务重启策略

概述 Flink支持不同的重启策略,以在故障发生时控制作业如何重启集群在启动时会伴随一个默认的重启策略,在没有定义具体重启策略时会使用该默认策略。如果在工作提交时指定了一个重启策略,该策略会覆盖集群的默认策略默认的重启策略可以通过 Flink 的配置文件 flink-conf.yaml 指定。配置参数 restart-strategy 定义了哪个策略被使用。常用的重启策略: 固定间隔 (Fixe

ispunct函数讲解 <ctype.h>头文件函数

目录 1.头文件函数 2.ispunct函数使用  小心!VS2022不可直接接触,否则..!没有这个必要,方源一把抓住VS2022,顷刻 炼化! 1.头文件函数 以上函数都需要包括头文件<ctype.h> ,其中包括 ispunct 函数 #include<ctype.h> 2.ispunct函数使用 简述: ispunct函数一种判断字符是否为标点符号的函

深度学习速通系列:深度学习算法讲解

深度学习算法是一系列基于人工神经网络的算法,它们通过模拟人脑处理信息的方式来学习和解决复杂问题。这些算法在图像识别、语音识别、自然语言处理、游戏等领域取得了显著的成就。以下是一些流行的深度学习算法及其基本原理: 1. 前馈神经网络(Feedforward Neural Networks, FNN) 原理:FNN 是最基本的神经网络结构,它由输入层、隐藏层和输出层组成。信息从输入层流向隐藏层,最

Java后端微服务架构下的API限流策略:Guava RateLimiter

Java后端微服务架构下的API限流策略:Guava RateLimiter 大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿! 在微服务架构中,API限流是保护服务不受过度使用和拒绝服务攻击的重要手段。Guava RateLimiter是Google开源的Java库中的一个组件,提供了简单易用的限流功能。 API限流概述 API限流通过控制请求的速率来防止