
ID:技術讓夢想更偉大
整理:李肖遙
FreeRTOS的信號量包括二進制信號量、計數信號量、互斥信號量(以后簡稱互斥量)和遞歸互斥信號量(以后簡稱遞歸互斥量)。
關于它們的區別可以參考《 FreeRTOS系列第19篇---FreeRTOS信號量》一文。
信號量API函數實際上都是宏,它使用現有的隊列機制。這些宏定義在semphr.h文件中。如果使用信號量或者互斥量,需要包含semphr.h頭文件。
二進制信號量、計數信號量和互斥量信號量的創建API函數是獨立的,但是獲取和釋放API函數都是相同的;
遞歸互斥信號量的創建、獲取和釋放API函數都是獨立的。
在《FreeRTOS高級篇5---FreeRTOS隊列分析》中,我們分析了隊列的實現過程,包括隊列創建、入隊和出隊操作。
在那篇文章中我們說過,創建隊列API函數實際是調用通用隊列創建函數xQueueGenericCreate()來實現的。
其實,不但創建隊列實際調用通用隊列創建函數,二進制信號量、計數信號量、互斥量和遞歸互斥量也都直接或間接使用這個函數,如表1-1所示。
表1-1中紅色字體表示是間接調用xQueueGenericCreate()函數。

二進制信號量創建實際上是直接使用通用隊列創建函數xQueueGenericCreate()。創建二進制信號量API接口實際上是一個宏,定義如下:
#define xSemaphoreCreateBinary() \
xQueueGenericCreate( \
( UBaseType_t ) 1, \
semSEMAPHORE_QUEUE_ITEM_LENGTH, \
NULL, \
NULL, \
queueQUEUE_TYPE_BINARY_SEMAPHORE\
)
通過這個宏定義我們知道創建二進制信號量實際上是創建了一個隊列,隊列項有1個,但是隊列項的大小為0(宏semSEMAPHORE_QUEUE_ITEM_LENGTH定義為0)。
有了隊列創建的知識,我們可以很容易的畫出初始化后的二進制信號量內存,如圖1-1所示。

