uc/os调度机制深度解析

一、准备工作:

//OSMapTbl与OSUnMapTbl定义
/*
*********************************************************************************************************
*                              MAPPING TABLE TO MAP BIT POSITION TO BIT MASK
*
* Note: Index into table is desired bit position, 0..7
*       Indexed value corresponds to bit mask
*********************************************************************************************************
*/

INT8U  const  OSMapTbl[]   = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};

/*
*********************************************************************************************************
*                                       PRIORITY RESOLUTION TABLE
*
* Note: Index into table is bit pattern to resolve highest priority
*       Indexed value corresponds to highest priority bit position (i.e. 0..7)
*********************************************************************************************************
*/

INT8U  const  OSUnMapTbl[] = {
    0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
    4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
};

需要理解的变量:OSMapTbl[](INT8U)、OSUnMapTbl[](INT8U)、OSRdyGrp(INT8U)、OSRdyTbl[](INT8U)
需理解的关系:OS_EVENT::OSEventGrp <---->OSRdyGrp, OS_EVENT::OSEventTbl <----> OSRdyTbl。

二、先来理解变量:

OSMapTbl是优先级映射表,设计者将它设计为与数组下标对应的位置为1,其他位0,例如OSMapTbl[0] = 0x01是因为下标为0,因此将0位置1即0x01.(具体设计者是怎样想到的就不是我这种沙雕能触及的了)。
OSUnMapTbl:优先级解映射表,设计者将0x00--0xff这256个数值中最低位为1的位号都列了出来,例如OSUnMapTbl[1] = 0的原因是0x01最低为为1的位号是0。(同上,不要问本雕为什么)
OSRdyGrp:全局组优先级
OSRdyTbl:全局组内优先级
ps:后两个变量有点抽象,需要结合起来理解。按我的理解,设计者将每个任务的优先级(0~63)分为了两段,首先将该优先级转化为二进制,然后从左到右依次编号0~7,使用2~4位来表示组优先级,使用5~7位来表示组内优先级,至于0~1位设计者是没有使用的(不要问本雕为什么)。那么问题来了,怎样使用这两个全局变量来存取多个不同优先级的任务呢?

我们假设某个任务的优先级表示记为prio
让我先来介绍两个表达式:

OSRdyGrp |= OSMapTbl[prio >> 3];
OSRdyTbl[prio >> 3] |= OSMapTbl[prio&0x07];

这或许很显而易见了:设计者用“或”运算将不同优先级先分组,然后再在组内记录该任务的优先级。让我们来看一个例子:假设初始时OSRdyGrp = 0,OSRdyTbl所有元素都为0,prio = 5.那么prio二进制表示为0b0000 0101,那么OSRdyGrp |= OSMapTbl[prio >> 3]的结果为0x01;OSRdyTbl[prio >> 3] |= OSMapTbl[prio&0x07]结果为0x20.这样就把优先级分段表示了。到这里,我想大家会有两个问题:你怎么取出刚刚存储进去的优先级呢?(或者说怎么验证存储进去的优先级就是5呢?)刚刚只存储了一个任务,怎么存储多个不同优先级的任务呢?哈哈,我们一个一个解决。
先看第一个问题,我们现在有两个已知条件:OSRdyGrp = 0x01,OSRdyTbl[0] = 0x20.首先我们取出组优先级存于变量y中:y = OSUnMapTbl[OSRydGrp],即y = 0;然后我们取出组内优先级存于变量x中:x = OSUnMapTbl[OSRdyTbl[y]] = OSUnMapTbl[0x20] = 5;于是我们再用一个通用表达式:curprio = (y << 3) + x;即curprio = 5.这样就求出了之前存进去的优先级。

先停一下,我们来总结一下目前为止我们知道了些什么:
首先,拿到一个任务的优先级prio,我们通过 OSRdyGrp |= OSMapTbl[prio >> 3];OSRdyTbl[prio >> 3] |= OSMapTbl[prio&0x07];这两个表达式将优先级存到OSRdyGrp和OSRdyTbl中。
其次,我们使用逆过程y = OSUnMapTbl[OSRydGrp];x = OSUnMapTbl[OSRdyTbl[y]];curprio = (y << 3) + x,来求得当前所存储的优先级中的最高优先级。注意,取出的是最高优先级,不是随便一个。

那么我们继续第二个问题:假设我有两个任务,优先级分别是1和6,初始时OSRdyGrp = 0,OSRdyTbl所有元素都为0.我首先将他们都存到OSRdyGrp和OSRdyTbl中:
1的二进制表示为0b0000 0001;
6的二进制表示为0b0000 0110;
先存优先级1(先存储哪个优先级都不重要):OSRdyGrp |= OSMapTbl[1 >> 3],OSRdyGrp = 0x01;
OSRdyTbl[1 >> 3] |= OSMapTbl[1 & 0x07],OSRdyTbl[0] = 0x02;
再存优先级6:OSRdyGrp |= OSMapTbl[6 >> 3],OSRdyGrp = 0x01 | 0x01 = 0x01;
OSRdyTbl[6 >> 3] |= OSMapTbl[6 & 0x07],OSRdyTbl[0] = 0x02 | 0x40 = 0x41;

