}
如上图所示:在任务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; }
原文:http://blog.csdn.net/csu_stackoverflow/article/details/24321067