从零开始学习UCOSII操作系统8--互斥型信号量
1、互斥型信号量的定义
(1)任务可以用互斥型信号量实现对共享资源的独占式处理,互斥型信号量也称为mutex,mutex是二值信号量,不但具有UCOSII普通信号量的机制外,还具有其他的一些特性。
(2)最重要的一点是,可以解除优先级反转的问题。当高优先级的任务需要使用某个共享资源的时候,而该资源已被一个低优先级反转的问题,就会发生优先级反转的问题。对于这个问题:内核将低优先级提升到高于那个高优先级的任务,直到低优先级的任务使用完占用的共享资源。
2、互斥型信号量的使用原理
(1)建立一个互斥型信号量OSMutexCreate()
在使用mutex之前,必须建立它,建立mutex是通过调用函数OSMutexCreate()来完成的。
OS_EVENT *OSMutexCreate (INT8U prio,
INT8U *perr)
{
OS_EVENT *pevent;
#if OS_ARG_CHK_EN > 0u
if (prio >= OS_LOWEST_PRIO) { /* 如果优先级大于最低的优先级 */
*perr = OS_ERR_PRIO_INVALID;
return ((OS_EVENT *)0);
}
#endif
if (OSIntNesting > 0u) { /* See if called from ISR ... */
*perr = OS_ERR_CREATE_ISR; /*不能在一个中断中设置互斥型信号量*/
return ((OS_EVENT *)0);
}
OS_ENTER_CRITICAL();
if (OSTCBPrioTbl[prio] != (OS_TCB *)0) { /* 互斥型信号量优先级存在 */
OS_EXIT_CRITICAL(); /* Task already exist at priority ... */
*perr = OS_ERR_PRIO_EXIST; /* ... inheritance priority */
return ((OS_EVENT *)0);
}
OSTCBPrioTbl[prio] = OS_TCB_RESERVED; /* Reserve the table entry */
pevent = OSEventFreeList; /* 得到一个空闲的链表 */
if (pevent == (OS_EVENT *)0) { /* See if an ECB was available */
OSTCBPrioTbl[prio] = (OS_TCB *)0; /* No, Release the table entry */
OS_EXIT_CRITICAL();
*perr = OS_ERR_PEVENT_NULL; /* No more event control blocks */
return (pevent);
}
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; /* Adjust the free list */
OS_EXIT_CRITICAL();
pevent->OSEventType = OS_EVENT_TYPE_MUTEX;
pevent->OSEventCnt = (INT16U)((INT16U)prio << 8u) | OS_MUTEX_AVAILABLE; /* Resource is avail. */
pevent->OSEventPtr = (void *)0; /*初始化为没有任何的任务拥有这个任务 */
OS_EventWaitListInit(pevent);
*perr = OS_ERR_NONE;
return (pevent);
}
(2)等待一个互斥型信号量(挂起),OSMutexPend()
使用前面的计数信号量。
1、不支持嵌套操作。
2、不支持所有者,任意的任务都能发送释放。
3、无法解决优先级反转的问题。
对于请求信号量的操作有以下的几种操作:
(1)当信号量已经被自己占有的时候,再次请求,就+1
(2)当有一个高优先级的任务已经占有这个信号量的时候,
现在有一个低优先级的任务进来了,显然这个任务是需要进行等待的。
(3)当有一个低优先级的任务已经占有这个信号量的时候,
现在有一个高优先级的任务进来了,显然这个时候,低优先级的任务需要暂停的提高到高优先级的任务中来。
互斥型信号量的请求:
参数定义:
(1)参数1:OS_EVENT pevent :定义一个指针,类型是指向这个事件控制块的指针。
(2)参数2:timeout:超时等待
(3)参数3:perr :返回的输出型参数:代表出错的类型
void OSMutexPend (OS_EVENT *pevent,
INT32U timeout,
INT8U *perr)
{
INT8U pip; /* Priority Inheritance Priority (PIP) */
INT8U mprio; /* Mutex owner priority */
BOOLEAN rdy; /* Flag indicating task was ready */
OS_TCB *ptcb;
OS_EVENT *pevent2;
INT8U y;
/*各种创建的时候的异常的代码*/
OS_ENTER_CRITICAL();
pip = (INT8U)(pevent->OSEventCnt >> 8u);
/* 该信号量继承的优先级*/
/* 当互斥信号量还没有被占用的时候 */
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)
{
/* PIP的优先级必须更小,不然的话,就会发生优先级反转 */
OS_EXIT_CRITICAL(); /* ... than current task! */
*perr = OS_ERR_PIP_LOWER;
}
else
{
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
}
return;
}
mprio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);
/* 占用该信号量的拥有者的任务优先级 */
//事件控制块的指针
ptcb = (OS_TCB *)(pevent->OSEventPtr);
/* 占用该信号量的任务的TCB */
if (ptcb->OSTCBPrio > pip)
{
/* 如果当前所有者优先级比继承的优先级大*/
if (mprio > OSTCBCur->OSTCBPrio)
{
y = ptcb->OSTCBY;
/*并且互斥型信号量拥有者处于就绪状态的话,那么就需要将其从就绪态中移除*/
if ((OSRdyTbl[y] & ptcb->OSTCBBitX) != 0u) { /* See if mutex owner is ready */
OSRdyTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX;
/* 把优先级进行反转之后移除现在占有该信号量的任务 */
if (OSRdyTbl[y] == 0u)
{
/* ... list at current prio */
OSRdyGrp &= (OS_PRIO)~ptcb->OSTCBBitY;
}
rdy = OS_TRUE;
}
else /*占有该信号量的任务现在在事件的等待列表中*/
{
pevent2 = ptcb->OSTCBEventPtr;
if (pevent2 != (OS_EVENT *)0)
{
/* 从事件列表中移除此任务 */
y = ptcb->OSTCBY;
pevent2->OSEventTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX;
if (pevent2->OSEventTbl[y] == 0u) {
pevent2->OSEventGrp &= (OS_PRIO)~ptcb->OSTCBBitY;
}
}
rdy = OS_FALSE;
}
ptcb->OSTCBPrio = pip;
/* Change owner task prio to PIP */
#if OS_LOWEST_PRIO <= 63u
ptcb->OSTCBY = (INT8U)( ptcb->OSTCBPrio >> 3u);
ptcb->OSTCBX = (INT8U)( ptcb->OSTCBPrio & 0x07u);
#else
ptcb->OSTCBY = (INT8U)((INT8U)(ptcb->OSTCBPrio >> 4u) & 0xFFu);
ptcb->OSTCBX = (INT8U)( ptcb->OSTCBPrio & 0x0Fu);
#endif
ptcb->OSTCBBitY = (OS_PRIO)(1uL << ptcb->OSTCBY);
ptcb->OSTCBBitX = (OS_PRIO)(1uL << ptcb->OSTCBX);
if (rdy == OS_TRUE)
{
/*如果之前占有该信号量的任务已经接近于就绪态了
就将提升之后的优先级加入到就绪表中*/
OSRdyGrp |= ptcb->OSTCBBitY; /* ... make it ready at new priority. */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
else
{
pevent2 = ptcb->OSTCBEventPtr;
if (pevent2 != (OS_EVENT *)0) {
/* Add to event wait list */
pevent2->OSEventGrp |= ptcb->OSTCBBitY;
pevent2->OSEventTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
}
}
OSTCBPrioTbl[pip] = ptcb;
}
}
OSTCBCur->OSTCBStat |= OS_STAT_MUTEX;
/* Mutex not available, pend current task */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;
OSTCBCur->OSTCBDly = timeout;
/* Store timeout in current task's TCB */
OS_EventTaskWait(pevent);
/* Suspend task until event or timeout occurs */
OS_EXIT_CRITICAL();
OS_Sched();
/* 因为已经切换任务了,查找最高优先级的任务 */
OS_ENTER_CRITICAL();
//如果事件控制块的指针是指向挂起状态的
switch (OSTCBCur->OSTCBStatPend)
{
/* See if we timed-out or aborted */
case OS_STAT_PEND_OK:
*perr = OS_ERR_NONE;
break;
case OS_STAT_PEND_ABORT:
*perr = OS_ERR_PEND_ABORT; /* Indicate that we aborted getting mutex */
break;
case OS_STAT_PEND_TO:
default:
OS_EventTaskRemove(OSTCBCur, pevent);
*perr = OS_ERR_TIMEOUT; /* Indicate that we didn't get mutex within TO */
break;
}
OSTCBCur->OSTCBStat = OS_STAT_RDY;
/* Set task status to ready */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK;
/* Clear pend status */
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;
/* Clear event pointers */
#if (OS_EVENT_MULTI_EN > 0u)
OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#endif
OS_EXIT_CRITICAL();
}
互斥型信号量释放的操作函数:
只有在同一个任务中请求互斥型函数,才能同一个任务中释放互斥型函数。
INT8U OSMutexPost (OS_EVENT *pevent)
{
INT8U pip;
/* Priority inheritance priority */
INT8U prio;
/* 异常处理 */
OS_ENTER_CRITICAL();
pip = (INT8U)(pevent->OSEventCnt >> 8u);
/*得到互斥型信号量的优先级 */
prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);
/* 得到拥有者原来的优先级 */
if (OSTCBCur != (OS_TCB *)pevent->OSEventPtr)
{
/* 进入到这个函数中的话,说明请求互斥型信号量的操作,被其他的任务释放,这是不符合互斥型信号量的,返回一个错误值 */
OS_EXIT_CRITICAL();
return (OS_ERR_NOT_MUTEX_OWNER);
}
if (OSTCBCur->OSTCBPrio == pip)
{
/* 查看占用mutex的任务的优先级是否已经升到了PIP,因为有一个更高的优先级的任务也需要整个mutex,在这种情况下,占用mutex的任务被将回到原来的优先级 */
OSMutex_RdyAtPrio(OSTCBCur, prio);
}
OSTCBPrioTbl[pip] = OS_TCB_RESERVED;
/* Reserve table entry */
/*如果互斥性信号量存在的话*/
if (pevent->OSEventGrp != 0u)
{
/* 有没有其他的任务在等待这个信号量 */
/* Yes, Make HPT waiting for mutex ready */
prio = OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_OK);
pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;
/* 保存新的拥有者的优先级 */
pevent->OSEventCnt |= prio;
pevent->OSEventPtr = OSTCBPrioTbl[prio];
/* Link to new mutex owner's OS_TCB */
if (prio <= pip)
{
/* PIP 'must' have a SMALLER prio ... */
OS_EXIT_CRITICAL();
/* ... than current task! */
OS_Sched();
/* Find highest priority task ready to run */
return (OS_ERR_PIP_LOWER);
}
else
{
OS_EXIT_CRITICAL();
OS_Sched();
/* Find highest priority task ready to run */
return (OS_ERR_NONE);
}
}
pevent->OSEventCnt |= OS_MUTEX_AVAILABLE; /* No, Mutex is now available */
pevent->OSEventPtr = (void *)0;
OS_EXIT_CRITICAL();
return (OS_ERR_NONE);
}
(3)无等待的获取互斥型信号量(任务不挂起)OSMutexAccept();
相当于查询的功能:
参数1:事件控制块的指针:
参数2:范回的错误值:
BOOLEAN OSMutexAccept (OS_EVENT *pevent,
INT8U *perr)
{
INT8U pip;
/* 异常处理 */
OS_ENTER_CRITICAL();
/* Get value (0 or 1) of Mutex */
pip = (INT8U)(pevent->OSEventCnt >> 8u);
/* 得到互斥型信号量的优先级 */
if ((pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE)
{
pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;
/* Mask off LSByte (Acquire Mutex) */
pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;
/* 如果互斥型信号量有效,那么OSMutex把调用该函数任务的优先级写到
OSEventCnt的低8位,并将mutex的事件控制块ECB链接到该任务的任务控制块。 */
pevent->OSEventPtr = (void *)OSTCBCur;
/* Link TCB of task owning Mutex */
if (OSTCBCur->OSTCBPrio <= pip)
{
/* PIP 'must' have a SMALLER prio ... */
OS_EXIT_CRITICAL();
/* ... than current task! */
*perr = OS_ERR_PIP_LOWER;
}
else
{
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
}
return (OS_TRUE);
}
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
return (OS_FALSE);
}