一、任务优先级与任务调度
- 任务切换,先保存正在运行任务的当前状态(context),即CPU寄存器中的全部内容,这些内容保存在任务的当前状态保存区(task’s context storage area)也就是任务自己的栈区之中。入栈工作完成后,就要把下一个将要运行的任务的当前状态从该任务的栈中重新装入CPU的寄存器,并开始下一个任务的运行
- 做任务切换所需要的时间取决于CPU有多少寄存器要入栈
内核负责管理各个任务,或者说为每个任务分配CPU时间,并负责任务之间的通信,内核提供的基本服务就是任务切换。 - 任务只会在OS_Sched()和OSIntExit()中进行切换。OSIntExit()是退出中断后,会寻找就绪任务切换一次
- 任务切换的实际操作:将被挂起任务的寄存器入栈,将就绪任务保存在栈里的寄存器数据出栈,恢复到硬件寄存器中去。
ucos ii 如何如何进行任务调度?如何选择优先级最高的任务?
优先级prio是一个无符号的8位数,但是只有低6位有效,0x111111就是63,所有为啥是任务优先级一共有64个
使用查表方式进行任务切换,因为查表法具有确定的时间,增加了系统的可预测性,uC/OS中所有的系统调用时间都是可确定的
分析从OSTimeDly(n)(有很多函数可以引起任务调度)函数开始,OSTimeDly(n)一般会被任务函数while(1)中任务调用,调用OSTimeDly(n)实际上调用OS_Sched()函数,在OS_Sched()内部实现获取最高优先级的操作(函数OS_SchedNew (void))。
当调用任务创建函数
INT8U OSTaskCreate (void (*task)(void *p_arg),
void *p_arg,
OS_STK *ptos,
INT8U prio )
时(其中参数prio为该任务优先级),函数内部调用OSTaskStkInit(task, p_arg, ptos, 0u)函数,该函数用来初始化任务控制块,并进行任务优先级设置的,其中有几个非常重要的变量:
ptcb->OSTCBY = (INT8U)(prio >> 3u);
ptcb->OSTCBX = (INT8U)(prio & 0x07u);
ptcb->OSTCBBitY = (OS_PRIO)(1uL << ptcb->OSTCBY);
ptcb->OSTCBBitX = (OS_PRIO)(1uL << ptcb->OSTCBX);
OSRdyGrp |= ptcb->OSTCBBitY;
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
从变量赋值可以看出,ptcb->OSTCBY为prio的第3 ~ 5 位,ptcb->OSTCBX为prio的第0 ~ 2 位,ptcb->OSTCBBitY 为将1 左移ptcb->OSTCBY位,ptcb->OSTCBBitX 为将1 左移ptcb->OSTCBX位,OSRdyGrp 为ptcb->OSTCBBitY值,OSRdyTbl[ptcb->OSTCBY为ptcb->OSTCBBitX值。例如,当优先级为12(0x00 001 100)时有:
ptcb->OSTCBY = 0x0000 0001 = 1
ptcb->OSTCBX = 0x0000 0100 = 4
ptcb->OSTCBBitY = 0x0000 0010
ptcb->OSTCBBitX = 0x0001 0000
最后得出:
OSRdyGrp = 0x0000 0010
OSRdyTbl[ptcb->OSTCBY] = OSRdyTbl[1] = 0x0001 0000
下面对这两个变量进行分析:
1、 OSRdyGrp表示当前有那些任务就绪,系统将64个任务分成8组,每一组8个,这样8位的 OSRdyGrp每一Bit就能表示一组,比如bit0表示0 ~ 7任务,bit1代表815任务。当一组中有任意一个任务就绪时,该Bit就值1,比如815中有任务就绪,那Bit1就是1
2、 OSRdyTbl[63/8 + 1]任务就绪表,共8个元素,每个元素8位,这样正好就64个任务,每一个Bit对应一个任务,比如OSRdyTbl[0]的bit0bit7对应07任务。如果任务就绪,对应的位置1。
为了更加直观的理解,画张表格:
OSRdyGrp OSRdyTbl[] 对应优先级
Bit 0 OSRdyTbl[0] 0 ~ 7
Bit 1 OSRdyTbl[1] 8 ~ 15
Bit 2 OSRdyTbl[2] 16 ~ 23
Bit 3 OSRdyTbl[3] 24 ~ 31
Bit 4 OSRdyTbl[4] 32 ~ 39
Bit 5 OSRdyTbl[5] 40 ~47
Bit 6 OSRdyTbl[6] 48 ~ 55
Bit 7 OSRdyTbl[7] 56~ 63
例如优先级12 则对应与OSRdyGrp Bit 1置位(0x0000 0010)与OSRdyTbl[1] ,而OSRdyTbl[1]的值应该为0x0001 0000,此时表示优先级为12的任务就绪了。
但是当有任务就绪的时候是如何找到最高点的优先级,uCOSII使用了一个表:
INT8U const OSUnMapTbl[256] = {
0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x00 to 0x0F */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x10 to 0x1F */
5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x20 to 0x2F */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x30 to 0x3F */
6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x40 to 0x4F */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x50 to 0x5F */
5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x60 to 0x6F */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x70 to 0x7F */
7u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x80 to 0x8F */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x90 to 0x9F */
5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xA0 to 0xAF */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xB0 to 0xBF */
6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xC0 to 0xCF */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xD0 to 0xDF */
5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xE0 to 0xEF */
4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u /* 0xF0 to 0xFF */
};
由变量OSRdyGrp(8位)随机位为1则有256中可能,表中全部列出,所以 OSUnMapTbl的作用就是用表来快速查找当前就绪的最高优先级任务。换句话说,任意多个任务就绪后其最先执行的最高优先级在表中查找。具体实现利用下面三个赋值式。
y = OSUnMapTbl[OSRdyGrp]; //获取优先级的5、4、3位
x = OSUnMapTbl[OSRdyTbl[y]]; //获取优先级的2、1、0位
OSPrioHighRdy = (INT8U)((y << 3u) + x);//获取出当前已经就绪的最高优先级任务
上述过程找到优先级最高的任务,为了便于理解举个例子正向推导。
若任务12 和任务22 同时就绪,那么理论应该是12 先执行,
对于任务12就绪则:
OSRdyGrp = 0x0000 0010
OSRdyTbl[ptcb->OSTCBY] = OSRdyTbl[1] = 0x0001 0000
对于任务22就绪则:
OSRdyGrp = 0x0000 0100
OSRdyTbl[ptcb->OSTCBY] = OSRdyTbl[2] = 0x0100 0000
则:
OSRdyGrp = 0x0000 0110 = 6
OSRdyTbl[1] = 0x0100 0000
OSRdyTbl[2] = 0x0100 0000
y = OSUnMapTbl[OSRdyGrp] =OSUnMapTbl[6] = 1;
x = OSUnMapTbl[OSRdyTbl[y]] = OSUnMapTbl[OSRdyTbl[1]]= OSUnMapTbl[16] = 4
最后:OSPrioHighRdy = (INT8U)((y << 3u) + x) = 8 + 4 = 12(执行该最高优先级),同理其他优先级就绪一样。
Y的来源,Y是最高优先级的高三位。OSRdyGrp一共8bit,那Y就是OSRdyGrp中最低位为1的位;X的来源,X是最高优先级的低三位。首先要找最高优先级,那么如果在同一组内,就是OSRdyTbl的一个元素里面,最低位是1的就是优先级最高的。举例OSRdyTbl[0] =0x0A;//0b00001010,这一位上有任务1和3都就绪,任务1优先级更高,就是最低位是1的就是优先级最高的。那么X就是OSUnMapTbl中最低位为1的位。
此外,在延时函数OSTimeDly(n)中,可以看到赋值:
y = OSTCBCur->OSTCBY;
OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX;
if (OSRdyTbl[y] == 0u) {
OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;}
这段代码的功能就是将当前运行的任务的相应位清零,让任务处于非就绪状态。
最后,只看很难理解,要自己动手推导一遍!!!