從零開始學習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);
}