在嵌入式操做系统复习中,咱们了解了μC/OS-II的相关基础知识,在任务调度这一节,咱们提到了
优先级位图算法
,本文详细介绍该算法的原理和实现。 说明: 本文参考了这篇文章,加入了一些本身的理解,若有侵权,请联系删除:原文连接html
一、μC/OS-II任务优先级相关简介:μC/OS-II中共有64个优先级(0~63级,数字越小优先级越高)。由于是实时系统,因此对应每一个任务就分配一个优先级。web
二、2进制和10进制转换基础 这里先介绍一个数学知识,二进制如何变为十进制,好比十进制26,其8位二进制表示为:00 011 010
。当十进制为0~63时,前两位无做用,因此只看后6位——011 010
.怎么计算成十进制呢?很简单:把这个十进制数,分为两个部分,高三位和低三位,这个十进制数的大小就等于高三位的十进制8+低三位的十进制数(其实就是二进制转八进制再转十进制)。高三位的011=3
,低三位的010=2
,因此26=3x8+2=(011)<<3+(010).即将高三位左移三位
就是8再加上低三位。下面要介绍的算法也是这个数学方法。算法
三、优先级任务调度过程数组
就绪任务
)进行标记。并经过标记来查找当中任务中优先级最高的任务
优先级建立 μC/OS-II中建立任务的函数原型: INT8U OSTASKCeate(void (*task)(void *pd),void *pdata,os_stk *ptos,INT8U prio)
,从这个函数能够看出,最后一个参数就是优先级,因此结论是,在建立任务的同时就要肯定任务的优先级,而且是该优先级是8位的(0~2^8-1),这里也能够看出为何会有64个优先级。数据结构
由于用户能够指定任务的优先级,但实时操做系统最大的好处就是高优先级的任务能够抢占低优先级的任务,那怎么实现的呢?固然是经过优先级实现。 既然用户在调用系统函数建立任务的同时指定了任务的优先级,一旦建立了任务,该任务就会当即成为就绪状态,操做系统就会将该任务的优先级
标志位置位1
,至关于作个记号,操做系统内心就会想,哦,这个优先级的任务已经就绪了,一样建立了其余的任务,操做系统都会在某个地方作好标记代表对应优先级的任务已经就绪,而后在调度函数的调度下进行调度,那么在哪一个地方作个标记呢?既然是实时操做系统,操做系统用什么算法去查找优先级最高的任务呢?编辑器
任务优先级的标定 什么是优先级的标定:就是操做系统要知道哪一个任务已经就绪了,而后就在这些就绪了的任务里面切换调度。因此第一步就是要知道哪些任务就绪了,而后就能够操做了。 这里要先介绍两个数据结构,:OSRdyGrp、OSRdyTbl[]
,这两个变量协同完成优先级的标定。 OSRdyGrp
:优先级就绪组 这是一个8位的变量。每个变量对应于OSRdyTbl[]中的一行(其实是一个元素,但也能够理解为一行)。 OSRdyTbl[]
:优先级就绪表 这是一个数组,有8个成员,每一个成员都是8位的变量,因此就能够构成了8*8的矩阵了。因此64个优先级都是标定在这个数组中的。 函数
从上图能够明显看出,这个图有64个空格(64个位),每一个空格对应一个数字,该数字就是优先级的标号,可是这个是人为的标上的,实际上这是64个空格,如今要作的事情就是将就绪任务的优先级相对应的标号置1,表示这个优先级任务就绪了,好比刚建立了一个任务,它的优先级是7,因此往表格中数字为7的空格写入1就代表该优先级的任务就绪了,能够被调度了。另外当全部须要建立任务都建立完毕后,各个就绪任务的相应数字空格都会置1,代表这些任务都就绪了,好比,如今建立了5个任务,优先级分别为4,7,9,10,24.因此在建立完这些任务后,这个优先级就绪表中的相对应的数字空格都会被置1,要特别注意上图的优先级顺序,0的优先级最高,63的优先级最低。url
那到底怎么往空格里置1的呢? 这里就要分析这个优先级就绪表了
:spa
1.这个表的数据结构是数组,也就是说,这个数组有8个成员,每一个成员都是8位的变量。 2.经过二级查找实现对就绪任务的标定的。这里能够理解为一个矩阵,找某个数,确定是先找行,再找列。从而找到这个元素位置。思想就是这个。 怎么找行呢?这里的一个变量OSRdyGrp,是一个8位的变量。每一位对应上图的一行,当某一位置1时,就代表就绪表对应的行有就绪任务,而后再查找列,就能够找到哪一个任务就绪了。如今举个列子来讲明:操作系统
建立一个任务,它的优先级为 prio=11(这是用户指定的),此优先级用二进制表示(8位):最前面两位无用处,前面已说过 00 001 011
。那么怎么在对应的OSRdyTbl[]优先级就绪表中进行标定呢?
对上面这个数来讲 001 =1说明是第1行(数组从0行开始),011=3说明在第3个位置要写入1,合在一块儿就是第一行的第三个位置写入1
,这样就完成了对应数字优先级标号的标记。
那从上面的思路来看,咱们只要知道数组中的第几行和第几列的值就能够了,因此又引进了一个映射数组
: OSMapTbl[]
,其具体内容以下。下标0对应的就是0位为1,下标1对应的就是1位为1,而后把这个数赋值给OSRdyGrp优先级就绪组。则OSRdyGrp哪一个位为1则说明就是就绪表哪一个行有就绪任务了。这样作的好处就是快。这也就是这个数组就是个映射数组,方便操做而已。
下标 | 二进制值 |
---|---|
0 | 00000001 |
1 | 00000010 |
2 | 00000100 |
3 | 00001000 |
4 | 00010000 |
5 | 00100000 |
6 | 01000000 |
7 | 10000000 |
至此,以上涉及3个数据结构了,如今来总结一下: 1.OSRdyGrp
优先级就绪组:第几位被置1,就说明就绪表中第几行有就绪任务。好比OSRdyGrp=0000 0001
。说明OSRdyTbl[0]
行有就绪任务。具体是这行的哪一个列还要根据低三位
的值来决定. 2.OSRdyTbl[]
优先级就绪表:行+列就能标定就绪任务的优先级. 3. OSMapTbl[]
优先级映射表:用来方便生成第几行,第几列的一个转换.
下面来看ucos中的源码怎么实现的: 任务就绪源码以下:
OSRdyGrp |= OSMapTbl[prio>>3];
OSRdyTbl[prio>>3] |= OSMapTbl[prio&0x07];
代码解释:prio>>3是获取优先级的高3位
,prio&0x07是获取优先级的低3位
。而后在经过OSMapTbl的映射分别得到了就绪表中的行和就绪表中的列, 而后查询就绪算法:
y = OSUnMapTbl[OSRdyGrp];
x = OSUnMapTbl[OSRdyTbl[y]];
prio = (y << 3) + x;
举个例子: 建立一个任务,且prio=11=001 011
的状况分析: 高3位
:001=1
经过OSMapTbl
映射后,OSRdyGrp=0000 0010,便是就绪表中第1行有任务就绪。 低3位
:011=3,经过OSMapTbl
映射后
//低三位的映射
OSMapTbl[prio&0x07] = OSMapTbl[3] = 0000 1000;
OSRdyTbl[prio>>3] = OSRdyTbl[1] = 0000 1000;
经过这句代码就往就绪表第一行(从OSRdyTbl[1]看到)第3个位置(从右往左看0000 1000,第一个为1的位,0开始)写入1,代表该任务就绪了。
这样就完成了单个任务优先级的标定。
多任务优先级设定 这里引入一个表格:优先级断定表OSUnMapTbl[]
,这个表的做用是为了节省时间,这样查表的话,耗的时间是必定的,很好的知足了实时性。下面来分析这个表的做用。 1.先看最旁边的注释,说明的是该数组中对应的位置。 2.解释这个数组中内容,这些数字怎么来的。
举例:0x53 如上图所示的位置,为何是0呢?咱们把0x53变成二进制来看: 0101 0011,从右往左看,第一个出现1的位,就是0位因此为0. 为何是从右往左看呢?由于高优先级的数字低,你应该懂的。
例子 : 有4个任务 ,优先级分别为6,10,11,17.。把上面就绪任务算法再贴一遍:前面2位无论。
6对应二进制:000 110
高3位:000=0 经过OSMapTbl映射后,
OSMapTbl[prio>>3]= OSMapTbl[0]=0000 0001
低3位:110=6,经过OSMapTbl映射后
OSMapTbl[6]=0100 0000
OSRdyTbl[prio>>3]= OSRdyTbl[0]=0100 0000
10对应二进制:001 010
高3位:001=1 经过OSMapTbl映射后,
OSMapTbl[prio>>3]= OSMapTbl[1]=0000 0010.
低3位:010=2,经过OSMapTbl映射后
OSMapTbl[2]=0000 0100
OSRdyTbl[prio>>3]= OSRdyTbl[1]=0000 0100
11对应二进制:001 011
高3位:001=1 经过OSMapTbl映射后,
OSMapTbl[prio>>3]= OSMapTbl[1]=0000 0010.
低3位:011=3,经过OSMapTbl映射后
OSMapTbl[3]=0000 1000
OSRdyTbl[prio>>3]= OSRdyTbl[1]=0000 1000
17对应二进制:010 001
高3位:010=2 经过OSMapTbl映射后,
OSMapTbl[prio>>3]= OSMapTbl[2]=0000 0100.
低3位:001=1,经过OSMapTbl映射后
OSMapTbl[1]=0000 0010
OSRdyTbl[prio>>3]= OSRdyTbl[2]=0000 0010
经过就绪任务算法:
OSRdyGrp |= OSMapTbl[prio>>3];
OSRdyTbl[prio>>3] |= OSMapTbl[prio&0x07];
最终OSRdyGrp的值就是将全部的OSMapTbl[prio>>3]进行位或运算:
OSRdyGrp=
000 00001
|0000 0010
|0000 0010
|0000 0100
=0000 0111 = 0x07
OSRdyTbl[0]=0100 0000
OSRdyTbl[1]=
0000 0100
|0000 1000
=0000 1100(相同的列进行位或运算)
OSRdyTbl[2]=0000 0010
而后查找优先级断定表OSUnMapTbl[]
OSRdyGrp=0x07
Y=OSUnMapTbl[0x07]=0
说明是最高优先级在第0组。
OSRdyTbl[0]=0100 0000=0x40
X = OSUnMapTbl[0x40]=6
最高优先级为:prio= y*8+x=6
至此,最高优先级就选出来了。而后调度此任务运行就是了,另外,删除任务就是将对应就绪列表位的置的1清零就是。
if ((OSRdyTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0)
OSRdyGrp &= ~OSMapTbl[prio >> 3];
看到这里,这行代码理解应该没有问题,就是反操做而已。