原始碼實現主要參考訊息佇列章節,因為底層原始碼是一樣的,所以本章筆記側重點在號誌、互斥量概念。
原始碼部分與訊息佇列重疊的函數不分析。
參考:李柱明部落格
同步,執行完一個再到下一個,一條邏輯流。
非同步,執行者著這個的時候也可執行另外一個,不止一條互相獨立的邏輯流。
資源保護,控制資源的被存取許可權。
在一個多工並行系統中,可能存在多工對共用資源的並行存取,這樣可能會導致資料不可控、非預期。
所以我們需要對共用資源進行保護,而任務同步可以實現對共用資源存取進行保護,維護資料一致性,產出結果達預期。
實現任務同步常見的元件有號誌、互斥量、鎖等等。
同步和互斥的區別:
號誌(Semaphore)也是實現任務間通訊的機制的一種。
可實現任務間同步、臨界資源的互斥存取。
號誌的核心其實就是一個非負值,表示當前資源數量:
但是在freertos核心,一個號誌除了核心的非負值外,還需要其他資料來維護當前號誌的特性、運作。
如讀、寫阻塞連結串列,實現其阻塞機制,號誌內部中斷鎖,實現其中斷存取特性。
號誌細分:
二值號誌既可以用於臨界資源存取也可以用於同步功能。
二值號誌和互斥量其號誌最大都是1,只有0和1兩種狀態,使用邏輯也類似,但是也有區別,主要在內部實現特性和上層應用場景方面:
例子執行條件:
執行過程描述如下:
該圖源自安富萊
建立號誌:
獲取號誌:
釋放號誌:
semGIVE_BLOCK_TIME
個節拍,要麼直接返回釋放失敗。計數號誌的最大資源大於1,主要用於計數。
獲取號誌,號誌值減1;釋放號誌,號誌值加1。
計數訊號通常用於兩種情況:
計算事件:
資源管理:
和二值號誌類似,只是資源最大值不止1。
互斥量是包含優先順序繼承機制的二值號誌。
而二值號誌是實現同步(任務之間或任務與中斷之間)的更好選擇,互斥量是實現簡單互斥的更好選擇。
互斥量就像保護資源的令牌一樣。
當一個任務希望存取該資源時,它必須首先獲得令牌。
當它使用完該資源時,它必須「歸還」令牌——允許其他任務存取相同的資源。
互斥鎖不應該在中斷中使用,因為:
優先順序繼承:高優先順序任務TH在等待低優先順序的任務TL繼承佔用的競爭資源時,為了使TH能夠儘快獲得排程執行,由作業系統把TL的優先順序提高到TH的優先順序,從而讓TL以TH的優先順序參與排程,儘快讓TL執行並釋放調TH欲獲得的競爭資源,然後TL的優先順序調整到繼承前的水平,此時TH可獲得競爭資源而繼續執行。
在FreeRTOS作業系統中為了降低優先順序翻轉問題利用了優先順序繼承演演算法。
不過優先順序繼承也不能解決優先順序反轉。
它只是在某些情況下將其影響降到最低。
舉個栗子:
三個任務:任務A優先順序10,任務B優先順序5,任務C優先順序1。
在任務C佔用互斥量時,任務A就緒,也需要該互斥量,此時任務C的優先順序會繼承任務A的優先順序,從優先順序1躍升到優先順序10。就算當前任務B就緒了,也不能打斷任務C,因為優先順序比10底。
和二值號誌類似,比二值號誌多個優先順序繼承機制。
就是互斥量具有遞迴性。
遞迴使用的互斥量可以被其所有者反覆「獲取」。
互斥量只有在所有者為每個成功的xSemaphoreTakeRecursive()
請求呼叫xSemaphoreGiveRecursive()
之後才會再次可用。
即是互斥量被同一個任務連續申請成功N次,就需要釋放N次才算真正釋放該互斥量。
互斥量型別的號誌不能在中斷服務程式中使用。
因為:
參考互斥量運作機制,比互斥量多個遞迴性。
就是邏輯上獲取一個已經被佔用且邏輯上不可能被釋放的鎖而阻塞,永久阻塞。
避免死鎖需要遵循的規則:
xSemaphoreCreateBinary()
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )
#endif
就是建立一個型別queueQUEUE_TYPE_BINARY_SEMAPHORE
、是佇列成員為1、不含資料區的佇列。
xSemaphoreCreateCounting( uxMaxCount, uxInitialCount )
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
#endif
#if ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount,
const UBaseType_t uxInitialCount )
{
QueueHandle_t xHandle = NULL;
if( ( uxMaxCount != 0 ) && /* 最大號誌不能少於1,這是常識 */
( uxInitialCount <= uxMaxCount ) ) /* 初始值也不能超過最大號誌值 */
{
/* 建立一個型別為queueQUEUE_TYPE_COUNTING_SEMAPHORE、佇列成員為uxMaxCount、且不含資料區的佇列 */
xHandle = xQueueGenericCreate( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE );
if( xHandle != NULL )
{
/* 初始化當前可用資源值 */
( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount;
traceCREATE_COUNTING_SEMAPHORE();
}
else
{
traceCREATE_COUNTING_SEMAPHORE_FAILED();
}
}
else
{
configASSERT( xHandle );
mtCOVERAGE_TEST_MARKER();
}
return xHandle;
}
#endif
xSemaphoreCreateMutex()
:
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
#endif
#if ( ( configUSE_MUTEXES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType )
{
QueueHandle_t xNewQueue;
const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0;
/* 建立互斥量,不含資料區的佇列 */
xNewQueue = xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType );
/* 初始化互斥量, */
prvInitialiseMutex( ( Queue_t * ) xNewQueue );
return xNewQueue;
}
#endif
#if ( configUSE_MUTEXES == 1 )
static void prvInitialiseMutex( Queue_t * pxNewQueue )
{
if( pxNewQueue != NULL )
{
/* 在呼叫xQueueGenericCreate()建立佇列的時候,預設都是佇列,聯合體初始化的是QueuePointers_t xQueue。
所以需要在這裡初始化回SemaphoreData_t xSemaphore,這個成員非常重要,是實現優先順序繼承機制和遞迴互斥量的必要資料。
用於互斥量。 */
/* 互斥量持有者。初始化為NULL */
pxNewQueue->u.xSemaphore.xMutexHolder = NULL;
/* 型別標記為互斥量 */
pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;
/* 遞迴互斥量使用,初始為0 */
pxNewQueue->u.xSemaphore.uxRecursiveCallCount = 0;
traceCREATE_MUTEX( pxNewQueue );
/* 建立後,預設為開鎖狀態 */
( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );
}
else
{
traceCREATE_MUTEX_FAILED();
}
}
#endif /* configUSE_MUTEXES */
#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) )
#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )
#endif
獲取和釋放號誌都是區分任務和中斷呼叫的API的,其主要區別也是中斷呼叫是不能阻塞。
這裡主要分析任務呼叫。(中斷呼叫,按區別理解下就可以了)
二值號誌、計數號誌、互斥量都是使用xSemaphoreTake()
獲取號誌。
而遞迴互斥量使用xSemaphoreTakeRecursive()
獲取互斥量。
#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueSemaphoreTake( ( xSemaphore ), ( xBlockTime ) )
BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue,
TickType_t xTicksToWait )
{
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = xQueue;
#if ( configUSE_MUTEXES == 1 )
BaseType_t xInheritanceOccurred = pdFALSE;
#endif
/* 引數校驗 */
configASSERT( ( pxQueue ) );
/* 號誌是不帶資料區的 */
configASSERT( pxQueue->uxItemSize == 0 );
#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
{
/* 排程器掛起是不能以阻塞式呼叫 */
configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
}
#endif
/* 迴圈方式。是實現阻塞機制的邏輯方式 */
for( ; ; )
{
taskENTER_CRITICAL(); /* 進入臨界 */
{
/* 備份下當前號誌值 */
const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting;
/* 號誌大於0,說明還有資源,可以被獲取佔用 */
if( uxSemaphoreCount > ( UBaseType_t ) 0 )
{
traceQUEUE_RECEIVE( pxQueue );
/* 號誌減1 */
pxQueue->uxMessagesWaiting = uxSemaphoreCount - ( UBaseType_t ) 1;
#if ( configUSE_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) /* 互斥量型別 */
{
/* 儲存當前互斥量持有者。且持有者也儲存佔用互斥量數。 */
pxQueue->u.xSemaphore.xMutexHolder = pvTaskIncrementMutexHeldCount();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_MUTEXES */
/* 如果有任務阻塞在當前號誌的獲取阻塞連結串列中,就解鎖一個,讓其寫入。 */
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE )
{
/* 把這個解除阻塞的任務從當前佇列的寫阻塞連結串列中解除,
並把該任務從延時連結串列或掛起連結串列中恢復到就緒連結串列或掛起的就緒連結串列中 */
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE )
{
/* 解鎖的任務比當前任務優先順序更加高,需要觸發任務排程。 */
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 退出臨界 */
taskEXIT_CRITICAL();
return pdPASS; /* 返回獲取成功 */
}
else /* 如果號誌為空,沒有可用資源。互斥量的話需要處理優先順序繼承機制。 */
{
if( xTicksToWait == ( TickType_t ) 0 ) /* 不阻塞 */
{
#if ( configUSE_MUTEXES == 1 )
{
/* 引數校驗。不阻塞是不會因當前獲取而發生優先順序繼承的。 */
configASSERT( xInheritanceOccurred == pdFALSE );
}
#endif /* configUSE_MUTEXES */
/* 退出臨界,返回獲取失敗。 */
taskEXIT_CRITICAL();
traceQUEUE_RECEIVE_FAILED( pxQueue );
return errQUEUE_EMPTY;
}
else if( xEntryTimeSet == pdFALSE ) /* 首次迴圈,需要開始計時阻塞 */
{
/* 獲取當前系統節拍 */
vTaskInternalSetTimeOutState( &xTimeOut );
xEntryTimeSet = pdTRUE; /* 標記已經開始計時 */
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
taskEXIT_CRITICAL();
/* 退出臨界後系統會先處理在臨界期觸發的被遮蔽的中斷服務,如工作切換的中斷服務、其它中斷服務等等。 */
vTaskSuspendAll(); /* 又回到了當前任務。掛起排程器 */
prvLockQueue( pxQueue ); /* 佇列上鎖 */
/* 檢查阻塞是否已經超時。 */
if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) /* 阻塞未超時 */
{
if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) /* 如果號誌中還沒有資源,需要繼續阻塞 */
{
traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue );
#if ( configUSE_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) /* 互斥量型別 */
{
taskENTER_CRITICAL(); /* 進入臨界 */
{
/* 處理優先順序繼承 */
xInheritanceOccurred = xTaskPriorityInherit( pxQueue->u.xSemaphore.xMutexHolder );
}
taskEXIT_CRITICAL(); /* 退出臨界 */
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* if ( configUSE_MUTEXES == 1 ) */
/* 當前任務進入阻塞,從就緒連結串列中抽離,插入到延時連結串列中,並把該任務插入當前號誌的獲取阻塞連結串列中 */
vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
prvUnlockQueue( pxQueue ); /* 就鎖佇列 */
if( xTaskResumeAll() == pdFALSE ) /* 恢復排程器 */
{
/* 如果在恢復排程器時沒有排程過,這裡必須手動觸發一次排程。 */
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else /* 號誌中有資源了 */
{
/* 需要解鎖當前號誌並恢復排程器,進入下一個迴圈處理 */
prvUnlockQueue( pxQueue );
( void ) xTaskResumeAll();
}
}
else /* 阻塞超時 */
{
/* 解鎖當前號誌 */
prvUnlockQueue( pxQueue );
/* 恢復排程器 */
( void ) xTaskResumeAll();
/* 再次判斷下是否真的沒有資源,現在有資源還來得及 */
if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) /* 確實沒有資源,解除阻塞處理 */
{
#if ( configUSE_MUTEXES == 1 )
{
if( xInheritanceOccurred != pdFALSE ) /* 發生過優先順序繼承 */
{
taskENTER_CRITICAL(); /* 進入臨界 */
{
UBaseType_t uxHighestWaitingPriority;
/* 重置優先順序繼承 */
/* 先解除當前互斥量的優先順序繼承 */
uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout( pxQueue );
/* 再設定新的優先順序繼承 */
vTaskPriorityDisinheritAfterTimeout( pxQueue->u.xSemaphore.xMutexHolder, uxHighestWaitingPriority );
}
taskEXIT_CRITICAL(); /* 退出臨界 */
}
}
#endif /* configUSE_MUTEXES */
traceQUEUE_RECEIVE_FAILED( pxQueue );
return errQUEUE_EMPTY; /* 返回獲取失敗 */
}
else /* 在超時了,退出前發現有資源,可以進入下一個迴圈獲取 */
{
mtCOVERAGE_TEST_MARKER();
}
}
} /*lint -restore */
}
#if ( configUSE_RECURSIVE_MUTEXES == 1 )
#define xSemaphoreTakeRecursive( xMutex, xBlockTime ) xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )
BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex,
TickType_t xTicksToWait )
{
BaseType_t xReturn;
Queue_t * const pxMutex = ( Queue_t * ) xMutex;
/* 引數校驗 */
configASSERT( pxMutex );
traceTAKE_MUTEX_RECURSIVE( pxMutex );
if( pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle() ) /* 如果遞迴互斥量已經被佔用了,持有者是當前任務的話,可以獲取遞迴互斥量成功 */
{
/* 遞迴深度加1 */
( pxMutex->u.xSemaphore.uxRecursiveCallCount )++;
xReturn = pdPASS; /* 返回獲取成功 */
}
else /* 遞迴互斥量沒有被佔用,或者遞迴互斥量已經被佔用,但是持有者不是當前任務 */
{
/* 獲取互斥量處理 */
xReturn = xQueueSemaphoreTake( pxMutex, xTicksToWait );
if( xReturn != pdFAIL ) /* 獲取成功 */
{
/* 遞迴深度加1 */
( pxMutex->u.xSemaphore.uxRecursiveCallCount )++;
}
else
{
traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex );
}
}
return xReturn;
}
#endif
獲取和釋放號誌都是區分任務和中斷呼叫的API的,其主要區別也是中斷呼叫是不能阻塞。
這裡主要分析任務呼叫。(中斷呼叫,按區別理解下就可以了)
二值號誌、計數號誌、互斥量都是使用xSemaphoreGive()
獲取號誌。
而遞迴互斥量使用xSemaphoreGiveRecursive()
獲取互斥量。
注意:互斥量和遞迴互斥量只有持有者才有許可權釋放。
參考訊息佇列章節。
#define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )
#if ( configUSE_RECURSIVE_MUTEXES == 1 )
#define xSemaphoreGiveRecursive( xMutex ) xQueueGiveMutexRecursive( ( xMutex ) )
#endif
#if ( configUSE_RECURSIVE_MUTEXES == 1 )
BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex )
{
BaseType_t xReturn;
Queue_t * const pxMutex = ( Queue_t * ) xMutex;
/* 引數校驗 */
configASSERT( pxMutex );
if( pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle() ) /* 如果遞迴互斥量已經被佔用了,持有者是當前任務的話,可以被釋放 */
{
traceGIVE_MUTEX_RECURSIVE( pxMutex );
/* 遞迴深度建1 */
( pxMutex->u.xSemaphore.uxRecursiveCallCount )--;
/* 遞迴深度為0,說明已經被完全釋放了 */
if( pxMutex->u.xSemaphore.uxRecursiveCallCount == ( UBaseType_t ) 0 )
{
/* 需要真正釋放這個遞迴互斥量 */
( void ) xQueueGenericSend( pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 返回成功 */
xReturn = pdPASS;
}
else /* 遞迴互斥量沒有被佔用,或者遞迴互斥量已經被佔用,但是持有者不是當前任務 */
{
/* 返回釋放失敗 */
xReturn = pdFAIL;
traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex );
}
return xReturn;
}
#endif
vSemaphoreDelete()
用於刪除一個號誌,包括二值號誌,計數號誌,互斥量和遞迴互斥量。
#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) )
注意:當持有者持有多個互斥量時,不能通過單個互斥量來解除或者重置優先順序繼承的優先順序,只能選擇忽略。這種情況也會存在優先順序翻轉。
優先順序繼承機制主要資料:Queue_t->u->xQueue
。
typedef struct SemaphoreData
{
TaskHandle_t xMutexHolder; /* 當前互斥量的持有者 */
UBaseType_t uxRecursiveCallCount; /* 當前互斥量被遞迴呼叫的深度 */
} SemaphoreData_t;
在互斥量被其它任務佔用,當前高優先順序任務因為該互斥量而進入阻塞時,會發生優先繼承,互斥量持有者的任務優先順序會躍升到阻塞在當前接收阻塞連結串列中最高,且比持有者高的任務優先順序。
xTaskPriorityInherit()
:
#if ( configUSE_MUTEXES == 1 )
BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder )
{
TCB_t * const pxMutexHolderTCB = pxMutexHolder;
BaseType_t xReturn = pdFALSE;
/* 互斥鎖已被佔用 */
if( pxMutexHolder != NULL )
{
/* 持有者優先順序比當前任務優先順序低,需要更新優先順序繼承 */
if( pxMutexHolderTCB->uxPriority < pxCurrentTCB->uxPriority )
{
/* 持有者的事件節點值沒有被其它IPC佔用(如事件組元件),方可設定為優先順序相關的值 */
if( ( listGET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
{
/* 重置持有者事件節點值,優先順序升級 */
listSET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 如果持有者處於就緒態,則需要將其移到新的就緒連結串列中 */
if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxMutexHolderTCB->uxPriority ] ), &( pxMutexHolderTCB->xStateListItem ) ) != pdFALSE )
{
if( uxListRemove( &( pxMutexHolderTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) /* 解除任務狀態 */
{
/* 更新任務優先順序點陣圖 */
portRESET_READY_PRIORITY( pxMutexHolderTCB->uxPriority, uxTopReadyPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 優先順序繼承:更新優先順序 */
pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority;
/* 重新插入對應就緒連結串列 */
prvAddTaskToReadyList( pxMutexHolderTCB );
}
else /* 持有者不在就緒態 */
{
/* 直接更新優先順序即可 */
pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority;
}
traceTASK_PRIORITY_INHERIT( pxMutexHolderTCB, pxCurrentTCB->uxPriority );
/* 繼承成功 */
xReturn = pdTRUE;
}
else /* */
{
if( pxMutexHolderTCB->uxBasePriority < pxCurrentTCB->uxPriority )
{
/* 持有者當前優先順序比當前任務高,但是持有者基優先順序比當前任務優先順序低,也是算是繼承成功過 */
xReturn = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
return xReturn;
}
#endif /* configUSE_MUTEXES */
互斥量持有者真正釋放互斥量時,方可解除優先順序繼承
正常由持有者解除:xTaskPriorityDisinherit()
:
#if ( configUSE_MUTEXES == 1 )
BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder )
{
TCB_t * const pxTCB = pxMutexHolder;
BaseType_t xReturn = pdFALSE;
if( pxMutexHolder != NULL )
{
/* 持有者才有許可權解除繼承 */
configASSERT( pxTCB == pxCurrentTCB );
/* 繼承過才能解除繼承 */
configASSERT( pxTCB->uxMutexesHeld );
( pxTCB->uxMutexesHeld )--;
/* 繼承判斷 */
if( pxTCB->uxPriority != pxTCB->uxBasePriority ) /* 繼承過 */
{
/* 只有當持有者這個任務不再持有任何互斥量時,才能解除優先順序繼承。
因為只解除當前的互斥量,但是當前優先順序繼承可能繼承的是其它互斥量的,所以不能直接解除。 */
if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 ) /* 持有者不再佔有任何互斥量 */
{
/* 解除任務狀態 */
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
/* 更新優先順序點陣圖 */
portRESET_READY_PRIORITY( pxTCB->uxPriority, uxTopReadyPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority );
/* 重置優先順序 */
pxTCB->uxPriority = pxTCB->uxBasePriority;
/* 重置任務事件值 */
listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority );
/* 把任務重新新增到就緒連結串列中 */
prvAddTaskToReadyList( pxTCB );
/* 優先順序解除成功 */
xReturn = pdTRUE;
}
}
}
return xReturn;
}
#endif /* configUSE_MUTEXES */
獲取互斥量阻塞阻塞超時時會檢查重置優先順序繼承。
高優先順序任務阻塞超時而解除:
prvGetDisinheritPriorityAfterTimeout()
vTaskPriorityDisinheritAfterTimeout()
prvGetDisinheritPriorityAfterTimeout()
:
#if ( configUSE_MUTEXES == 1 )
static UBaseType_t prvGetDisinheritPriorityAfterTimeout( const Queue_t * const pxQueue )
{
UBaseType_t uxHighestPriorityOfWaitingTasks;
/* 當前互斥量中有任務阻塞在獲取互斥量阻塞連結串列中 */
if( listCURRENT_LIST_LENGTH( &( pxQueue->xTasksWaitingToReceive ) ) > 0U )
{
/* 獲取獲取互斥量阻塞連結串列任務中的最高優先順序 */
uxHighestPriorityOfWaitingTasks = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) listGET_ITEM_VALUE_OF_HEAD_ENTRY( &( pxQueue->xTasksWaitingToReceive ) );
}
else /* 當前互斥量沒有阻塞獲取的任務 */
{
uxHighestPriorityOfWaitingTasks = tskIDLE_PRIORITY; /* 最低 */
}
return uxHighestPriorityOfWaitingTasks;
}
#endif /* configUSE_MUTEXES */
vTaskPriorityDisinheritAfterTimeout()
:
#if ( configUSE_MUTEXES == 1 )
void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder,
UBaseType_t uxHighestPriorityWaitingTask )
{
TCB_t * const pxTCB = pxMutexHolder;
UBaseType_t uxPriorityUsedOnEntry, uxPriorityToUse;
const UBaseType_t uxOnlyOneMutexHeld = ( UBaseType_t ) 1;
if( pxMutexHolder != NULL )
{
/* 引數校驗,持有者必須持有互斥量 */
configASSERT( pxTCB->uxMutexesHeld );
/* 當前互斥量持有者基優先順序低於當前互斥量中阻塞獲取連結串列任務中的最高優先順序 */
if( pxTCB->uxBasePriority < uxHighestPriorityWaitingTask )
{
uxPriorityToUse = uxHighestPriorityWaitingTask; /* 下一個繼承的優先順序 */
}
else
{
uxPriorityToUse = pxTCB->uxBasePriority; /* 需要解除繼承 */
}
/* 目標優先順序和當前優先順序不一致,需要重置優先順序 */
if( pxTCB->uxPriority != uxPriorityToUse )
{
/* 只有持有者只持有當前互斥量才能重置優先順序繼承,因為如果持有者持有多個互斥量時,並不能只參考當前互斥量來重置優先順序 */
if( pxTCB->uxMutexesHeld == uxOnlyOneMutexHeld )
{
/* 確保持有者不是當前任務。否則可能就是一個死鎖的邏輯。斷言得了 */
configASSERT( pxTCB != pxCurrentTCB );
traceTASK_PRIORITY_DISINHERIT( pxTCB, uxPriorityToUse );
/* 備份持有者優先順序 */
uxPriorityUsedOnEntry = pxTCB->uxPriority;
/* 更新持有者優先順序 */
pxTCB->uxPriority = uxPriorityToUse;
/* 持有者的事件節點值沒有被其它IPC佔用(如事件組元件),方可設定為優先順序相關的值 */
if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
{
/* 重置持有者事件節點值,優先順序更新 */
listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriorityToUse );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 如果持有者處於就緒態,就需要更新就緒連結串列 */
if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE )
{
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) /* 解除任務狀態 */
{
/* 更新就緒任務點陣圖 */
portRESET_READY_PRIORITY( pxTCB->uxPriority, uxTopReadyPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 重新插入就緒連結串列 */
prvAddTaskToReadyList( pxTCB );
}
else /* 不是就緒態就不用管 */
{
mtCOVERAGE_TEST_MARKER();
}
}
else /* 持有者持有多個互斥量,也不需要重置優先順序繼承 */
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_MUTEXES */