操作系统的实时性主要体现在:当优先级高的任务要求工作时,操作系统要以尽快的时间将此任务调度到CPU
执行。这里所花费的时间主要包括两部分:查找最高优先级任务和任务上下文切换。其中,任务上下文切换时
间是和处理器相关的,操作系统无法控制。我们主要分析uC/OS-ii如何查找最高优先级任务的。
因为任务较少,uC/OS-II采用单一优先级,这为算法的实现提供了很大的方便。在uC/OS-II中,优先级可以作为任务的标识(当然要在任务存在的情况下,是通过一个指针数组实现的)来用。
调度算法主要基于分级查询。考虑到任务数目<64,可以用6bit来表示,分为高3位和低三位。uC/OS
-II将优先级进行分组,按高三位进行分组,可得8个(最多)优先级数组(000-111);每个优先级的在数组
中的位置由其低三位表示。在源码中,高三位用带Y后缀的变量表示,而低三位用带X后缀的变量表示。这样建
立了1个变量OSRdyGrp(INT8U,8bit,每个bit代表一组)和1个数组OSRdyTbl[8](INT8U,每 组8bit,每个
bit代表一个优先级)。这样形成了的二级查询,先选组,再选组内偏移。
其中,OSRdyGrp每一bit置1,表示该组有任务就绪。(第0~7组)。其示意图见下图:
我们举一个例子,看一下如果优先级为22的任务就绪,我们如何对优先级数组进行操作。用二进制表示为
0b00010110,其高3位为010,为2,则将第2组,也就是OSRdyGrp的第2位置1。其低3位为110,为6,则将其
OSRdyTbl[2]的第6位置1。编程实现时,可以通过对1进行移位, 再进行 或 操作来实现。但考虑到移位时间
不确定(要进行统一化),uC/OS-II选择建立了一个表OSMapTbl[8],如下。
表 T3.1 OSMapTbl[]的值
Index Bit Mask (Binary)
0 00000001
1 00000010
2 00000100
3 00001000
4 00010000
5 00100000
6 01000000
7 10000000
这样,当一个任务就绪时,我们这样处理。其中Prio是任务的优先级。
程序清单L3.5 使任务进入就绪态
OSRdyGrp |= OSMapTbl[prio >> 3];
OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07];
而当任务被挂起或删除时,我们这样处理:
程序清单L3.6 从就绪表中删除一个任务
if ((OSRdyTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0)
OSRdyGrp &= ~OSMapTbl[prio >> 3];
表建立了,如何查询最高优先级呢?这是很关键的。因为优先级的值越小,优先级越高,我们只需从OSRdyGrp
中找到最低位置1的的那一组,再从该组中,查找最低位置1的位置,组合一下,就得到了最高的优先级。
同样,这可以通过一个while循环进行判断,但是,时间也是不确定的(要进行统一化)。所以,uC/OS-II又
建立了一张表OSUnMapTbl,这张表有点大,其下标值范围为0x00-0xff,值域为0-7。
这样,其查询流程为:
程序清单L3.7 找出进入就绪态的优先级最高的任务
y = OSUnMapTbl[OSRdyGrp];
x = OSUnMapTbl[OSRdyTbl[y]];
prio = (y << 3) + x;
例如:
OSRdyGrp确定了优先级的次低三位(bit5-bit3),OSOSRdyTbl确定了优先级的低三位(bit2-bit0),
OSRdyGrp = 0x011; //0b00010001
OSRdyTbl[0] = 0x0a; //0b00001010
OSRdyTbl[4] = 0x01; //0b00000001
计算出存在的几个优先级为;0*8+1=1,0*8+3=3,4*8+0=32
假设OSRdyGrp最低位为1的是X位,OSRdyTbl[X]最低为1的是Y位,
则优先级=X*8+Y
因此只要知道了上述的X,Y就可算出最高优先级
OSUnMapTbl就是将0x00-0xff每个数据中最低位为1的位数一一列举出来
INT8U const OSUnMapTbl[256] = {
0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x00 to 0x0F */
//OSUnMapTbl[0]
//OSUnMapTbl[1] 1 bit0
//OSUnMapTbl[2] 2 bit1
//OSUnMapTbl[3] 3 bit0
//................
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x10 to 0x1F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x20 to 0x2F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x30 to 0x3F */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x40 to 0x4F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x50 to 0x5F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x60 to 0x6F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x70 to 0x7F */
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x80 to 0x8F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x90 to 0x9F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xA0 to 0xAF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xB0 to 0xBF */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xC0 to 0xCF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xD0 to 0xDF */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xE0 to 0xEF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 0xF0 to 0xFF */
};
X = OSUnMapTbl[OSRdyGrp];
Y = OSUnMapTbl[OSRdyTbl[X]];
最高优先级为X*8+Y
注:
X = OSUnMapTbl[OSRdyGrp];
此句的试举例以得此推论(只找从低位起,首次出现就绪标志“1”的那个座位号,从0号数起....):
如:OSRdyGrp=0x00;%00000000,<-字节里没有出现“1”,默认OSUnMapTbl[1]表中第1号元素,位置号为0
如:OSRdyGrp=0x01;%00000001,<-字节里第0位出现“1”,即OSUnMapTbl[1]表中第1号元素,位置号为0
如:OSRdyGrp=0x02;%00000010,<-字节里第1位出现“1”,即OSUnMapTbl[2]表中第2号元素,位置号为1
如:OSRdyGrp=0x03;%00000011,<-字节里第0位出现“1”,即OSUnMapTbl[3]表中第3号元素,位置号为0
如:OSRdyGrp=0x04;%00000100,<-字节里第1位出现“1”,即OSUnMapTbl[4]表中第4号元素,位置号为2
...........
...........
以此推之便得到unUnMapTbl[]表,该表的最重要的作用:就是放进一个字符型数据(不大于255的数),就可以找到该数据 最低位是“1”的是哪个位!以此将就绪组放进去,查到的是最低就绪组,找到最低就绪组x后,再把改组的数据放进去,然后查到的是最低就绪组里的数据的最低位是“1”的是哪个位y........
那么:最高优先级为X*8+Y 解决!!
2.2事件处理算法分析
在uC/OS-II中,事件可以是信号量、邮箱或者消息队列等,并用统一的结构体OS_EVENT表示。
我们先看一下OS_EVENT的组成:
typedef struct {
INT8U OSEventType;
INT8U OSEventGrp;
INT16U OSEventCnt;
void *OSEventPtr;
INT8U OSEventTbl[OS_EVENT_TBL_SIZE];
} OS_EVENT;
注意其中两个变量:OSEventGrp和OSEventTbl[],是不是觉得有点象OSRdyGrp和OSRdyTbl[]。那它们
的处理算法是不是 也一样呢?你猜对了,这里关于事件的各种操作(如pend、post、timeout、wait等)的算
法和任务调度算法如出一辙。当然也用到了OSUnMapTb[]和OSMapTbl[]。只是任务就绪时(等待CPU时)插入就
绪表,而当任务需要等待事件时要插入EVENT等待列表,反之亦同。
2.3 小结