送别诗的名句合作者:ucos 就绪表(Ready List):任务设置,清除和查表最高优先级任务

来源:百度文库 编辑:偶看新闻 时间:2024/04/20 15:51:24
3.0 就绪表(Ready List)
每个任务被赋予不同的优先级等级,从0级到最低优先级OS_LOWEST_PR1O,包括0和OS_LOWEST_PR1O在内(见文件OS_CFG.H)。当uCOS II初始化的时候,最低优先级OS_LOWEST_PR1O总是被赋给空闲任务idle task。注意,最多任务数目OS_MAX_TASKS和最低优先级数是没有关系的。用户应用程序可以只有10个任务,而仍然可以有32个优先级的级别(如果用户将最低优先级数设为31的话)。
每个任务的就绪态标志都放入就绪表中的,就绪表中有两个变量OSRedyGrp和OSRdyTbl[]。在OSRdyGrp中,任务按优先级分组,8个任务为一组。OSRdyGrp中的每一位表示8组任务中每一组中是否有进入就绪态的任务。任务进入就绪态时,就绪表OSRdyTbl[]中的相应元素的相应位也置位。就绪表OSRdyTbl[]数组的大小取决于OS_LOWEST_PR1O(见文件OS_CFG.H)。当用户的应用程序中任务数目比较少时,减少OS_LOWEST_PR1O的值可以降低uCOS II对RAM(数据空间)的需求量。
为确定下次该哪个优先级的任务运行了,内核调度器总是将OS_LOWEST_PR1O在就绪表中相应字节的相应位置1。OSRdyGrp和OSRdyTbl[]之间的关系见图3.3,
是按以下规则给出的:
当OSRdyTbl[0]中的任何一位是1时,OSRdyGrp的第0位置1,
当OSRdyTbl[1]中的任何一位是1时,OSRdyGrp的第1位置1,
当OSRdyTbl[2]中的任何一位是1时,OSRdyGrp的第2位置1,
当OSRdyTbl[3]中的任何一位是1时,OSRdyGrp的第3位置1,
当OSRdyTbl[4]中的任何一位是1时,OSRdyGrp的第4位置1,
当OSRdyTbl[5]中的任何一位是1时,OSRdyGrp的第5位置1,
当OSRdyTbl[6]中的任何一位是1时,OSRdyGrp的第6位置1,
当OSRdyTbl[7]中的任何一位是1时,OSRdyGrp的第7位置1,
一句话:OSRdyTbl[]这个数组的任何一个数非0时,OSRdyGrp相应(通过OSTblMap映射:0-7这八个数字和一个8位的8个位从低到高对应)的位置一。
程序清单3.5中的代码用于将任务放入就绪表。Prio是任务的优先级。

