uC/OS II任务就绪表

原文地址:uC/OS II任务就绪表 作者:simplorer

    先来了解一下uC/OS II的任务状态。uC/OS II的任务共分为五种状态:Dormant(休眠)、Waiting(等待)、Running(运行)、Ready(就绪)以及ISP(中断)。

    Dormant:指该任务驻留内存,但是不被任务调度器调度,被删除的任务就处于这种状态;
    Waiting:指该任务在某一事件的发生,例如等待某外设的I/O操作,等待某共享资源由暂不能使用变成能使用状态,等待定时脉冲的到来或等待超时信号的到来以结束目前的等待,等等;
    Running:指该任务掌握了CPU的使用权,正在执行中;
    Ready  :指该任务已经准备好,可以运行了,但是由于优先级低于别的任务,暂时不能运行,只能在就绪表中等待;
    ISP    :指发生中断时,CPU需要相应中断,正在运行中的任务被打断,就进入了ISP状态。

    图1. 是uC/OS II系统的任务状态切换图,具体的切换过程在本文中不做深究。

图1. uC/OS II任务状态切换图

clip_image002

    对于2.52版本的uC/OS II来说,共有64级的任务优先级,编号为0~63,数字越小,优先级越低。必须保证所有任务都分配到不同的优先级。系统保留了4个最高优先级的任务和4个最低优先级的任务,所有用户可以使用的任务数有56个。当μC/OS-Ⅱ初始化的时候,最低优先级OS_LOWEST_PR1O总是被赋给空闲任务idle task。

    如果同时只有一个任务处于就绪状态,OS直接执行就可以了,但是如果同时多个任务都处于就绪状态,OS怎么操作?这就引出了就绪表的问题。uC/OS II的就绪表结构设计的十分巧妙,现在你也许感受不到,等我介绍完了就绪表的整个结构,及其带来的便利,大家就会佩服Labrosse的天才思维了。

    考虑到系统只有64级的任务优先级,Labrosse设计了两个变量OSRdyGrp和OSRdyTbl[]。把就绪表分成8*8的一个矩阵形表(使用全部优先级的情况)。变量声明如下:
      OS_EXT  INT8U     OSRdyGrp;                           
      OS_EXT  INT8U     OSRdyTbl[OS_RDY_TBL_SIZE];          
    从声明中可以看出,OSRdyGrp为一个8位的无符号字符型,而OSRdyTbl则是一个8位的无符号字符数组,字符数组的大小由下面的代码给出:

      #define  OS_RDY_TBL_SIZE   ((OS_LOWEST_PRIO) / 8 + 1) 
    可以说,字符数组的大小是根据你所定义的任务个数改变的,这样可以减少不必要的空间浪费,节约RAM资源。

图2. uC/OS II就绪表

clip_image002[7]

    OSRdyGrp代表任务优先级的组,一位代表一个优先级组,当某位被置1时,表示该组中至少有一个任务优先级被设置了。比如OSRdyGrp = 0x01,则代表第一组(优先级为8~15)中的某一个或多个优先级有效。通过检查OSRdyGrp的值可以确定某一组是否有优先级有效,但是无法确定到底有几个优先级有效。这时,就需要数组OSRdyTbl[]确定具体的优先级位置了。

    假设优先级为31的任务进入就绪表(只有这一个任务),如何确定OSRdyGrp与OSRdyTbl[]的值呢?先通过图例法观察,如图3所示:

图3. 图形法求解变量

image   
    从图3中,可以方便的得出OSRdyGrp = 0x08,OSRdyTbl[3] = 0x80,这种方式反映到代码上,可以是如下形式:
      OSRdyGrp = 2^(prio / 8);            
      OSRdyTbl[prio / 8] = 2^(prio % 8);
    当然,上面的代码只是简单的实现了所需的功能,没有什么效率可言,对于一般的处理器来说,位操作都要比算术操作速度要快,于是使用位操作代替上面代码中的算术操作 。
      OSRdyGrp = (1 << (prio >> 3));      
      OSRdyTbl[prio >> 3] = (1 << (prio & 0x07));

    好了,我现在已经开始沾沾自喜了,因为我成功了优化了部分代码,提高了OS的效率。对于大师来说,这些优化还不是最优的,上面的代码还有继续“压榨”的可能性。位操作虽然比算术运算效率高,还有一种效率更高的做法-----查表。第一次运算,再次使用,不通过运算,直接通过查表,得到结果。

    对于只有64级优先级的系统来说,prio的取值最大为63,则prio >> 3与prio & 0x70表达式的值位于0~7位之间。而对于1 << (prio >> 3)和1 << (prio & 0x70))来说,由于右移了移位,表达式的值在1~8位之间。
   
令变量OSTCBX = prio & 0x07,OSTCBY = prio >> 3,OSTCBBitX = 1 << (prio & 0x07),OSTCBBitY = 1 << (prio >> 3),同时设计一个数组OSMapTbl[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80},则可以实现如下的对应关系:
      OSTCBBitX = OSMapTbl[OSTCBX];
      OSTCBBitY = OSMapTbl[OSTCBY];
    我们最初求解的等式可以表达为;
      OSRdyGrp = OSMapTbl[OSTCBY];
      OSRdyTbl[prio >> 3] = OSMapTbl[OSTCBX];
    增加、删除某一任务,为了不影响整个就绪表,实际操作中使用的是或运算,这点请注意。
       OSRdyGrp = OSMapTbl[prio >> 3];
       OSRdyTbl[prio >> 3] = OSMapTbl[prio & 0x07];   

 
     if ((OSRdyTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0)
           OSRdyGrp &= ~OSMapTbl[prio >> 3]           

    
    现在,任务优先级如何加入,退出就绪表已经明白了,那么另一个问题来了,如何找出进入就绪态的优先级最高的任务?我可以想到的办法就是扫描整个表格,或者说遍历整个表格,优先级越高,扫描的时间越短,如果刚好是63号优先级,则所花费时间最长,当然还可以采用一些常用的算法减少平均扫描时间,最终都无法保证扫描时间是个常数。

    通过观察上面等式的求解过程,还可以得出一个结论:prio值的高3位决定优先级属于哪个组,而低3位决定优先级处于这一组的具体位置。Labrosse先生于是又设计一个名为OSUnMapTbl[]的查找表,利用空间换时间的方式,将查找任务的时间固定为一个值。为理解这个表格的工作原理,我们假设OSRdyGrp = 0b10101010,即第1,3,5,7组为1,对应的OSRdyTbl[1] = 0b10101010,OSRdyTbl[3] = 0b10101010,OSRdyTbl[5] = 0b10101010,OSRdyTbl[7] = 0b10101010
又y = OSUnMapTbl[OSRdyGrp] = OSUnMapTbl[170] = 1,x= OSUnMapTbl[OSRdyTbl[y]] = OSUnMapTbl[170] = 1,最终表格中的最高优先级prio =y<<3 + x=9。也就是说现有就绪表中优先级最高的任务是9号任务,这与假设相同。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值