或許不止一人像我一樣奇怪,創建一個沒有隊列項存儲空間的隊列,「信號量用什么表示?」
其實二進制信號量的釋放和獲取都是通過操作隊列結構體成員uxMessageWaiting來實現的(圖1-1紅色部分,uxMessageWaiting表示隊列中當前隊列項的個數)。
經過初始化后,變量uxMessageWaiting為0,這說明隊列為空,也就是信號量處于無效狀態。
在使用API函數xSemaphoreTake()獲取信號之前,需要先釋放一個信號量。后面講到二進制信號量釋放和獲取時還會詳細介紹。
創建計數信號量間接使用通用隊列創建函數xQueueGenericCreate()。創建計數信號量API接口同樣是個宏定義:
#define xSemaphoreCreateCounting(uxMaxCount, uxInitialCount ) \
xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ), (NULL ) )
創建計數信號量API接口有兩個參數,含義如下:
「uxMaxCount」:最大計數值,當信號到達這個值后,就不再增長了。
「uxInitialCount」:創建信號量時的初始值。
我們來看一下函數xQueueCreateCountingSemaphore()如何實現的:
QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_tuxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue )
{
QueueHandle_t xHandle;
configASSERT( uxMaxCount != 0 );
configASSERT( uxInitialCount <= uxMaxCount );
/*調用通用隊列創建函數*/
xHandle =xQueueGenericCreate(
uxMaxCount,
queueSEMAPHORE_QUEUE_ITEM_LENGTH,
NULL,
pxStaticQueue,
queueQUEUE_TYPE_COUNTING_SEMAPHORE );
if( xHandle != NULL )
{
( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount;
}
configASSERT( xHandle );
return xHandle;
}
從代碼可以看出,創建計數信號量仍然調用通用隊列創建函數xQueueGenericCreate()來創建一個隊列,隊列項的數目由參數uxMaxCount指定,每個隊列項的大小由宏queueSEMAPHORE_QUEUE_ITEM_LENGTH指出,我們找到這個宏定義發現,這個宏被定義為0,也就是說創建的隊列只有隊列數據結構存儲空間而沒有隊列項存儲空間。
如果隊列創建成功,則將隊列結構體成員uxMessageWaiting設置為初始計數信號量值。初始化后的計數信號量內存如圖1-2所示。

創建互斥量間接使用通用隊列創建函數xQueueGenericCreate()。創建互斥量API接口同樣是個宏,定義如下:
#define xSemaphoreCreateMutex() \
xQueueCreateMutex( queueQUEUE_TYPE_MUTEX, NULL )
其中,宏queueQUEUE_TYPE_MUTEX用于通用隊列創建函數,表示創建隊列的類型是互斥量,在文章《FreeRTOS高級篇5---FreeRTOS隊列分析》關于通用隊列創建函數參數說明中提到了這個宏。
我們來看一下函數xQueueCreateMutex()是如何實現的:
#if ( configUSE_MUTEXES == 1 )
QueueHandle_t xQueueCreateMutex( const uint8_tucQueueType, StaticQueue_t *pxStaticQueue )
{
Queue_t *pxNewQueue;
const UBaseType_tuxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0;
/* 防止編譯器產生警告信息 */
( void ) ucQueueType;
/*調用通用隊列創建函數*/
pxNewQueue = ( Queue_t * )xQueueGenericCreate( uxMutexLength, uxMutexSize, NULL, pxStaticQueue, ucQueueType );
/* 成功分配新的隊列結構體? */
if( pxNewQueue != NULL )
{
/*xQueueGenericCreate()函數會按照通用隊列的方式設置所有隊列結構體成員,但是我們是要創建互斥量.因此需要對一些結構體成員重新賦值. */
pxNewQueue->pxMutexHolder = NULL;
pxNewQueue->uxQueueType =queueQUEUE_IS_MUTEX; //NULL
/* 用于遞歸互斥量創建 */
pxNewQueue->u.uxRecursiveCallCount = 0;
/* 使用一個預期狀態啟動信號量 */
( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK);
}
return pxNewQueue;
}
#endif /* configUSE_MUTEXES */
這個函數是帶條件編譯的,只有將宏configUSE_MUTEXES定義為1才會編譯這個函數。
函數首先調用通用隊列創建函數xQueueGenericCreate()來創建一個隊列,隊列項數目為1,隊列項大小為0,說明創建的隊列只有隊列數據結構存儲空間而沒有隊列項存儲空間。
如果隊列創建成功,通用隊列創建函數還會按照通用隊列的方式 初始化所有隊列結構體成員。
但是這里要創建的是互斥量,所以有一些結構體成員必須重新賦值。
在這段代碼中,可能你會疑惑,隊列結構體成員中,并沒有pxMutexHolder和uxQueueType!
其實這兩個標識符只是宏定義,是專門為互斥量而定義的,如下所示:
#define pxMutexHolder pcTail
#define uxQueueType pcHead
#define queueQUEUE_IS_MUTEX NULL
當隊列結構體用于互斥量時,成員pcHead和pcTail指針就不再需要,并且將pcHead指針設置為NULL,表示pcTail指針實際指向互斥量持有者任務TCB(如果有的話)。
最后調用函數xQueueGenericSend()釋放一個互斥量,相當于互斥量創建后是有效的,可以直接使用獲取信號量API函數來獲取這個互斥量。
如果某資源同時只準一個任務訪問,可以用互斥量保護這個資源。
這個資源一定是存在的,所以創建互斥量時會先釋放一個互斥量,表示這個資源可以使用。
任務想訪問資源時,先獲取互斥量,等使用完資源后,再釋放它。
也就是說互斥量一旦創建好后,要先獲取,后釋放,要在同一個任務中獲取和釋放。
這也是互斥量和二進制信號量的一個重要區別,二進制信號量可以在隨便一個任務中獲取或釋放,然后也可以在任意一個任務中釋放或獲取。
「互斥量不同于二進制信號量的還有」:互斥量具有優先級繼承機制,二進制信號量沒有,互斥量不可以用于中斷服務程序,二進制信號量可以。
初始化后的互斥量內存如圖1-3所示。

創建遞歸互斥量間接使用通用隊列創建函數xQueueGenericCreate()。創建遞歸互斥量API接口同樣是個宏,定義如下:
#definexSemaphoreCreateRecursiveMutex() \
xQueueCreateMutex(queueQUEUE_TYPE_RECURSIVE_MUTEX, NULL )
其中,宏queueQUEUE_TYPE_RECURSIVE_MUTEX用于通用隊列創建函數,表示創建隊列的類型是遞歸互斥量,在文章《FreeRTOS高級篇5---FreeRTOS隊列分析》關于通用隊列創建函數參數說明中提到了這個宏。
創建互斥量和創建遞歸互斥量是調用的同一個函數xQueueCreateMutex();
至于參數queueQUEUE_TYPE_RECURSIVE_MUTEX,我們在FreeRTOS一文中已經知道,它只是用于可視化調試;
因此創建互斥量和創建遞歸互斥量可以看作是一樣的,初始化后的遞歸互斥量對象內存也和互斥量一樣,如圖1-3所示。
無論二進制信號量、計數信號量還是互斥量,它們都使用相同的獲取和釋放API函數。釋放信號量用于使信號量有效,分為不帶中斷保護和帶中斷保護兩個版本。
用于釋放一個信號量,不帶中斷保護。被釋放的信號量可以是二進制信號量、計數信號量和互斥量。
注意遞歸互斥量并不能使用這個API函數釋放。其實信號量釋放是一個宏,真正調用的函數是xQueueGenericSend(),宏定義如下:
#definexSemaphoreGive( xSemaphore ) \
xQueueGenericSend( \
( QueueHandle_t ) ( xSemaphore ), \
NULL, \
semGIVE_BLOCK_TIME, \
queueSEND_TO_BACK )
可以看出釋放信號量實際上是一次入隊操作,并且阻塞時間為0(由宏semGIVE_BLOCK_TIME定義)。
對于二進制信號量和計數信號量,根據上一章的內容可以總結出,釋放一個信號量的過程實際上可以簡化為兩種情況:
「第一」,如果隊列未滿,隊列結構體成員uxMessageWaiting加1,判斷是否有阻塞的任務,有的話解除阻塞,然后返回成功信息(pdPASS);
「第二」,如果隊列滿,返回錯誤代碼(err_QUEUE_FULL),表示隊列滿。
對于互斥量要復雜些,因為互斥量具有優先級繼承機制。
「優先級繼承是個什么過程呢?」
我們舉個例子。某個資源X同時只能有一個任務訪問,現在有任務A和任務C都要訪問這個資源,任務A的優先級為1,任務C的優先級為10,所以任務C的優先級大于任務A的優先級。
我們用互斥量保護資源X,并且當前任務A正在訪問資源X。
在任務A訪問資源X的過程中,來了一個中斷,中斷事件使得任務C執行。
任務C執行的過程中,也想訪問資源X,但是因為資源X還被任務A獨占著,所以任務C無法獲取互斥量,會進入阻塞狀態。
此時,低優先級任務A會繼承高優先級任務C的優先級,任務A的優先級臨時的被提升,優先級變成10。這個機制能夠將已經發生的優先級反轉影響降低到最小。
「那么什么是優先級反轉呢?」
還是看上面的例子,任務C的優先級高于任務A,但是任務C因為沒有獲得互斥量而進入阻塞,只能等待低優先級的任務A釋放互斥量后才能運行,這種情況就是優先級反轉。
「那為什么優先級繼承可以降低優先級反轉的影響呢?」
還是看上面的例子,不過我們再增加一個優先級為5的任務B,這三個任務都處于就緒狀態。
如果沒有優先級繼承機制,三個任務的優先級順序為任務C>任務B>任務A。
當任務C因為得不到互斥量而阻塞后,任務B會獲取CPU權限,等到任務B主動或被動讓出CPU后,任務A才會執行,任務A釋放互斥量后,任務C才能得到運行。
再看一下有優先級繼承的情況,當任務C因為得不到互斥量而阻塞后,任務A繼承任務C的優先級,現在三個任務的優先級順序為任務C=任務A>任務B。
當任務C因為得不到互斥量而阻塞后,任務A會獲得CPU權限,等到任務A釋放互斥量后,任務C就會得到運行。看,任務C等待的時間變短了。
有了上面的基礎理論,我們就很好理解為什么釋放互斥量會比較復雜了。
「還是可以簡化為兩種情況:」
「第一」,如果隊列未滿,除了隊列結構體成員uxMessageWaiting加1外,還要判斷獲取互斥量的任務是否有優先級繼承,如果有的話,還要將任務的優先級恢復到原始值。當然,恢復到原來值也是有條件的,就是該任務必須在沒有使用其它互斥量的情況下,才能將繼承的優先級恢復到原始值。然后判斷是否有阻塞的任務,有的話解除阻塞,最后返回成功信息(pdPASS);
「第二」,如果如果隊列滿,返回錯誤代碼(err_QUEUE_FULL),表示隊列滿。
用于釋放一個信號量,帶中斷保護。被釋放的信號量可以是二進制信號量和計數信號量。
和普通版本的釋放信號量API函數不同,它不能釋放互斥量,這是因為互斥量不可以在中斷中使用!
互斥量的優先級繼承機制只能在任務中起作用,在中斷中毫無意義。帶中斷保護的信號量釋放其實也是一個宏,真正調用的函數是xQueueGiveFromISR (),宏定義如下:
#definexSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) \
xQueueGiveFromISR( \
( QueueHandle_t ) ( xSemaphore), \
( pxHigherPriorityTaskWoken ) )
我們看真正被調用的函數源碼(經過整理后的):
BaseType_t xQueueGiveFromISR(
QueueHandle_t xQueue,
BaseType_t * constpxHigherPriorityTaskWoken )
{
BaseType_t xReturn;
UBaseType_t uxSavedInterruptStatus;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
uxSavedInterruptStatus =portSET_INTERRUPT_MASK_FROM_ISR();
{
/*當隊列用于實現信號量時,永遠不會有數據出入隊列,但是任然要檢查隊列是否為空 */
if( pxQueue->uxMessagesWaiting < pxQueue->uxLength )
{
/* 一個任務可以獲取多個互斥量,但是只能有一個繼承優先級,如果任務是互斥量的持有者,則互斥量不允許在中斷服務程序中釋放.因此這里不需要判斷是否要恢復任務的原始優先級值,只是簡單更新隊列項計數器. */
++( pxQueue->uxMessagesWaiting );
/* 如果列表上鎖,不能改變隊列的事件列表. */
if( pxQueue->xTxLock == queueUNLOCKED )
{
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive) ) == pdFALSE )
{
if(xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive) ) != pdFALSE )
{
/* 解除阻塞的任務有更高優先級,因此記錄上下文切換請求*/
if(pxHigherPriorityTaskWoken != NULL )
{
*pxHigherPriorityTaskWoken= pdTRUE;
}
}
}
}
else
{
/* Increment thelock count so the task that unlocks the queue
knows that data wasposted while it was locked. */
++( pxQueue->xTxLock );
}
xReturn = pdPASS;
}
else
{
xReturn = errQUEUE_FULL;
}
}
portCLEAR_INTERRUPT_MASK_FROM_ISR(uxSavedInterruptStatus );
return xReturn;
}
因為不涉及互斥量,不涉及阻塞,函數xQueueGiveFromISR()異常簡單,如果隊列滿,直接返回錯誤代碼(err_QUEUE_FULL);
如果隊列未滿,則將隊列結構體成員uxMessageWaiting加1,然后視隊列是否上鎖而決定是否解除任務阻塞(如果有得話)。
如果你覺得難以理解,則需要先看看《FreeRTOS高級篇5---FreeRTOS隊列分析》。
無論二進制信號量、計數信號量還是互斥量,它們都使用相同的獲取和釋放API函數。
釋獲取信號量會消耗信號量,如果獲取信號量失敗,任務可能會阻塞,阻塞時間由函數參數xBlockTime指定,如果為0,則直接返回,不阻塞。
獲取信號量分為不帶中斷保護和帶中斷保護兩個版本。
用于獲取信號量,不帶中斷保護。獲取的信號量可以是二進制信號量、計數信號量和互斥量。
注意遞歸互斥量并不能使用這個API函數獲取。其實獲取信號量是一個宏,真正調用的函數是xQueueGenericReceive (),宏定義如下:
#definexSemaphoreTake( xSemaphore, xBlockTime ) \
xQueueGenericReceive( \
( QueueHandle_t ) ( xSemaphore ), \
NULL, \
( xBlockTime ), \
pdFALSE )
通過上面的宏定義可以看出,獲取信號量實際上是執行出隊操作。
「對于二進制信號量和計數信號量,可以簡化為三種情況:」
「第一」,如果隊列不為空,隊列結構體成員uxMessageWaiting減1,判斷是否有因入隊而阻塞的任務,有的話解除阻塞,然后返回成功信息(pdPASS);
「第二」,如果隊列為空并且阻塞時間為0,則直接返回錯誤碼(errQUEUE_EMPTY),表示隊列為空;
「第三」,如果隊列為空并且阻塞時間不為0,則任務會因為等待信號量而進入阻塞狀態,任務會被掛接到延時列表中。
「對于互斥量,也可以簡化為三種情況」,但是過程要復雜一些:
「第一」,如果隊列不為空,隊列結構體成員uxMessageWaiting減1、將當前任務TCB結構體成員uxMutexesHeld加1,表示任務獲取互斥量的個數、將隊列結構體成員指針pxMutexHolder指向任務TCB、判斷是否有因入隊而阻塞的任務,有的話解除阻塞,然后返回成功信息(pdPASS);
「第二」,如果隊列為空并且阻塞時間為0,則直接返回錯誤碼(errQUEUE_EMPTY),表示隊列為空;
「第三」,如果隊列為空并且阻塞時間不為0,則任務會因為等待信號量而進入阻塞狀態,在將任務掛接到延時列表之前,會判斷當前任務和擁有互斥量的任務優先級哪個高,如果當前任務優先級高,則擁有互斥量的任務繼承當前任務優先級。
用于獲取信號量,帶中斷保護。獲取的信號量可以是二進制信號量和計數信號量。
和普通版本的獲取信號量API函數不同,它不能獲取互斥量,這是因為互斥量不可以在中斷中使用!
互斥量的優先級繼承機制只能在任務中起作用,在中斷中毫無意義。
帶中斷保護的獲取信號量其實也是一個宏,真正調用的函數是xQueueReceiveFromISR (),宏定義如下:
#definexSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken ) \
xQueueReceiveFromISR( \
( QueueHandle_t ) ( xSemaphore ), \
NULL, \
( pxHigherPriorityTaskWoken ) )
同樣因為不涉及互斥量,不涉及阻塞,函數xQueueReceiveFromISR ()同樣異常簡單:如果隊列為空,直接返回錯誤代碼(pdFAIL);
如果隊列非空,則將隊列結構體成員uxMessageWaiting減1,然后視隊列是否上鎖而決定是否解除任務阻塞(如果有得話)。
函數xSemaphoreGiveRecursive()用于釋放一個遞歸互斥量。
已經獲取遞歸互斥量的任務可以重復獲取該遞歸互斥量。
使用xSemaphoreTakeRecursive() 函數成功獲取幾次遞歸互斥量,就要使用xSemaphoreGiveRecursive()函數返還幾次,在此之前遞歸互斥量都處于無效狀態。
比如,某個任務成功獲取5次遞歸互斥量,那么在它沒有返還5次該遞歸互斥量之前,這個互斥量對別的任務無效。
像其它信號量一樣,xSemaphoreGiveRecursive()也是一個宏定義,它最終使用現有的隊列機制,實際執行的函數是xQueueGiveMutexRecursive(),這個宏定義如下所示:
#definexSemaphoreGiveRecursive( xMutex ) \
xQueueGiveMutexRecursive( (xMutex ) )
我們重點來看函數xQueueGiveMutexRecursive()的實現過程。經過整理后(去除跟蹤調試語句)的源碼如下所示:
#if ( configUSE_RECURSIVE_MUTEXES == 1 )
BaseType_txQueueGiveMutexRecursive( QueueHandle_t xMutex )
{
BaseType_t xReturn;
Queue_t * const pxMutex = ( Queue_t * ) xMutex;
/* 互斥量和遞歸互斥量要在同一個任務中獲取和釋放,當獲取互斥量或遞歸互斥量時,隊列結構體成員指針pxMutexHolder指向獲取互斥量或遞歸互斥量的任務TCB,所以在釋放遞歸互斥量時需要檢查這個指針指向的TCB是否是和當前任務TCB相同,如果不相同是不能釋放這個遞歸互斥量的! 注:釋放互斥量時,這個檢查不是必須的,FreeRTOS的作者將這個檢查放在了斷言中(configASSERT( pxTCB == pxCurrentTCB);).*/
if( pxMutex->pxMutexHolder == ( void * )xTaskGetCurrentTaskHandle() )
{
/* 每當任務獲取遞歸互斥量時,隊列結構體成員u.uxRecursiveCallCount會加1,互斥量不會使用這個變量,它用來保存遞歸次數.所以,在釋放遞歸互斥量的時候要將它減1*/
( pxMutex->u.uxRecursiveCallCount)--;
/* 遞歸計數器為0? */
if( pxMutex->u.uxRecursiveCallCount == ( UBaseType_t ) 0 )
{
/* 調用入隊函數釋放一個互斥量,注意阻塞時間(由宏queueMUTEX_GIVE_BLOCK_TIME定義)為0 */
( void ) xQueueGenericSend( pxMutex, NULL,queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK );
}
xReturn = pdPASS;
}
else
{
/* 如果不是本任務擁有這個互斥量,則直接返回錯誤碼 */
xReturn = pdFAIL;
}
return xReturn;
}
#endif /* configUSE_RECURSIVE_MUTEXES */
這個函數是帶條件編譯的,只有將宏configUSE_RECURSIVE_MUTEXES定義為1才會編譯這個函數。
互斥量和遞歸互斥量的最大區別在于一個遞歸互斥量可以被已經獲取這個遞歸互斥量的任務重復獲取,這個遞歸調用功能是通過隊列結構體成員u.uxRecursiveCallCount實現的。
這個變量用于存儲遞歸調用的次數,每次獲取遞歸互斥量后,這個變量加1,在釋放遞歸互斥量后,這個變量減1。
只有這個變量減到0,即釋放和獲取的次數相等時,互斥量才能再次有效,使用入隊函數釋放一個遞歸互斥量。
函數xSemaphoreTakeRecursive()用于獲取一個遞歸互斥量。像其它信號量一樣,xSemaphoreTakeRecursive()也是一個宏定義,它最終使用現有的隊列機制,實際執行的函數是xQueueTakeMutexRecursive(),這個宏定義如下所示:
#definexSemaphoreTakeRecursive( xMutex, xBlockTime ) \
xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )
獲取遞歸互斥量具有阻塞超時參數,如果互斥量正被別的任務使用,可以阻塞設定的時間。
我們重點來看函數xQueueTakeMutexRecursive()的實現過程。經過整理后(去除跟蹤調試語句)的源碼如下所示:
#if ( configUSE_RECURSIVE_MUTEXES == 1 )
BaseType_txQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_txTicksToWait )
{
BaseType_t xReturn;
Queue_t * const pxMutex = ( Queue_t * ) xMutex;
/*互斥量和遞歸互斥量要在同一個任務中獲取和釋放,遞歸互斥量可以在一個任務中多次獲取,當第一次獲取遞歸互斥量時,隊列結構體成員指針pxMutexHolder指向獲取遞歸互斥量的任務TCB,在此獲取這個遞歸互斥量時,如果這個指針指向的TCB和當前任務TCB相同,只需要將遞歸次數計數器u.uxRecursiveCallCount加1即可,不需要再操作隊列.*/
if( pxMutex->pxMutexHolder == ( void * )xTaskGetCurrentTaskHandle() )
{
( pxMutex->u.uxRecursiveCallCount)++;
xReturn = pdPASS;
}
else
{
/*調用出隊函數*/
xReturn =xQueueGenericReceive( pxMutex, NULL, xTicksToWait, pdFALSE );
/* 成功獲取遞歸互斥量后,要將遞歸次數計數器加1*/
if( xReturn != pdFAIL )
{
( pxMutex->u.uxRecursiveCallCount)++;
}
}
return xReturn;
}
#endif /* configUSE_RECURSIVE_MUTEXES */
這個函數是帶條件編譯的,只有將宏configUSE_RECURSIVE_MUTEXES定義為1才會編譯這個函數。
程序邏輯比較簡單,如果是第一次獲取這個遞歸互斥量,直接使用出隊函數,成功后將遞歸次數計數器加1;
如果是第二次或者更多次獲取這個遞歸互斥量,則只需要將遞歸次數計數器加1即可。
嵌入式編程專輯 Linux 學習專輯 C/C++編程專輯 Qt進階學習專輯
關注我的微信公眾號,回復“加群”按規則加入技術交流群。
點擊“閱讀原文”查看更多分享。