FreeRTOS互斥锁
信號量API函數實際上都是宏,它使用現有的隊列機制。這些宏定義在semphr.h文件中。如果使用信號量或者互斥量,需要包含semphr.h頭文件。
信號量包括二值信號量、計數信號量、互斥信號量和遞歸互斥信號量。和普通隊列比起來,信號量雖然沒有隊列項實體,但是信號量值等同于隊列項個數。
?
?
互斥鎖和遞歸互斥鎖:互斥鎖是用來保證共享數據操作的完整性,同時只能有一個任務訪問共享數據。遞歸互斥鎖和普通互斥鎖比起來,同一個任務可以多次獲取遞歸互斥鎖,在釋放同等次數之后才能解鎖。
?
?
在分析互斥鎖之前,先搞清楚兩個概念,優先級反轉和優先級繼承
優先級反轉:互斥鎖被低優先級的任務持有,高優先級任務等待解鎖。這時中等優先級任務一直運行,這導致高優先級任務在等待低優先級任務,而低優先級任務無法執行。這種高優先級等待中優先級的情況,叫做優先級反轉。
優先級繼承:為了解決優先級反轉而提出優化機制,讓低優先級任務臨時繼承高優先級任務的優先級。在低優先級釋放互斥鎖之后,還要恢復原來的優先級。
?
?
先看一下任務TCB
因為優先級繼承機制,在互斥鎖釋放后需要恢復優先級,uxBasePriority在優先級繼承期間被用來保存任務優先級
uxMutexesHeld表示任務持有了多少個互斥鎖
/* 任務TCB */ typedef struct tskTaskControlBlock {volatile StackType_t *pxTopOfStack; /* 棧頂地址 */......ListItem_t xStateListItem; /* 狀態列表項:運行、就緒、掛起、阻塞 */ListItem_t xEventListItem; /* 事件列表項 */UBaseType_t uxPriority; /* 優先級 */StackType_t *pxStack; /* 棧指針 */char pcTaskName[configMAX_TASK_NAME_LEN]; /* 任務名 */......#if (configUSE_MUTEXES == 1)UBaseType_t uxBasePriority; /* 任務基礎優先級 */UBaseType_t uxMutexesHeld; /* 互斥鎖持有數量 */ #endif...... }tskTCB; typedef tskTCB TCB_t;?
?
創建互斥鎖
?互斥鎖實際上是調用了隊列的創建函數,創建好之后調用prvInitialiseMutex來初始化一些互斥鎖特有的變量
#define xSemaphoreCreateMutex() xQueueCreateMutex(queueQUEUE_TYPE_MUTEX) /* 創建互斥鎖 */ 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; }prvInitialiseMutex函數,將互斥鎖初始化為沒有被任何任務持有,并且處于解鎖狀態(隊列中有一個隊列項)
/* 初始化互斥鎖 */ static void prvInitialiseMutex(Queue_t *pxNewQueue) {if(pxNewQueue != 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();} }?
?
獲取互斥鎖
互斥鎖也叫互斥信號量,獲取互斥鎖和獲取信號量使用同一個函數
和普通信號量不同的是:
1.獲取成功時,將互斥鎖持有者設為當前任務,當前任務持有互斥鎖數量加一
2.因互斥鎖被其它任務持有而阻塞時,為了防止優先級反轉現象,進行優先級繼承處理
3.因互斥鎖被其它任務持有而阻塞,超時之后,因為可能進行了優先級繼承,剝奪原先繼承的優先級(剝不剝奪看優先級是否繼承自該任務)
從源代碼看,調用xTaskPriorityInherit函數來進行優先級繼承;在超時之后,使用prvGetDisinheritPriorityAfterTimeout和vTaskPriorityDisinheritAfterTimeout函數來恢復優先級
#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; #endifconfigASSERT((pxQueue));configASSERT(pxQueue->uxItemSize == 0);#if ((INCLUDE_xTaskGetSchedulerState == 1) || (configUSE_TIMERS == 1)){configASSERT(!((xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) && (xTicksToWait != 0)));}#endiffor(;;){/* 進入臨界區 */taskENTER_CRITICAL();{/* 信號量計數 */const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting;/* 信號量值大于0 */if(uxSemaphoreCount > (UBaseType_t)0){traceQUEUE_RECEIVE(pxQueue);/* 信號量值減一 */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/* 等待釋放信號量而阻塞的任務列表不為空 */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;}/* 信號量值為0 */else{/* 等待時間為0 */if(xTicksToWait == (TickType_t)0){#if (configUSE_MUTEXES == 1){configASSERT(xInheritanceOccurred == pdFALSE);}#endif/* 退出臨界區 */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){/* 檢查信號量值是否為0,為0 */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/* 將任務插入等待獲取信號量而阻塞的任務列表中 */vTaskPlaceOnEventList(&(pxQueue->xTasksWaitingToReceive), xTicksToWait);/* 解鎖隊列 */prvUnlockQueue(pxQueue);/* 解除調度器掛起,沒有切換過任務 */if(xTaskResumeAll() == pdFALSE){/* 請求切換任務 */portYIELD_WITHIN_API();}else{mtCOVERAGE_TEST_MARKER();}}/* 隊列不為空,while下一個循環時取走隊列項 */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();}}#endiftraceQUEUE_RECEIVE_FAILED(pxQueue);/* 返回隊列為空錯誤 */return errQUEUE_EMPTY;}/* 隊列不為空,while下一個循環時取走隊列項 */else{mtCOVERAGE_TEST_MARKER();}}} }?
?
釋放互斥鎖
從源代碼來看,看不出和普通信號量處理有什么區別。
但是,因為互斥鎖可能存在優先級繼承的問題,因此釋放互斥鎖時肯定需要恢復優先級。
事實上,恢復優先級在prvCopyDataToQueue函數中進行。
#define xSemaphoreGive(xSemaphore) xQueueGenericSend((QueueHandle_t)(xSemaphore), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK) /* 發送隊列項 */ BaseType_t xQueueGenericSend(QueueHandle_t xQueue, const void *const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition) {BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;TimeOut_t xTimeOut;Queue_t *const pxQueue = xQueue;configASSERT(pxQueue);configASSERT(!((pvItemToQueue == NULL) && (pxQueue->uxItemSize != (UBaseType_t)0U)));configASSERT(!((xCopyPosition == queueOVERWRITE) && (pxQueue->uxLength != 1)));#if ((INCLUDE_xTaskGetSchedulerState == 1) || (configUSE_TIMERS == 1)){configASSERT(!((xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) && (xTicksToWait != 0)));}#endiffor(;;){/* 進入臨界區 */taskENTER_CRITICAL();{/* 目前已插入隊列項數小于最大可插入隊列數或者覆蓋型插入 */if((pxQueue->uxMessagesWaiting < pxQueue->uxLength) || (xCopyPosition == queueOVERWRITE)){traceQUEUE_SEND(pxQueue);#if (configUSE_QUEUE_SETS == 1){UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting;xYieldRequired = prvCopyDataToQueue(pxQueue, pvItemToQueue, xCopyPosition);if(pxQueue->pxQueueSetContainer != NULL){if((xCopyPosition == queueOVERWRITE) && (uxPreviousMessagesWaiting != (UBaseType_t)0)){mtCOVERAGE_TEST_MARKER();}else if(prvNotifyQueueSetContainer(pxQueue, xCopyPosition) != pdFALSE){queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else{if(listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE){if(xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE){queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else if(xYieldRequired != pdFALSE){queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}}#else{/* 根據不同的入隊方式,將隊列項數據拷貝到隊列中 */xYieldRequired = prvCopyDataToQueue(pxQueue, pvItemToQueue, xCopyPosition);/* 等待接收隊列項而阻塞的任務列表不為空 */if(listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE){/* 將任務從事件列表中移除一個任務,并掛接到就緒列表 */if(xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE){/* 請求切換任務 */queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}/* 等待接收隊列項而阻塞的任務列表為空 */else if(xYieldRequired != pdFALSE){queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}#endif/* 退出臨界區 */taskEXIT_CRITICAL();/* 成功 */return pdPASS;}/* 目前隊列已經滿了,且不是覆蓋型插入 */else{/* 阻塞時間為0 */if(xTicksToWait == (TickType_t)0){taskEXIT_CRITICAL();traceQUEUE_SEND_FAILED(pxQueue);/* 返回隊列已滿錯誤 */return errQUEUE_FULL;}/* 當前節拍狀態還未記錄 */else if(xEntryTimeSet == pdFALSE){/* 記錄當前節拍狀態 */vTaskInternalSetTimeOutState(&xTimeOut);/* 當前節拍狀態已經記錄 */xEntryTimeSet = pdTRUE;}else{mtCOVERAGE_TEST_MARKER();}}}/* 退出臨界區 */taskEXIT_CRITICAL();/* 掛起調度器 */vTaskSuspendAll();/* 鎖定隊列 */prvLockQueue(pxQueue);/* 檢查任務是否超時,并未超時 */if(xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE){/* 檢查隊列是否已滿,已經滿了 */if(prvIsQueueFull(pxQueue) != pdFALSE){traceBLOCKING_ON_QUEUE_SEND(pxQueue);/* 將任務掛接到等待發送而阻塞的任務列表中,并將任務掛接到延時列表中 */vTaskPlaceOnEventList(&(pxQueue->xTasksWaitingToSend), xTicksToWait);/* 解鎖隊列 */prvUnlockQueue(pxQueue);/* 解除調度器掛起 */if(xTaskResumeAll() == pdFALSE){/* 請求切換 */portYIELD_WITHIN_API();}}/* 剛好隊列出現空位,下一次while循環重新插入 */else{/* 解鎖隊列 */prvUnlockQueue(pxQueue);/* 解除調度器掛起 */(void)xTaskResumeAll();}}/* 已經超時或者超時之后 */else{/* 解鎖隊列 */prvUnlockQueue(pxQueue);/* 解除調度器掛起 */(void)xTaskResumeAll();traceQUEUE_SEND_FAILED(pxQueue);/* 隊列已滿 */return errQUEUE_FULL;}} }下面看一下prvCopyDataToQueue函數,看看是怎么恢復優先級的
從源代碼來看是通過xTaskPriorityDisinherit函數來恢復優先級
/* 將隊列項拷貝到隊列中 */ static BaseType_t prvCopyDataToQueue(Queue_t *const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition) {BaseType_t xReturn = pdFALSE;UBaseType_t uxMessagesWaiting;/* 隊列中隊列項數 */uxMessagesWaiting = pxQueue->uxMessagesWaiting;/* 隊列項大小為0 */if(pxQueue->uxItemSize == (UBaseType_t)0){#if (configUSE_MUTEXES == 1){/* 隊列類型為互斥鎖 */if(pxQueue->uxQueueType == queueQUEUE_IS_MUTEX){/* 剝奪繼承優先級 */xReturn = xTaskPriorityDisinherit(pxQueue->u.xSemaphore.xMutexHolder);/* 互斥鎖持有者設置為空 */pxQueue->u.xSemaphore.xMutexHolder = NULL;}else{mtCOVERAGE_TEST_MARKER();}}#endif}/* 隊列項大小不為0,從隊列尾部插入 */else if(xPosition == queueSEND_TO_BACK){/* 將隊列項插入隊列 */(void)memcpy((void *)pxQueue->pcWriteTo, pvItemToQueue, (size_t)pxQueue->uxItemSize);/* 將隊列項寫入指針向后偏移一個隊列項 */pxQueue->pcWriteTo += pxQueue->uxItemSize;/* 隊列項寫入指針已經偏移到隊列尾部了 */if(pxQueue->pcWriteTo >= pxQueue->u.xQueue.pcTail){/* 將隊列項寫入指針偏移到頭部 */pxQueue->pcWriteTo = pxQueue->pcHead;}else{mtCOVERAGE_TEST_MARKER();}}/* 隊列項大小不為0,從隊列頭部插入/覆蓋插入 */else{/* 將隊列項插入隊列 */(void)memcpy((void *)pxQueue->u.xQueue.pcReadFrom, pvItemToQueue, (size_t)pxQueue->uxItemSize);/* 將隊列項讀出指針向前偏移一個隊列項 */pxQueue->u.xQueue.pcReadFrom -= pxQueue->uxItemSize;/* 隊列項讀出指針已經偏移到隊列頭部了 */if(pxQueue->u.xQueue.pcReadFrom < pxQueue->pcHead){/* 將隊列項讀出指針偏移到尾部 */pxQueue->u.xQueue.pcReadFrom = (pxQueue->u.xQueue.pcTail - pxQueue->uxItemSize);}else{mtCOVERAGE_TEST_MARKER();}/* 覆蓋式插入 */if(xPosition == queueOVERWRITE){/* 隊列項個數大于0 */if(uxMessagesWaiting > (UBaseType_t)0){/* 因為覆蓋了一個隊列項,所以隊列項數減一 */--uxMessagesWaiting;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}/* 隊列項數加一 */pxQueue->uxMessagesWaiting = uxMessagesWaiting + (UBaseType_t)1;return xReturn; }?
?
創建遞歸互斥鎖
遞歸互斥鎖和互斥鎖的創建過程是一樣的,只是互斥鎖的類型不同
#define xSemaphoreCreateMutex() xQueueCreateMutex(queueQUEUE_TYPE_MUTEX)?
?
獲取遞歸互斥鎖
和互斥鎖不同的是,同一任務可以多次獲取遞歸互斥鎖。因此,主要步驟為:
1.如果當前任務不持有該互斥鎖,則申請持有互斥鎖,遞歸次數加一
2.如果當前任務已經持有該互斥鎖,則直接遞歸次數加一?
/* 獲取遞歸互斥鎖 */ 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()){/* 遞歸次數加一 */(pxMutex->u.xSemaphore.uxRecursiveCallCount)++;xReturn = pdPASS;}/* 互斥鎖持有者不為當前任務 */else{/* 持有該互斥鎖 */xReturn = xQueueSemaphoreTake(pxMutex, xTicksToWait);if(xReturn != pdFAIL){/* 遞歸次數加一 */(pxMutex->u.xSemaphore.uxRecursiveCallCount)++;}else{traceTAKE_MUTEX_RECURSIVE_FAILED(pxMutex);}}return xReturn; }?
?
釋放遞歸互斥鎖
和互斥鎖不同的是,同一任務可以多次獲取遞歸互斥鎖,解除同等次數才能徹底釋放。因此,主要步驟為:
1.遞歸次數減一
2.當遞歸次數減完,徹底釋放互斥鎖
/* 釋放遞歸互斥鎖 */ 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);/* 遞歸次數減一 */(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; }?
?
優先級繼承和剝奪
從上面的分析,我們知道優先級繼承和剝奪函數如下
優先級繼承:xTaskPriorityInherit
互斥鎖阻塞超時后,恢復優先級:prvGetDisinheritPriorityAfterTimeout和vTaskPriorityDisinheritAfterTimeout
解放互斥鎖,恢復優先級:xTaskPriorityDisinherit
?
下面我們一個一個來分析,首先是xTaskPriorityInherit
1.當前任務優先級和互斥鎖持有任務優先級進行比較,將互斥鎖持有任務的優先級更新為更高的那個優先級
2.將互斥鎖持有任務按照新的優先級重新放到就緒列表中
/* 任務優先級繼承 */ BaseType_t xTaskPriorityInherit(TaskHandle_t const pxMutexHolder) {TCB_t *const pxMutexHolderTCB = pxMutexHolder;BaseType_t xReturn = pdFALSE;/* 互斥鎖持有者不為空 */if(pxMutexHolder != NULL){/* 互斥鎖持有者優先級小于當前任務優先級 */if(pxMutexHolderTCB->uxPriority < pxCurrentTCB->uxPriority){/* 將互斥鎖持有者的事件列表項值(任務優先級)設置為當前任務事件列表項值(任務優先級) */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){/* 檢查就緒列表中是否有任務,如果沒有將該優先級從當前任務優先級記錄中清除 */taskRESET_READY_PRIORITY(pxMutexHolderTCB->uxPriority);}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; }?
prvGetDisinheritPriorityAfterTimeout和vTaskPriorityDisinheritAfterTimeout
用法
UBaseType_t uxHighestWaitingPriority;/* 獲取剩下所有等待互斥鎖任務的最高優先級 */ uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout(pxQueue); /* 超時之后剝奪繼承優先級 */ vTaskPriorityDisinheritAfterTimeout(pxQueue->u.xSemaphore.xMutexHolder, uxHighestWaitingPriority);prvGetDisinheritPriorityAfterTimeout獲取超時后可能需要剝奪的優先級
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; }vTaskPriorityDisinheritAfterTimeout超時后剝奪優先級
1.先計算剝奪繼承當前任務優先級后,需要重新設置的繼承優先級
2.檢查互斥鎖持有者的優先級是否繼承自當前任務
3.如果互斥鎖持有者的優先級是否繼承自當前任務,那么就需要重新設置的繼承優先級
4.設置新的優先級,并將互斥鎖持有者從原就緒列表中轉移到新的就緒列表中
注意:FreeRTOS中處理的并不完美,如果互斥鎖持有者如果持有不止一把鎖的話,即使互斥鎖持有者的優先級是否繼承自當前任務也不進行剝奪,這可能導致互斥鎖的持有任務優先級并不完全準確。
/* 超時之后剝奪繼承優先級 */ 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, pxTCB->uxBasePriority);/* 保存任務優先級 */uxPriorityUsedOnEntry = pxTCB->uxPriority;/* 互斥鎖持有者優先級設置為剩下等待互斥鎖任務的最高優先級 */pxTCB->uxPriority = uxPriorityToUse;/* 將事件列表值也改成剩下等待互斥鎖任務的最高優先級 */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){taskRESET_READY_PRIORITY(pxTCB->uxPriority);}else{mtCOVERAGE_TEST_MARKER();}/* 重新添加到新優先級就緒列表中 */prvAddTaskToReadyList(pxTCB);}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();} }?
xTaskPriorityDisinherit,剝奪優先級繼承
1.先判斷是否繼承了優先級
2.如果繼承了優先級,恢復基礎優先級,并將任務根據新優先級從原就緒列表中轉移到新的就緒列表
注意:FreeRTOS中處理的并不完美,如果互斥鎖持有者如果持有不止一把鎖的話,并不會剝奪繼承優先級,這可能導致互斥鎖的持有任務優先級并不完全準確。
/* 剝奪繼承優先級 */ 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){/* 任務持有鎖數量為0 */if(pxTCB->uxMutexesHeld == (UBaseType_t)0){/* 將互斥鎖持有者從就緒列表中移除 */if(uxListRemove(&(pxTCB->xStateListItem)) == (UBaseType_t)0){/* 檢查就緒列表中是否有任務,如果沒有將該優先級從當前任務優先級記錄中清除 */taskRESET_READY_PRIORITY(pxTCB->uxPriority);}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;}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}return xReturn; }?
?
總結
以上是生活随笔為你收集整理的FreeRTOS互斥锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不修条地铁,都不好意思叫自己大城市
- 下一篇: Adidas、金拱门、KFC、乐天玛特,