继续......

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!                             */}

依然是这一部分,接下来的重点是这个函数:OS_Sched()

这个函数实在是太重要了,因此我不得不慎重。

首先看一下官方的注释:

/*********************************************************************************************************
* SCHEDULER
*
* Description: This function is called by other uC/OS-II services to determine whether a new, high
* priority task has been made ready to run. This function is invoked by TASK level code
* and is not used to reschedule tasks from ISRs (see OSIntExit() for ISR rescheduling).
*
*********************************************************************************************************/

从上面的说明可以看出,这个函数的作用,主要是用来调度当前已经进入了就绪状态的最高优先级任务,然后切换进去。

函数定义如下:

void  OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3u                           /* Allocate storage for CPU status register     */OS_CPU_SR  cpu_sr = 0u;
#endifOS_ENTER_CRITICAL();if (OSIntNesting == 0u) {                          /* Schedule only if all ISRs done and ...       */if (OSLockNesting == 0u) {                     /* ... scheduler is not locked                  */OS_SchedNew();OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy     */
#if OS_TASK_PROFILE_EN > 0uOSTCBHighRdy->OSTCBCtxSwCtr++;         /* Inc. # of context switches to this task      */
#endifOSCtxSwCtr++;                          /* Increment context switch counter             */OS_TASK_SW();                          /* Perform a context switch                     */}}}OS_EXIT_CRITICAL();
}

关于任务调度的部分肯定是原子操作,不允许任何中断存在,因此必须要关闭中断。

其次,任务调度不能发生在中断中以及任务调度器上锁的情况,因此必须加以判定。

上面的内容比较简单,去掉那些判断和宏开关,我们需要关注的重点主要在以下的部分:

OS_SchedNew();
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
if (OSPrioHighRdy != OSPrioCur) {         /* No Ctx Sw if current task is highest rdy     */OS_TASK_SW();                          /* Perform a context switch                     */
}

首先看这个函数:OS_SchedNew()

函数定义如下:

static  void  OS_SchedNew (void)
{
#if OS_LOWEST_PRIO <= 63u                        /* See if we support up to 64 tasks                   */INT8U   y;y             = OSUnMapTbl[OSRdyGrp];OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);
#else                                            /* We support up to 256 tasks                         */INT8U     y;OS_PRIO  *ptbl;if ((OSRdyGrp & 0xFFu) != 0u) {y = OSUnMapTbl[OSRdyGrp & 0xFFu];} else {y = OSUnMapTbl[(OS_PRIO)(OSRdyGrp >> 8u) & 0xFFu] + 8u;}ptbl = &OSRdyTbl[y];if ((*ptbl & 0xFFu) != 0u) {OSPrioHighRdy = (INT8U)((y << 4u) + OSUnMapTbl[(*ptbl & 0xFFu)]);} else {OSPrioHighRdy = (INT8U)((y << 4u) + OSUnMapTbl[(OS_PRIO)(*ptbl >> 8u) & 0xFFu] + 8u);}
#endif
}

因为我们的系统最高支持64个任务,所以去掉那些我们不需要关注的地方,函数定义简化如下:

static  void  OS_SchedNew (void)
{INT8U   y;y  = OSUnMapTbl[OSRdyGrp];OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);
}

看着好像很简单,整个函数就两句话,但是,只要能把这两句话给弄明白了,关于调度的东西基本上就都没问题了。

关于变量OSRdyGrp与数组OSRdyTbl[]的意义,相信各位都已经十分理解,分别代表组就绪状态和任务就绪状态,那么新出来的这个数组OSUnMapTbl[]又代表什么呢?它和任务就绪表有什么关系?

跟踪OSUnMapTbl数组的定义可以发现,这是一个常数数组,它里面的内容是只读的,定义如下:

INT8U  const  OSUnMapTbl[256] = {0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x00 to 0x0F                   */4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x10 to 0x1F                   */5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x20 to 0x2F                   */4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x30 to 0x3F                   */6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x40 to 0x4F                   */4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x50 to 0x5F                   */5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x60 to 0x6F                   */4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x70 to 0x7F                   */7u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x80 to 0x8F                   */4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x90 to 0x9F                   */5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xA0 to 0xAF                   */4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xB0 to 0xBF                   */6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xC0 to 0xCF                   */4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xD0 to 0xDF                   */5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xE0 to 0xEF                   */4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u  /* 0xF0 to 0xFF                   */
};

这个表还不小,一眼看去脑袋都大了,它到底是个什么玩意儿?别慌,听我慢慢讲解……

依然举个例子,现在我们的系统只有两个任务(0和12),当前的任务优先级是0,然后这个任务进入了延时,这个时候根据前面了解的东西:

 y            =  OSTCBCur->OSTCBY;        /* Delay current task */                             OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;

这个优先级为0的任务已经被设置为了未就绪状态,也就是把它的就绪表清空了,对应的OSRdyTbl[0]肯定是0,由于只有两个任务,因此对应的OSRdyGrp的最后一个bit位,也肯定是0,。

