在Ucos里,任务优先级是可以改变的,可以修改任务优先级的一个好处就是可以解决优先级反转问题,
什么是优先级反转问题,即当一个高优先级任务通过信函量计征访问共享资源是,该信号量被一低优先级任务占用,而这个低优先级任务在访问共享资源
是可能又被其它一些中等优先级抢先,因此造成高优先级任务被许多具有较低优先级任务阻塞,实时性难以得到保证。在访问共享资源时,恰当地修改任务的优先级
就可以解决优先级反转弯头了,
但改变任务优先级也是很花时间的,如果不发生优先级反转而提升优先级任务,释放资源又改回原优先级,则无形中浪费了许多CPU,
改变优先级的操作也是很简单的,主要调用ucosii系统函数OSTaskChangePrio(INT8U oldprio,INT8U newprio), 它需要两个参数,一个任务原优先级,一个
是任务改变后的优先级,
INT8U OSTaskChangePrio(ITN8U oldprio,INT8U newprio)
{
OS_TCB *ptcb;
OS_EVENT *pevent;
INT8U x;
INT8U y;
INT8U bitX;
INT8U bitY;
if((olddprio>=OS_LOWEST_PRIO&&oldprio!=OS_PRIO_SELF)|| (1)
newprio>=OS_LOWEST_PRIO)
return (OS_PRIO_INVALID);
OS_ENTER_CRITICAL();
if(OSTCBPrioTbl[newprio]!=(OS_TCB*)0) (2)
{
OS_EXIT_CRITICAL();
return(OS_PRIO_EXIT);
}
else
{
OSTCBPrioTbl[newprio]=(OS_TCB*)1; (3)
OS_EXIT_CRITICAL();
y=newprio>>3; (4)
bity=OSMap[y];
x=newprio&0x07;
bitx=OSMapTbl[x];
OS_ENTER_CRITICAL();
if(oldprio==OS_PRIO_SELF) (5)
{
oldprio=OSTCBCur->OSTCBPrio;
}
if((ptcb=OSTCBPrioTbl[oldprio])!=(OS_TCB*)0) (6)
{
OSTCBPrioTbl[oldprio]=(OS_TCB*)0; (7)
if(OSRdyTbl[ptcb->OSTCBY]&ptcb->OSTCBBitX) (8)
{
if((OSRdyTbl[ptcb->OSTCBY]&=~ptcb->OSTCBBitx)==0) (9)
{
OSRdyGrp&=~ptcb->OSTCBBitY;
}
OSRdyGrp|=bity; (10)
OSRdyTbl[y]=bitx;
}
else
{
if((pevent=ptcb->OSTCBEventPtr!=(OS_EVENT*)0))
{
if((pevent->OSEventTbl[ptcb->OSTCBY]&=~ptcb->OSTCBBitX)==0) (11)
{
pevent->OSEventGrp&=~ptcb->OSTCBBitY;
}
pevent->OSEventGrp|=bity; (12)
pevent->OSEventTbl[y]|=bitx;
}
}
OSTCBPrioTbl[newprio]=ptcb; (13)
ptcb->OSTCBPrio=newprio; (14)
ptcb->OSTCBY =y; (15)
ptcb->OSTCBX=x;
ptcb->OSTCBBitY=bity;
ptcb->OSTCBBitX=bitx;
OSSched(); (16)
return(OS_NO_ERRR);
}
else
{
OSTCBPrioTbl[newprio]=(OS_TCB*)0 ; (17)
OS_EXIT_CRITICAL();
return(OS_PRIO_ERR);
}
}
}
修改任务的优先级,需要经过以下几个步骤:
1.首先,用户不能修改变空闲任务的优先级(1),但用户可以改变本函数的任务或其他任务的优先级,为了改变调用本函数的任务的优先级,用户可以
指定该任务当前的优先级或OS_PRIO_SELF,OSTaskChangePrio()会决定该任务的优先级,用户还必须指定任务的新优先级,因为Ucos不允许多个任务具有
相同的优先级,所以OSTaskChangePrio需要检查新的优先级是否合法,如果新优先级是合法的,ucosii通过某些东西存储到OSTCBPrioTbl[newprio中保留这个优先级,如此
就使得OSTaskChangePrio可以重新允许中断,因为此时其它任务已经不可能建立拥有该优先级的任务,也不可能通过指定相同的新的优先级调用OSTaskChangePrio();
接下来OSTaskChangePrio()可以预先计算新优先级任务的OS_TCB中的某些值,而这些值用来将任务放入就绪表或从表中移除。
2.其次,OSTaskChangePrio检查目前的任务是否想要改变它的优先级,然后,OSTaskChangePrio检查想要改变优先级的任务是否存在,很明显,如果要改变优先级的任务就是当前任务,这个测试就会成功,但是,如果OSTaskChangePrio()想要改变优先级的任务不存在,它必须保留的新优先级放回到优先级表OSTCBPrioTbl,并返回给调用则
一个错误。
3.再次,OSTaskChangePrio可以通过插入NULL指针将指向当前任务OS_TCB的指针从优先级表中移除(7),这就使得当前任务的旧的优先级可以重新使用了,接着,我们检查一下OSTasChangePrio想要改变优先级的额任务是否就绪(8),如果该任务处于就绪态,它必须在当前的优先级下从就绪表中移除,然后在新的优先级插入到就绪表(10);这
二需要注意的是,OSTaskChangePrio()所用的是重新计算的值(4),将任务插入就绪表中的。
如果任务已经就绪,它可能会在等待一个信号量,一封邮箱,或是一个消息队列,如果OSTCBEventPtr非空,OSTaskChangePrio()就会知道任务正在等待以上的某个事件,
如果任务在等待某一事件的发生,OSTaskChangePrio必须将任务从事件控制块的等待队列中移除,并在新的优先级下将事件插入到等待队列中(12).任务也有可能正在等待延时的期满或被挂起,在这些情况下,从(8)到(12)这几行可以略过。
4. 最后,这也是最关健的一步,任务的优先级就是这里被修改的,其实简单来说就是修改任务控制块里面的参数,在任务块里,OSTCBPrio,OSTCBY,OSTCBX,OSTCBitY,OSTCBBitX这几个变量的值标注了任务的优先级,修改任务的优先级就是修改者几个变量的值,OSTaskChangePrio()将指向任务OS_TCB指针存到OSTCBPrioTbl[](13)。新的优先级被保存在OS_TCB中(14),重新计算的值也被保存在OS_TCB中(15).OSTaskChangePrio完成了关键的步骤后,在新的优先级高于旧的优先级或新的优先级高于调用本函数的任务的优先级情况下,任务调度程序就会被调用(16).