优先级反转发生在有多个任务需要使用共享资源的情况下,可能会出现高优先级任务被低优先级任务阻塞,并等待低优先级任务执行的现象。
高优先级任务需要等待低优先级任务释放资源,而低优先级任务又正在等待中等优先级任务,这种现象就被称为优先级反转。
两个任务都试图访问共享资源是出现优先级反转最通常的情况。为了保证一致性,这种访问应该是顺序进行的。如果高优先级任务首先访问共享资源,则会保持共享资源访问的合适的任务优先级顺序;
但如果是低优先级任务首先获得共享资源的访问,然后高优先级任务请求对共享资源的访问,则高优先级任务被阻塞,直到低优先级任务完成对共享资源的访问。
1)设计了3个应用任务TA0~TA2,其优先级逐渐降低,任务TA0的优先级最高。
2)除任务TA1外,其它应用任务都要使用同一种资源,该资源必须被互斥使用。为此,创建一个二值信号量mutex来模拟该资源。虽然μC/OS-Ⅱ在创建信号量时可以选择采用防止优先级反转的策略,但在本实验中我们不使用这种策略。
#include "includes.h"
#define TASK_STK_SIZE 512
void TaskStart(void *pdata);
void Task0(void *pdata);
void Task1(void *pdata);
void Task2(void *pdata);
OS_STK TaskStartStk[TASK_STK_SIZE];
OS_STK Task0Stk[TASK_STK_SIZE];
OS_STK Task1Stk[TASK_STK_SIZE];
OS_STK Task2Stk[TASK_STK_SIZE];
INT16S key;
INT8U y=0;
INT8U err;
char*s;
OS_EVENT*Semp;
void main (void)
{
OSInit();
PC_DOSSaveReturn();
PC_VectSet(uCOS, OSCtxSw);
OSTaskCreate(TaskStart,(void*)0,&TaskStartStk[TASK_STK_SIZE - 1],0);
OSStart();
}
void TaskStart(void *pdata)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
pdata = pdata;
OS_ENTER_CRITICAL();
PC_VectSet(0x08, OSTickISR);
PC_SetTickRate(OS_TICKS_PER_SEC);
OS_EXIT_CRITICAL();
OSStatInit();
OSTaskCreate(Task0,0,&Task0Stk[TASK_STK_SIZE-1],1);
OSTaskCreate(Task1,0,&Task1Stk[TASK_STK_SIZE-1],2);
OSTaskCreate(Task2,0,&Task2Stk[TASK_STK_SIZE-1],3);
OSTaskSuspend(OS_PRIO_SELF);
for (;;)
{
if(PC_GetKey(&key)==TRUE)
{
if(key==0x1B)
{
PC_DOSReturn();
}
}
OSTimeDlyHMSM(0,0,3,0);
}
}
void Task0(void *pdata)
{
#if OS_CRITICAL_METHOD==3
OS_CPU_SR cpu_sr;
#endif
pdata=pdata;
for(;;)
{
OSTimeDlyHMSM(0,0,3,0);
s="Task0 start!";
PC_DispStr(15,++y,s,DISP_BGND_BLACK+DISP_FGND_WHITE);
OSTimeDlyHMSM(0,0,1,0);
OSSemPend(Semp,0,&err);
OSTimeDlyHMSM(0,0,2,0);
s="Task0 completion!";
PC_DispStr(15,++y,s,DISP_BGND_BLACK+DISP_FGND_WHITE);
OSSemPost(Semp);
OSTimeDlyHMSM(0,0,5,0);
}
}
void Task1(void *pdata)
{
#if OS_CRITICAL_METHOD==3
OS_CPU_SR cpu_sr;
#endif
pdata=pdata;
for(;;)
{
OSTimeDlyHMSM(0,0,2,0);
s="Task1 start!";
PC_DispStr(15,++y,s,DISP_BGND_BLACK+DISP_FGND_WHITE);
OSTimeDlyHMSM(0,0,2,0);
s="Task1 completion!";
PC_DispStr(15,++y,s,DISP_BGND_BLACK+DISP_FGND_WHITE);
OSTimeDlyHMSM(0,0,5,0);
}
}
void Task2(void *pdata)
{
#if OS_CRITICAL_METHOD==3
OS_CPU_SR cpu_sr;
#endif
pdata=pdata;
for(;;)
{
OSTimeDlyHMSM(0,0,1,0);
s="Task2 start!";
PC_DispStr(15,++y,s,DISP_BGND_BLACK+DISP_FGND_WHITE);
OSSemPend(Semp,0,&err);
OSTimeDlyHMSM(0,0,2,0);
s="Task2 completion!";
PC_DispStr(15,++y,s,DISP_BGND_BLACK+DISP_FGND_WHITE);
OSSemPost(Semp);
OSTimeDlyHMSM(0,0,5,0);
}
}
TaskStart创建完Task0,Task1,Task2后挂起自己。
Task0优先级最大,Task2最小。
开始时Task0比Task2晚两秒运行。
Task1比Task2晚一秒运行。
Task2运行开始立刻获取共享资源Semp。
一秒后Task1因优先级高于Task2抢占CPU运行。
两秒后Task0因优先级高于Task1抢占CPU运行。
第三秒,Task0申请共享资源失败挂起。
剩余Task1优先级最高,运行直到结束。
之后Task2运行到结束,释放共享资源。
Task0获取资源后,最后运行结束。
因此,出现Task0为等待Task2释放共享资源,而被Task1这样低优先级无关进程抢占CPU运行的现象。
即为优先级翻转现象。
该函数建立并初始化一个信号量,信号量的作用如下:
函数原型:OSSemCreate( INT16U value);
参数说明:value 参数是所建立的信号量的初始值,可以取0到65535之间的任何值。
返回值:OSSemCreate()函数返回指向分配给所建立的信号量的控制块的指针。如果没有可用的控制块,OSSemCreate()函数返回空指针。
该函数用于任务试图取得设备的使用权、任务需要和其他任务或中断同步、任务需要等待特定事件的发生的场合。如果任务调用OSSemPend()函数时,信号量的值大于零,OSSemPend()函数递减该值并返回该值。如果调用时信号量值等于零,OSSemPend()函数将任务加入该信号量的等待队列。OSSemPend()函数挂起当前任务直到其他的任务或中断设置信号量或超出等待的预期时间。如果在预期的时钟节拍内信号量被设置,μC/OS-Ⅱ默认让最高优先级的任务取得信号量并回到就绪状态。一个被OSTaskSuspend()函数挂起的任务也可以接受信号量,但这个任务将一直保持挂起状态直到通过调用OSTaskResume()函数恢复该任务的运行。
函数原型:Void OSSemPend ( OS_EVNNT *pevent, INT16U timeout, int8u *err );
参数说明:
pevent 是指向信号量的指针。该指针的值在建立该信号量时可以得到。(参考OSSemCreate()函数)。
Timeout 允许一个任务在经过了指定数目的时钟节拍后还没有得到需要的信号量时恢复就绪状态。如果该值为零表示任务将持续地等待信号量,最大的等待时间为65535个时钟节拍。这个时间长度并不是非常严格的,可能存在一个时钟节拍的误差。
Err 是指向包含错误码的变量的指针。
返回值:
OSSemPend()函数返回的错误码可能为下述几种:
该函数用于设置指定的信号量。如果指定的信号量是零或大于零,OSSemPost()函数递增该信号量的值并返回。如果有任何任务在等待该信号量,则最高优先级的任务将得到信号量并进入就绪状态。任务调度函数将进行任务调度,决定当前运行的任务是否仍然为最高优先级的就绪任务。
函数原型:INT8U OSSemPost(OS_EVENT *pevent);
参数说明:pevent 是指向信号量的指针。该指针的值在建立该信号量时可以得到。(参考OSSemCreate()函数)。
返回值:
OSSemPost()函数的返回值为下述之一:
该函数用于将一个任务延时若干个时钟节拍。如果延时时间大于0,系统将立即进行任务调度。延时时间的长度可从0到65535个时钟节拍。延时时间0表示不进行延时,函数将立即返回调用者。延时的具体时间依赖于系统每秒钟有多少个时钟节拍(由文件SO_CFG.H中的OS_TICKS_PER_SEC宏来设定)。
函数原型:void OSTimeDly ( INT16U ticks);
参数说明:ticks为要延时的时钟节拍数。
返回值:无