然后我们还有一个优先级为12的任务已经准备就绪, 那么代码执行到了这里,任务的OSRdyTbl[1]必然等于0x10,组号OSRdyGrp必然等于0x02,把2带进这个常数表,得到的结果是:1

这个“1”有什么意义?

y             = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);

结论:这两句代码真正的功能,就是从系统中,把当前已经就绪了的任务里,优先级最高的那个任务给找出来,而这个任务也就是我接下来要切换进去的那一个。

假设现在我们系统中只有两个任务,一个优先级为0(未就绪),一个优先级为12(就绪),现在我们就来看看,他到底是怎么把12这个数据给找出来的。

上面说了,当一个任务的优先级是确定数的时候,他的组号、组内坐席号,偏移量等都是确定的。

当只有任务12就绪时,这个时候组号OSRdyGrp必然等于0x02,那么把它带入那个常数表中,得到结果y为1。

在把1带入数组OSRdyTbl[1]中,等到结果的结果是0x10(参考上一节),把0x10带入常数表,得到的结果OSUnMapTbl[OSRdyTbl[y]] == 4

这个y等于1,把它向左移动3个bit,得到的结果是8(二进制00001000)……最后的结果8 + 4 = 12。

没想到它真的把我需要的优先级给算出来了,到底是怎么做到的?

其实,UCOSII使用的这种方法被叫查表法,根据一定的规律,直接计算出当前优先级最高的那个任务号。

那个看似莫名其妙的常数表OSUnMapTbl,其实它所代表的意思,是0~255个数字中,1所在的最低位:

比如1,二进制00000001,在它的最低位出现了1,那么带入常数表一查,发现OSUnMapTbl[1] = 0,也就是第0位出现了1。

比如2,二进制00000010,在它的次低位出现了1,那么带入常数表一查,发现OSUnMapTbl[2] = 1,也就是第1位出现了1。

比如3,二进制00000011,在它的最低位出现了1,那么带入常数表一查,发现OSUnMapTbl[3] = 0,也就是第0位出现了1。

  ……

比如12,二进制00001100,在它的第2位出现了1,那么带入常数表一查,发现OSUnMapTbl[12] = 2,也就是第2位出现了1。

比如63,二进制011111111,在它的最低位出现了1,那么带入常数表一查,发现OSUnMapTbl[3] = 0,也就是第0位出现了1。

因为有了这个表,算法上才可能做到无论有多少个任务进入了就绪的状态,我都能轻轻松松地取出优先级最高的那一个,根据变量OSRdyGrp的状态,我可以找到那些组有就绪的任务,

如果第0组和第3组内都有就绪的任务,那么OSRdyGrp肯定等于0x05,但是我根本不用关心第3组的状态,忽略即可,因为第0组明显优先程度更大,我只需要继续前往第0组内寻找便可。

进入第0组内部后,假如优先级为1和优先级为5的任务都就绪了,变量OSRdyTbl[0] == 0x22(二进制00100010),我肯定要执行优先级为1的任务,那么我就直接把这个数据带进那个常数表中去查寻,看看最低位出现1的位置,也就是就绪的任务到底是哪一个,

只要找到了它,别的任务就算是就绪状态,我也不用管了……查表法便是基于这个原理。

y             = OSUnMapTbl[OSRdyGrp];
OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]);

再看一下这两句代码,第一句代码的意思是:找到就绪任务中优先级最高的组号,比如1组和3组都就绪了,我需要的结果是:1。

第二句代码中的这句话OSUnMapTbl[OSRdyTbl[y]]的意思是:找到在这个组中,优先级最高的任务的坐席号,也就是偏移量,比如任务12和任务13都就绪了,我需要的结果是:4(代表任务12的偏移)。

整个第二个代码的意思是:把组号和组内坐席号组合起来,形成最后的任务优先级。

如果想不明白的话,可以参考上一章:

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

这两句话的意思,在建立任务的时候,把一个好好的优先级给拆开,现在终于是把它们给重新合上了。

问:现在回过头看,OS_SchedNew这个函数的作用是什么?

答:很明显,它的作用就是寻找到,在当前已经就绪的任务中,优先级最高的那一个任务。

问:题外话,思考一个问题,为什么要用查表法呢?如果是自己来做这个策略,有没有其他的方法?

答:当然有,如果是我来做,或许可以建立一个大表,里面装有所有任务的就绪状态,然后写一个for循环,每次进行任务切换的时候,从低到高依次判断,如果任务状态位bit是1,那么就证明这个任务是就绪了的,立即跳出去进行任务切换,如果任务状态bit是0,那就证明这个任务没有就绪,继续进行下一个判断,我想这样肯定更容易理解一些。

不过这样做有一个问题,如果当前我就绪的任务优先级是0,那么在第一个循环就能找到任务,然后任务切换,时间比查表法块很多,如果我就绪的任务是255呢?那么我可能就需要循环255次才能找到就绪的任务,那么时间肯定会很长。