图3.3 uCOS II就绪表程序清单 L3.5 使任务进入就绪态
代码
OS_EXT INT8U    OSRdyGrp;              /* Ready list group                         */
OS_EXT INT8U    OSRdyTbl[OS_RDY_TBL_SIZE];              /* Table of tasks which are ready to run    */
OSRdyGrp |= OSMapTbl[prio >> 3];
OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07];
读者可以看出,任务优先级的低三位用于确定任务在总就绪表OSRdyTbl[]中的所在位。接下去的三位用于确定是在OSRdyTbl[]数组的第几个元素。OSMapTbl[]是在ROM中的(见文件OS_CORE.C)屏蔽字,用于限制OSRdyTbl[]数组的元素下标在0到7之间,见表3.1
表 T3.1 OSMapTbl[]的值Index Bit Mask (Binary)
000000001
100000010
200000100
300001000
400010000
500100000
601000000
710000000
一句话:OSTblMap映射:0-7这八个数字和一个8位的8个位从低到高对应
如果一个任务被删除了,则用程序清单3.6中的代码做求反处理。
程序清单 L3.6 从就绪表中删除一个任务
代码
if ((OSRdyTbl[prio >> 3] &= ~OSMapTbl[prio & 0x07]) == 0)
OSRdyGrp &= ~OSMapTbl[prio >> 3];
以上代码将就绪任务表数组OSRdyTbl[]中相应元素的相应位清零,而对于OSRdyGrp,只有当被删除任务所在任务组中全组任务一个都没有进入就绪态时,才将相应位清零。也就是说OSRdyTbl[prio>>3]所有的位都是零时,OSRdyGrp的相应位才清零。
找到最高优先级任务
为了找到那个进入就绪态的优先级最高的任务,并不需要从OSRdyTbl[0]开始扫描整个就绪任务表,只需要查另外一张表,即优先级判定表OSUnMapTbl([256])(见文件OS_CORE.C)。
OSRdyTbl[]中每个字节的8位代表这一组的8个任务哪些进入就绪态了,低位的优先级高于高位。利用这个字节为下标来查OSUnMapTbl这张表,返回的字节就是该组任务中就绪态任务中优先级最高的那个任务所在的位置。这个返回值在0到7之间。确定进入就绪态的优先级最高的任务是用以下代码完成的,如程序清单L3.7所示。
程序清单 L3.7 找出进入就绪态的优先级最高的任务
代码
y    = OSUnMapTbl[OSRdyGrp];
x    = OSUnMapTbl[OSRdyTbl[y]];
prio = (y << 3) + x;
例如,如果OSRdyGrp的值为二进制01101000,查OSUnMapTbl[OSRdyGrp]得到的值是3,它相应于OSRdyGrp中的第3位bit3,这里假设最右边的一位是第0位bit0。类似地,如果OSRdyTbl[3]的值是二进制11100100,则OSUnMapTbl[OSRdyTbc[3]]的值是2,即第2位。于是任务的优先级Prio就等于26(3*8+2)。利用这个优先级的值。查任务控制块优先级表OSTCBPrioTbl[],得到指向相应任务的任务控制块OS_TCB的工作就完成了。
INT8U  const  OSUnMapTbl[256] = {
0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,       /* 0x00 to 0x0F                             */
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                             */
};
Where does this fucking table comes from?  How is it made ?!
考虑下:针对就绪表,我们先找到OSRdyGrp中最小的被置为1的位,也就找到了找到了最高优先级所在的组;然后再在这个组里(OSRdyTbl[i]的值对应每一位),最小的被置为1的位,也就找到了最高优先级任务在所在的位的位置;现在找到了位置如何得到最高优先级的值呢?OSRdyTb这个数组的每一个位对应一个优先级,看看就绪表
X与Y的值和每个小方格的优先级的值有什么关系呢:Priority=8*Y+X
正好对应:
y    = OSUnMapTbl[OSRdyGrp];
x    = OSUnMapTbl[OSRdyTbl[y]];
prio = (y << 3) + x;
OSUnMapTbl数组中元素OSUnMapTbl[n]表示一个任意的8位无符号数n对应的最低位为1的那个最低位数,例如二进制数00000001B最低位为1的是bit0,如是OSUnMapTbl[00000001B] = 0,又例如1000000B最低位为1的是bit7,所以OSUnMapTbl[10000000B] = 7,及OSUnMapTbl[128]也就是上面OSUnMapTbl第9行第一列为7.
INT8U  const  OSUnMapTbl[256] 就是个一维数组,也许书中应该这样写:
INT8U  const  OSUnMapTbl[256]={    0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 。。。}
其中的值对应于其索引最低位为1的那个最低位数。
小结下:上面谈论了四个对象:优先级(值)、就续表(包括OSRdyGrp和OSRdyTbl)、OSMapTbl和OSUnMapTbl。
优先级通过OSMapTbl和OSUnMapTbl与就绪表进行map和unmap.
扩展 下:引自百度百科:http://baike.baidu.com/view/1627735.htm
查找表
目录
例子
计算正弦值
计算 1 的位数
硬件查找表
在计算机科学中,查找表是用简单的查询操作替换运行时计算的数组或者 associative array 这样的数据结构。由于从内存中提取数值经常要比复杂的计算速度快很多,所以这样得到的速度提升是很显著的。
一个经典的例子就是三角表。每次计算所需的正弦值在一些应用中可能会慢得无法忍受,为了避免这种情况,应用程序可以在刚开始的一段时间计算一定数量的角度的正弦值,譬如计算每个整数角度的正弦值,在后面的程序需要正弦值的时候,使用查找表从内存中提取临近角度的正弦值而不是使用数学公式进行计算。
在计算机出现之前,人们使用类似的表格来加快手工计算的速度。非常流行的表格有三角、对数、统计 density 函数。另外一种用来加快手工计算的工具是滑动计算尺。
一些折衷的方法是同时使用查找表和插值这样需要少许计算量的方法,这种方法对于两个预计算的值之间的部分能够提供更高的精度,这样稍微地增加了计算量但是大幅度地提高了应用程序所需的精度。根据预先计算的数值,这种方法在保持同样精度的前提下也减小了查找表的尺寸/
在图像处理中,查找表经常称为LUT,它们将索引号与输出值建立联系。颜色表作为一种普通的 LUT 是用来确定特定图像所要显示的颜色和强度。
另外需要注意的一个问题是,尽管查找表经常效率很高,但是如果所替换的计算相当简单的话就会得不偿失,这不仅仅因为从内存中提取结果需要更多的时间,而且因为它增大了所需的内存并且破坏了高速缓存。如果查找表太大,那么几乎每次访问查找表都回倒置 cache miss,这在处理器速度超过内存速度的时候愈发成为一个问题。在编译器优化的 rematerialization 过程中也会出现类似的问题。在一些环境如Java 编程语言中,由于强制性的边界检查带来的每次查找的附加比较和分支过程,所以查找表可能开销更大。
何时构建查找表有两个基本的约束条件,一个是可用内存的数量;不能构建一个超过能用内存空间的表格,尽管可以构建一个以查找速度为代价的基于磁盘的查找表。另外一个约束条件是初始计算查找表的时间——尽管这项工作不需要经常做,但是如果耗费的时间不可接受,那么也不适合使用查找表。
编辑本段例子
编辑本段计算正弦值
许多计算机只能执行基本的算术运算,而不能直接计算给定值的正弦值,它们使用如下面泰勒级数(en:Taylor series)这样的复杂公式计算相当高精度的正弦值:
(x 接近 0)
然而,这样的计算费用可能是非常大的,尤其是在低速的处理器上。有许多的应用程序,尤其是传统的计算机图形每秒需要几千次的正弦值计算。一个常用的解决方案就是在刚开始计算许多均匀分布数值的正弦值,然后在表中查找最接近所需 x 的正弦值,这个值非常接近于正确的数值,这是因为正弦函数是一个有限变化率的连续函数。例如:
real array sine_table[-1000..1000]
for x from -1000 to 1000
sine_table[x] := sine(x/1000/pi)
function lookup_sine(x)
return sine_table[round(x/1000/pi)]
Image:Interpolation example linear.png
部分正弦函数的线性插值不幸的是,查找表需要一定的空间:如果使用 IEEE 双精度浮点数的话,将会需要 16,000 字节。如果使用较少的采样点,那么精度将会大幅度地下降。一个较好的解决方案是线性插值,在表中待计算点左右两侧两个点的值之间连直线,这个点对应的直线上的值就是所计算点的正弦值。这种方法计算速度也很快,对于如正弦函数这样的平滑函数来说也有更高的精度。这里是使用线性插值的一个例子:
function lookup_sine(x)
x1 := floor(x/1000/pi)
y1 := sine_table[x1]
y2 := sine_table[x1+1]
return y1 + (y2-y1)*(x/1000/pi-x1)
当使用插值的时候,可以得益于不均匀采样,也就是说在接近直线的地方,使用较少的采样点,在变化较快的地方使用较多的采样点以最大限度地接近实际的曲线。更多的信息请参考插值。
编辑本段计算 1 的位数
population function。例如,数字 37 的二进制形式是 100101,所以它包含有三个设置成 1 的位。一个计算 32 位整数中 1 的位数的简单c语言程序是:
int count_ones(unsigned int x) {
int i, result = 0;
for(i=0; i<32; i++) {
result += x & 1;
x = x >> 1;
}
return result;
}
不幸的是,这个简单的算法在现代的架构上将需要数以百计的时钟周期才能完成,这是因为它造成了许多分支和循环,而分支的速度是很慢的。这可以使用 loop unrolling 和其它一些聪明的技巧进行改进,但是最简单快捷的解决方案是查找表:简单地构建一个 包含每个字节可能值包含的 1 的个数的256 个条目的表。然后使用这个表查找整数中每个字节包含的 1 的个数,并且将结果相加。没有分支、四次内存访问、几乎没有算术运算,这样与上面的算法相比就可以大幅度地提升速度。
int count_ones(unsigned int x) {
return (bits_set[x & 255] + bits_set[(x >> 8) & 255]
+ bits_set[(x >> 16) & 255] + bits_set[(x >> 24) & 255]);
}
----------------------------------------------------------------------
#include
#define BYTE unsigned char
/* 定义查找表 */
BYTE numTable[256] =
{
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3,
3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3,
4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4,
3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3,
4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6,
6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4,
5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3,
4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4,
4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6,
7, 6, 7, 7, 8
};
int main(int argc, char *argv[])
{
int i, num = 0;
BYTE a = 0;
/* 接收用户输入 */
printf("\nPlease Input a BYTE(0~255):");
scanf("%d", &a);
/* 计算1 的个数 */
/* 用BYTE 直接作为数组的下标取出1 的个数,妙哉! */
printf("\nthe num of 1 in the BYTE is %d", checknum[a]);
return 0;
}
编辑本段硬件查找表
在数字逻辑中,n位查找表可以使用多路复用器来实现,它的选择线是 LUT 的输入,它的输入是常数。n 位 LUT 通过将布尔逻辑函数建模为真值表从而可以编码任意 n 位输入,这是编码布尔逻辑函数的一个有效途径,4 位 LUT 实际上是现代 FPGAs 的主要元件。
采用这种结构的PLD芯片我们就可以称之为FPGA:如altera的ACEX,APEX系列,xilinx的Spartan,Virtex系列等。
查找表(Look-Up-Table)简称为LUT,LUT本质上就是一个RAM。 目前FPGA中多使用4输入的LUT,所以每一个LUT可以看成一个有4位地址线的16x1的RAM。 当用户通过原理图或HDL语言描述了一个逻辑电路以后,PLD/FPGA开发软件会自动计算逻辑电路的所有可能的结果,并把结果事先写入RAM,这样,每输入一个信号进行逻辑运算就等于输入一个地址进行查表,找出地址对应的内容,然后输出即可。