Ok,存储完成,此时OSRdyGrp = 0x01,OSRdyTbl[0] = 0x42。那么我们开始取(记住,我们要取的是当前最大优先级,也就是1):
y = OSUnMapTbl[OSRdyGrp] = OSUnMapTbl[1] = 0;
x = OSUnMapTbl[OSRdyTbl[y]] = OSUnMapTbl[0x42] = 1;
因此,curprio = (y << 3) + x = 1;
那么既然把此优先级取出来了,就应该把他从存储库中删除。设计者很机智,在每次取出一个优先级的过程中,他记录了组优先级所在的位,以及组内优先级所在的位:组优先级位bity = OSMapTbl[y],组内优先级位bitx = OSMapTbl[x];
就刚刚取出的1优先级来说,bity = 0x01,bitx = 0x02;那么取出1优先级后,我们用 OSRdyTbl[y] &= ~bitx,即OSRdyTbl[y] = 0x40;若此时OSRdyTbl[y] == 0x00,说明y优先组中没有任务了,那么就用OSRdyGrp &= ~bity;删除该组。

显然,提取了优先级1后,我们有OSRdyGrp = 0x01,OSRdyTbl[0] = 0x40.于是我们再提取:
y = OSUnMapTbl[OSRdyGrp] = OSUnMapTbl[1] = 0;
x = OSUnMapTbl[OSRdyTbl[y]] = OSUnMapTbl[0x40] = 6;
curprio = (y << 3) + x = 6;
提取后,刚好OSRdyTbl[0] = 0x00,OSRdyGrp = 0x00.over

好了,优先级调度的基本思想就是这样。但是这还不能用于ucos中直接使用,因为它现在只是独立的东西,需要与其他东西联动才能完成重任。

三、我们来看看关系:

那么我们就结合V操作来看看吧:
复习一下V操作,就是执行一个等待任务,然后再让信号量资源加一。
对应到ucos中,V操作就是函数 INT8U OSSemPost(OS_EVENT* pevent),该函数中调用了函数OS_EventTaskRdy(OS_ENVET* pevent,void* msg,INT8U msk),OS_EventTaskRdy函数中有这么一段:

y = OSUnMapTbl[pevent->OSEventGrp];
bity = OSMapTbl[y];
x = OSUnMapTbl[pevent->OSEventTbl[y]];
bitx = OSMapTbl[x];
prio = (INT8U)((y << 3) + x);
if ((pevent->OSEventTbl[y] &= ~bitx) == 0x00) { 
pevent->OSEventGrp &= ~bity; 
} 


对,就是这么熟悉。但是又有一丝不同,主要就是,OSRdyGrp换成了 OS_EVENT::OSEventGrp,OSRdyTbl换成了OS_EVENT::OSEventTbl。因此可以猜测,这两对是有“激情”的。这就要追溯到P操作中了,P操作中有一个调用了一个函数:OS_EventTaskwait(OS_EVENT* pevent),它是这样写的:

void OS_EventTaskWait (OS_EVENT *pevent)
{
OSTCBCur->OSTCBEventPtr = pevent; /* Store pointer to event control block in TCB */
if ((OSRdyTbl[OSTCBCur->OSTCBY] &= ~OSTCBCur->OSTCBBitX) == 0x00) { /* Task no longer ready */
OSRdyGrp &= ~OSTCBCur->OSTCBBitY; /* Clear event grp bit if this was only task pending */
}
pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; /* Put task in waiting list */
pevent->OSEventGrp |= OSTCBCur->OSTCBBitY;
}


简要介绍一下里边了每个变量:
OSTCBCur(OS_TCB*):全局变量,当前事件控制块
OS_TCB::OSTCBBitX(INT8U):组内优先级访问掩码,作用类似于bitx;
OS_TCB::OSTCBBitY(INT8U):组优先级访问掩码,作用类似于bity;

那么OS_EVENT::OSEventGrp <---->OSRdyGrp, OS_EVENT::OSEventTbl <----> OSRdyTbl这两对好基友的关系也就确认了,确实可以等价。那么理解OS_EventTaskRdy(OS_ENVET* pevent,void* msg,INT8U msk)就不难了,他其实就是将被阻塞的任务由阻塞态转为就绪态,将状态记录到OSRdyGrp和OSRdyTbl中。
然后在V操作中还调用了一个函数OS_Sched(),该函数就是直接从OSRdyGrp和OSRdyTbl中直接获取当前最大优先级任务,然后调用OS_TASK_SW执行该任务。

温润如玉,坚毅如铁。
原文地址:https://www.cnblogs.com/heisen/p/10579159.html