您的位置:首页 > 其它

uC/OS II任务就绪表

2014-08-01 15:10 148 查看
原文地址: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任务状态切换图




   
对于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就绪表




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

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

图3.
图形法求解变量


   

    从图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号任务,这与假设相同。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: