前言
本文主要分析ucosiii中管理优先级表的os_prio.c文件。
正文
优先级表中每一个bit表示一个优先级,优先级表中某一个bit置位表示该优先级有就绪态的任务等待运行。其定义如下:
CPU_DATA OSPrioTbl[OS_PRIO_TBL_SIZE];
其中CPU_DATA
是unsigned int
类型数据;OS_PRIO_TBL_SIZE
定义为
#define OS_PRIO_TBL_SIZE ((OS_CFG_PRIO_MAX - 1u) / (DEF_INT_CPU_NBR_BITS) + 1u)
也就是1,所以优先级表翻译过来就是:
unsigned int OSPrioTbl[1];
一个32位无符号的变量,它表示32个优先级。
优先级表相关的函数都在os_prio.c文件中,包括:
OS_PrioInit()
初始化优先级列表(把OSPrioTbl
清零)。
OS_PrioInsert()
把优先级表中的某一位置位。
OS_PrioRemove()
把优先级表中的某一位清零。
OS_PrioGetHighest()
根据优先级表查找最高优先级。
接下来重点分析OS_PrioGetHighest()
,其代码内容为:
OS_PRIO OS_PrioGetHighest (void)
{
CPU_DATA *p_tbl;
OS_PRIO prio;
prio = (OS_PRIO)0;
p_tbl = &OSPrioTbl[0]; //获取优先级表的首地址
while (*p_tbl == (CPU_DATA)0) { //32位的遍历 找到不为0的32位 /* Search the bitmap table for the highest priority */
prio += DEF_INT_CPU_NBR_BITS; /* Compute the step of each CPU_DATA entry */
p_tbl++;
}
prio += (OS_PRIO)CPU_CntLeadZeros(*p_tbl); //计算前导0,如果汇编没有 就用二分查找法 /* Find the position of the first bit set at the entry */
return (prio);
}
比如优先级表二进制形式为10000000 00000000 00000000 00000000b
那么OS_PrioGetHighest()
函数返回的优先级数为0
,
10010100 01000000 00000000 00000000b
返回也是0
01010100 01000000 00000000 00000000b
返回1
也就是返回第一个被置位的bit的位置。
其中调用的CPU_CntLeadZeros()
函数用于快速找出32位变量中第一个被置位的bit位置。
为了提高查询速度,CPU_CntLeadZeros()
用到了二分查找法
和哈希表算法
。
其代码如下:
CPU_DATA CPU_CntLeadZeros (CPU_INT32U val)
{
if (val > 0x0000FFFFu) {
if (val > 0x00FFFFFFu) {
ix = (CPU_DATA)(val >> 24u);
nbr_lead_zeros = (CPU_DATA)(CPU_CntLeadZerosTbl[ix] + 0u);
} else {
ix = (CPU_DATA)(val >> 16u);
nbr_lead_zeros = (CPU_DATA)(CPU_CntLeadZerosTbl[ix] + 8u);
}
} else {
if (val > 0x000000FFu) {
ix = (CPU_DATA)(val >> 8u);
nbr_lead_zeros = (CPU_DATA)(CPU_CntLeadZerosTbl[ix] + 16u);
} else {
ix = (CPU_DATA)(val >> 0u);
nbr_lead_zeros = (CPU_DATA)(CPU_CntLeadZerosTbl[ix] + 24u);
}
}
return (nbr_lead_zeros);
}
首先判断val
是否大于0x0000FFFFu
,如果大于,那么第一个被置位的bit一定在前16位,后16位就不用再找了;
然后再判断val
是否大于0x00FFFFFFu
,如果大于,那么第一个被置位的bit一定在前16位中的前8位,前16位中的后8位就不用再找了。
第一个被置位的bit的位置也叫前导零
,。
通过这段代码,只需要两个if
判断就确定下来第一个被置位的bit在32位中的哪8位,确定是哪8位之后,接下来用哈希表法判断这8位中第一个被置位的bit是哪一位,实现方法简单粗暴,因为8位数据中每一位0
或1
的排列组合一共就只有256中情况,而每种情况的前导零是固定的,所以UCOS中干脆用一个数据把他们列举了出来。如下:
static const CPU_INT08U CPU_CntLeadZerosTbl[256] = { /* Data vals : */
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
8u, 7u, 6u, 6u, 5u, 5u, 5u, 5u, 4u, 4u, 4u, 4u, 4u, 4u, 4u, 4u, /* 0x00 to 0x0F */
3u, 3u, 3u, 3u, 3u, 3u, 3u, 3u, 3u, 3u, 3u, 3u, 3u, 3u, 3u, 3u, /* 0x10 to 0x1F */
2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, /* 0x20 to 0x2F */
2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, 2u, /* 0x30 to 0x3F */
1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, /* 0x40 to 0x4F */
1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, /* 0x50 to 0x5F */
1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, /* 0x60 to 0x6F */
1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, /* 0x70 to 0x7F */
0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, /* 0x80 to 0x8F */
0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, /* 0x90 to 0x9F */
0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, /* 0xA0 to 0xAF */
0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, /* 0xB0 to 0xBF */
0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, /* 0xC0 to 0xCF */
0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, /* 0xD0 to 0xDF */
0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, /* 0xE0 to 0xEF */
0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u /* 0xF0 to 0xFF */
};
比如:如果想知道00000001b
的前导零,00000001b
是1
,CPU_CntLeadZerosTbl[1]
为7,所以00000001b
的前导零是7,
再比如:0x14
二进制形式为00010100b
,十进制为20
,CPU_CntLeadZerosTbl[20]
为3,所以0x14
的前导零就是3。
通过以上方式,只需要两个if判断
和一次数组调用
就找到了32位变量中第一个被置位bit的位置。
后记
如果有对位图操作的需求,可以模仿os_prio.c
文件中的内容进行代码编写。