UCOS学习之互斥信号量
本文和大家分享的主要是UCOS中的互斥信号量相关内容,一起来看看吧,希望对大家学习UCOS有所帮助。
二值信号量主要用于进行共享资源的独占式访问,比如我们用一个变量来标志一个资源是否可用,当这个变量为1的时候表示资源可用,当这个资源为0的时候表示资源不可用,但是二值信号量容易产生优先级反转,影响系统的实时性。互斥信号量一般用于降解优先级反转,优先级反转就是高优先级的任务的优先级被拉低了。具体如下:
我们有三个任务Task1,Task2,Task3,三个任务的优先级依次降低。
void Task1()
{
while(1)
{
OSSemPend(); //获取信号量
......
OSSemPost(); //释放信号量
}
}
void Task2()
{
while(1)
{
//注意任务2不需要信号量
}
}
void Task3()
{
while(1)
{
OSSemPend(); //获取信号量
OSSemPost(); //释放信号量
}
}
void main()
{
OSInit();
CreateTask(Task1); //1 最高
CreateTask(Task2); //2
CreateTask(Task3);
OSStart();
}
如上图所示:在任务2获得信号量的时候,任务1恢复就绪态之后因为没有获得信号量而挂起,所以任务3继续执行,直到任务3执行完毕之后,任务1才开始执行。 虽然任务1的优先级最高,但是因为信号量的原因而是任务1的优先级降到任务3的优先级水平。而且任务2加重了优先级反转的程度。
当我们使用了互斥信号量之后,就可以在某种程度上缓解优先级反转的问题了。当高优先级的任务请求互斥信号量时,如果低优先级的任务占有该信号量,则先提升低优先级任务的优先级,使之尽快执行完以释放互斥信号量,这样高优先级的任务也能尽快执行,在某种程度上缓解了优先级反转问题。
使用了互斥信号量之后的运行图如下:
如图所示, 在任务3执行的过程中,任务1请求互斥信号量,提升任务3的优先级到最高,使任务3尽快执行完,任务3执行完后释放信号量,任务1开始执行。
UCOS_II中互斥信号量相关的函数主要在os_mutex.c中,核心代码如下:
void OSMutexPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
{
INT8U pip; //继承优先级
INT8U mprio; //现在占有该信号量的优先级
BOOLEAN rdy; //当前任务是否就绪标志
OS_TCB *ptcb;
OS_EVENT *pevent2;
INT8U y;
OS_ENTER_CRITICAL();
//该信号量的继承优先级
pip = (INT8U)(pevent->OSEventCnt >> 8);
//该互斥信号量还没有被占用
if ((INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE)
{
pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;
//直接以当前优先级占用该信号量
pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;
pevent->OSEventPtr = (void *)OSTCBCur;
if (OSTCBCur->OSTCBPrio <= pip)
{
OS_EXIT_CRITICAL();
//继承优先级比当前任务的优先级还小
*err = OS_ERR_PIP_LOWER;
}
else
{
OS_EXIT_CRITICAL();
//继承优先级至少比当前任务的优先级高
*err = OS_NO_ERR;
}
return;
}
//占用该信号量的任务的优先级
mprio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);
//现在占有该信号量的任务的TCB
ptcb = (OS_TCB *)(pevent->OSEventPtr);
//占有该信号量的任务的优先级比继承优先级小时才会进行反转
if (ptcb->OSTCBPrio > pip)
{
//占有该信号量的任务的优先级小于当前任务的优先级进行反转
if (mprio > OSTCBCur->OSTCBPrio)
{
//从就绪表中移除现在占有该信号量的任务
y = ptcb->OSTCBY;
if ((OSRdyTbl[y] & ptcb->OSTCBBitX) != 0)
{
OSRdyTbl[y] &= ~ptcb->OSTCBBitX;
if (OSRdyTbl[y] == 0)
{
OSRdyGrp &= ~ptcb->OSTCBBitY;
}
//占有该信号量的任务已经处于就绪态
rdy = OS_TRUE;
}
else //占有该信号量的任务现在在系统的等待列表中
{
//占有该信号量的任务的ECB
pevent2 = ptcb->OSTCBEventPtr;
if (pevent2 != (OS_EVENT *)0)
{
//将占有该信号量的任务从该信号量的等待列表中移除
if ((pevent2->OSEventTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {
pevent2->OSEventGrp &= ~ptcb->OSTCBBitY;
}
}
//占有改信号量的任务现在还没有就绪
rdy = OS_FALSE;
}
//更改占有该信号量的任务的优先级
ptcb->OSTCBPrio = pip;
ptcb->OSTCBY = (INT8U)( ptcb->OSTCBPrio >> 3);
ptcb->OSTCBX = (INT8U)( ptcb->OSTCBPrio & 0x07);
ptcb->OSTCBBitY = (INT8U)(1 << ptcb->OSTCBY);
ptcb->OSTCBBitX = (INT8U)(1 << ptcb->OSTCBX);
if (rdy == OS_TRUE)
{
//如果之前占有该信号量的任务已近处于就绪态了
//就将提升之后的优先级加入到就绪表中
OSRdyGrp |= ptcb->OSTCBBitY;
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
else
{
pevent2 = ptcb->OSTCBEventPtr;
if (pevent2 != (OS_EVENT *)0)
{
//占有该信号量的任务处于等待列表中就把提升
//优先级之后的任务加入到该信号量的等待列表中
pevent2->OSEventGrp |= ptcb->OSTCBBitY;
pevent2->OSEventTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
}
//彻底占用该优先级
OSTCBPrioTbl[pip] = ptcb;
}
}
OSTCBCur->OSTCBStat |= OS_STAT_MUTEX;
OSTCBCur->OSTCBPendTO = OS_FALSE;
OSTCBCur->OSTCBDly = timeout;
OS_EventTaskWait(pevent);
OS_EXIT_CRITICAL();
//重新调度
OS_Sched();
OS_ENTER_CRITICAL();
//该任务等待超时
if (OSTCBCur->OSTCBPendTO == OS_TRUE)
{
OS_EventTO(pevent);
OS_EXIT_CRITICAL();
*err = OS_TIMEOUT;
return;
}
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
}
来源:CSDN