Swift51.com
麦子学院 头像
麦子学院  2017-05-27 15:44

UCOS学习之互斥信号量

回复:0  查看:2331  

本文和大家分享的主要是UCOS中的互斥信号量相关内容,一起来看看吧,希望对大家学习UCOS有所帮助。

  二值信号量主要用于进行共享资源的独占式访问,比如我们用一个变量来标志一个资源是否可用,当这个变量为1的时候表示资源可用,当这个资源为0的时候表示资源不可用,但是二值信号量容易产生优先级反转,影响系统的实时性。互斥信号量一般用于降解优先级反转,优先级反转就是高优先级的任务的优先级被拉低了。具体如下:

  我们有三个任务Task1Task2,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();

}

UCOS学习之互斥信号量


如上图所示:在任务2获得信号量的时候,任务1恢复就绪态之后因为没有获得信号量而挂起,所以任务3继续执行,直到任务3执行完毕之后,任务1才开始执行。 虽然任务1的优先级最高,但是因为信号量的原因而是任务1的优先级降到任务3的优先级水平。而且任务2加重了优先级反转的程度。

  当我们使用了互斥信号量之后,就可以在某种程度上缓解优先级反转的问题了。当高优先级的任务请求互斥信号量时,如果低优先级的任务占有该信号量,则先提升低优先级任务的优先级,使之尽快执行完以释放互斥信号量,这样高优先级的任务也能尽快执行,在某种程度上缓解了优先级反转问题。

  使用了互斥信号量之后的运行图如下:


UCOS学习之互斥信号量


如图所示, 在任务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