用这种方法会导致寻找就绪任务需要的时间完全不能确定,有时候短,有时候长,然而这对于一个系统而言,最怕的就是这种不确定因素。

查表法和循环法就完全不同了,它虽然死板一些,但不管当前系统有多少任务,不管当前有多少任务是处于就绪状态,它每次计算出最高优先级的任务的时间是一定的,这种确定性对于系统很重要。

待续......

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

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

    继续...... 整个UCOSII嵌入式操作系统的任务调度策略便是如此,现在进行一个总结: 某个任务在执行中,每隔一定周期发生滴答时钟中断,在中断中遍历整个任务链表,更新每个任务的延时时间,修改就绪状 ...

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

    刚参加工作那几年做MCU程序,由于实现的功能和需求都比较简单,外围模块也很少,所以大多数的项目直接就在裸机上写代码.当时也没有任务和线程的概念,脑子里想的只有单个函数的调度,变量的控制等等.工作时先把 ...

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

    继续-- if (ticks > 0u) { /* 延时参数是否为0 */OS_ENTER_CRITICAL(); /* 禁止中断 */y = OSTCBCur->OSTCBY;OSRdy ...

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

    继续...... 再回到那个重要得函数: void OS_Sched (void) { #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU ...

  5. 【ROS入门-4】嘴对嘴讲解ROS的核心概念——ROS话题通信机制

    文章目录 前言 ROS系列文章 ROS的通信机制 话题(topic) 发布者 订阅者 消息(Message) 用C++来写话题通信的代码 发布者 订阅者 使用rqt_graph 源码附录 引用说明 参 ...

  6. 【ROS入门-3】嘴对嘴讲解ROS的核心概念——节点与节点管理器

    文章目录 前言 ROS系列文章 ROS的通信机制 节点(Node) 节点管理器(Master) 简单运行 引用说明 参考 前言 我要给大家来介绍一下ROS当中一些核心的概念,帮助大家去在后面的ROS学 ...

  7. 手把手,嘴对嘴教你Spring Cloud 微服务实战 -- 前言

    Spring Cloud 总结 博主接触到Spring Cloud 大概已经一年多了,当时Spring Cloud微服务框架已经是潮流了,不会一点都不好意思出去面试.并且主流技术基本上都在谈论微服务, ...

  8. 深入研究嵌入式操作系统的绝佳教材

    前 言 本书作者多年的嵌入式领域研发.管理和教学经验,促成了本书的诞生. 本书的目的 操作系统是一个古老的话题,它的出现和发展,对于计算机技术来说,意义非凡.这种发展不仅仅体现在传统的计算机中,在强调 ...

  9. 三种嵌入式操作系统的分析与比析

    1.1 嵌入式系统 嵌入式系统是以嵌入式计算机为技术核心,面向用户.面向产品.面向应用,软硬件可裁减的,适用于对功能.可靠性.成本.体积.功耗等综合性能有严格要求的专用计算机系统. 嵌入式系统应具有的 ...

最新文章

  1. 打算看的书或正在看的书
  2. PTA —— 基础编程题目集 —— 编程题 —— 7-2 然后是几点 (15 分)
  3. springmvc 注解总结
  4. 为什么越来越多的人消失在朋友圈?
  5. 使用github+hexo搭建静态blog
  6. 计算机网络属性设置方法,电脑本地连接的属性设置在哪里
  7. vue-cli3 中 sockjs-node/info?t=报错 的解决方法
  8. 【python】中 type dtype astype辨析
  9. Java-----Excel转HTML
  10. 图解设计模式-State模式
  11. 《推荐系统》基于标签的用户推荐系统
  12. 用SVD压缩彩色图片(MATLAB代码)
  13. android壁纸软件代码,android高清壁纸APP完整源码HD Wallpaper(服务端+客户端)
  14. Strings、bytes and runes -- 就要学习Go语言
  15. 人大金仓数据库-表的定义
  16. gps转百度地图坐标 java,GPS坐标与百度地图坐标转换
  17. 帕斯卡三角形题解(Python代码实现)
  18. 实现一个鼠标自动按键程序
  19. ATK1218-BD_GPS定位模块消息输出格式配置
  20. oracle数据泵到处表结构,数据泵导出数据库所有表结构

热门文章

  1. Matlab数字图像处理 01 图像数据的表示与基本运算
  2. ROS kinetic VREP3.6.2 配合使用在Unbuntu 16.04中
  3. Win2003共享文件夹权限设置以及如何不需要密码访问共享文件?
  4. 对于用visio画图的一些思考
  5. Idea之阿里巴巴代码规范
  6. torchtext安装教程
  7. 请问下载的arcgis切片怎么打开?
  8. ensp rip配置
  9. REMOTE 红外遥控模块
  10. 大数据开发工程师之SQL面试题(一)