@(嵌入式)
FreeRtos
簡(jiǎn)述
FreeRTOS 信號(hào)量和互斥鎖是基于隊(duì)列實(shí)現(xiàn)的, 隊(duì)列介紹見(jiàn) << FreeRTOS 消息隊(duì)列 >>依啰。 使用信號(hào)量需要在源文件中包含頭文件 semphr.h , 該文件定義了信號(hào)量的 API, 實(shí)際我們使用的信號(hào)量 API 都是宏定義, 宏的實(shí)際是隊(duì)列提供的函數(shù)。
FreeRTOS 信號(hào)量包括二進(jìn)制信號(hào)量褪测、計(jì)數(shù)信號(hào)量、互斥鎖和遞歸互斥鎖潦刃。 這篇文章介紹如何使用這些信號(hào)量就行任務(wù)間同步以及其實(shí)現(xiàn)侮措。
分析的源碼版本是 v9.0.0
二進(jìn)制信號(hào)量
二進(jìn)制信號(hào)量可以用于互斥和同步, 多用于同步乖杠。 可以把二進(jìn)制信號(hào)量看成一個(gè)深度為1的隊(duì)列(實(shí)際FreeRTOS也是這么實(shí)現(xiàn)的)分扎, 調(diào)用信號(hào)量獲取函數(shù), 設(shè)置阻塞時(shí)間滑黔, 在該時(shí)間內(nèi)沒(méi)有收到信號(hào)笆包, 任務(wù)會(huì)被掛起, 當(dāng)收到信號(hào)或者超時(shí)略荡, 任務(wù)恢復(fù)庵佣,函數(shù)返回。
多個(gè)任務(wù)同時(shí)阻塞在一個(gè)信號(hào)量汛兜, 當(dāng)信號(hào)量有效時(shí)巴粪, 最高優(yōu)先級(jí)的任務(wù)最先解除阻塞。
二進(jìn)制信號(hào)量使用
舉個(gè)使用場(chǎng)景粥谬, 一個(gè)任務(wù)讀取一個(gè)外設(shè)肛根,一直等待外設(shè)可讀占用CPU效率低, 可以調(diào)用信號(hào)量獲取函數(shù)阻塞等待漏策, 當(dāng)外設(shè)可讀派哲,在其中斷函數(shù)中發(fā)送信號(hào)量,喚醒任務(wù)執(zhí)行讀取操作掺喻。
(中斷中必須使用帶有 FromISR
結(jié)尾的 API)
// 信號(hào)量句柄
SemaphoreHandle_t xSemaphore;
void vATask( void * pvParameters )
{
// 創(chuàng)建二進(jìn)制信號(hào)量
xSemaphore = xSemaphoreCreateBinary();
if( xSemaphore == NULL )
{
//heap 空間不夠 芭届,創(chuàng)建失敗
}
else
{
// 信號(hào)量獲取 設(shè)置阻塞時(shí)間 10 ticks
if( xSemaphoreTake( xSemaphore, ( TickType_t ) 10 ) == pdTRUE )
{
// 獲取到信號(hào)量 储矩!
//...
// 如果任務(wù)中發(fā)送信號(hào)量
//xSemaphoreGive( xSemaphore );
}
else
{
// 等待 10 tick 無(wú)法獲取信號(hào)量
// 超時(shí)返回
}
}
}
// 中斷
void vTimerISR( void * pvParameters )
{
static signed BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
// 發(fā)送信號(hào)量
// 傳遞參數(shù)判斷是否有高優(yōu)先級(jí)任務(wù)就緒
xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );
// 判斷是否需要觸發(fā)任務(wù)切換
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
如果把信號(hào)量作為互斥鎖使用, 則任務(wù)在獲取信號(hào)后褂乍,處理完畢需要返回持隧。
FreeRTOS 在 8.02 版本提供了一種更加輕量級(jí)的任務(wù)同步, 任務(wù)通知逃片, 由于該方式是集合在任務(wù)控制塊的屡拨,所以不需要額外的內(nèi)存消耗,推薦使用褥实。
二進(jìn)制信號(hào)量實(shí)現(xiàn)
以下看看 FreeRTOS 如何基于隊(duì)列實(shí)現(xiàn)信號(hào)量的呀狼。
創(chuàng)建信號(hào)量
在信號(hào)量定義頭文件可以找到該宏的定義, 可以發(fā)現(xiàn)性锭, 創(chuàng)建一個(gè)信號(hào)量赠潦,實(shí)際上是創(chuàng)建了一個(gè)隊(duì)列, 隊(duì)列深度設(shè)置為1個(gè)草冈, 同時(shí), semSEMAPHORE_QUEUE_ITEM_LENGTH
這個(gè)宏的值為0瓮增, 即 item size 為0怎棱, 表示信號(hào)量這個(gè)隊(duì)列沒(méi)有隊(duì)列項(xiàng)存儲(chǔ)空間, 因?yàn)閷?duì)于信號(hào)量绷跑,沒(méi)有這個(gè)需要拳恋,
對(duì)于二進(jìn)制信號(hào)量, 創(chuàng)建后默認(rèn)初始化其 uxMessageWaiting
為0砸捏, 當(dāng)有信號(hào)發(fā)出時(shí)谬运, 該值變?yōu)?(最大也只能為1),此時(shí)信號(hào)量有效垦藏, 如果有任務(wù)獲取消費(fèi)了信號(hào)量梆暖,該變量再次變?yōu)?, 信號(hào)量無(wú)效掂骏, 有任務(wù)在次調(diào)用獲取信號(hào)量轰驳,可能阻塞等待或者返回信號(hào)量空。
#define xSemaphoreCreateBinary() \
xQueueGenericCreate( ( UBaseType_t ) 1, \
semSEMAPHORE_QUEUE_ITEM_LENGTH, \
queueQUEUE_TYPE_BINARY_SEMAPHORE )
獲取信號(hào)量
任務(wù)調(diào)用接口獲取信號(hào)量弟灼, 可以通過(guò)如下宏實(shí)現(xiàn) :
#define xSemaphoreTake( xSemaphore, xBlockTime ) \
xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), \
NULL,( xBlockTime ), pdFALSE )
這個(gè)函數(shù)是供任務(wù)調(diào)用的级解, 可以看到該函數(shù)實(shí)際上調(diào)用的是隊(duì)列的接收函數(shù), 由于沒(méi)有數(shù)據(jù)可讀田绑, 傳遞的指針為 NULL勤哗。
函數(shù)調(diào)用設(shè)置阻塞時(shí)間,如果調(diào)用函數(shù)時(shí)信號(hào)量無(wú)效掩驱, 則會(huì)阻塞任務(wù)等待芒划。
如果是在中斷中冬竟, 則必須調(diào)用如下宏
#define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken )\
xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), \
NULL, ( pxHigherPriorityTaskWoken ) )
函數(shù) xSemaphoreTakeFromISR
是供中斷調(diào)用的,做了中斷優(yōu)先級(jí)處理腊状,并且不會(huì)阻塞诱咏。
釋放信號(hào)量
釋放信號(hào)量的地方可能是中斷,或者是任務(wù)中缴挖, 對(duì)應(yīng)調(diào)用不同接口袋狞。
中斷中釋放
如果在中斷中調(diào)用發(fā)送信號(hào)量, 需要調(diào)用的 API 是 xSemaphoreGiveFromISR
映屋, 查看該宏定義如下 :
#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) \
xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ),\
( pxHigherPriorityTaskWoken ) )
該宏實(shí)際調(diào)用的函數(shù) xQueueGiveFromISR
定義于 queue.c
中苟鸯,
在<< FreeRTOS 消息隊(duì)列 >> 介紹過(guò), 隊(duì)列在中斷中調(diào)用的發(fā)送函數(shù)卻是 xQueueGenericSendFromISR
棚点。
對(duì)比了一下兩個(gè)函數(shù)的差別早处, 發(fā)現(xiàn) xQueueGiveFromISR
相比隊(duì)列默認(rèn)用的, 差別是沒(méi)有調(diào)用了內(nèi)存拷貝的函數(shù)瘫析,因?yàn)閷?duì)于信號(hào)量而言砌梆, 發(fā)送的消息隊(duì)列不關(guān)心其內(nèi)容,在前面在創(chuàng)建信號(hào)量也提過(guò)贬循, 對(duì)應(yīng)創(chuàng)建的隊(duì)列是沒(méi)有隊(duì)列項(xiàng)存儲(chǔ)空間的咸包, 其 item size 是0。 而主要操作的是變量 uxMessagesWaiting
的值杖虾。
簡(jiǎn)化以下該函數(shù) 烂瘫,如下
BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue,
BaseType_t * const pxHigherPriorityTaskWoken )
{
BaseType_t xReturn;
UBaseType_t uxSavedInterruptStatus;
Queue_t * const pxQueue = ( Queue_t * ) xQueue;
// 對(duì)應(yīng) item size == 0 的隊(duì)列
configASSERT( pxQueue->uxItemSize == 0 );
// 互斥鎖不能在中斷中使用
configASSERT( !( ( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) && ( pxQueue->pxMutexHolder != NULL ) ) );
// 設(shè)置中斷優(yōu)先級(jí)
uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR();
{
// 獲取當(dāng)前隊(duì)列可讀消息數(shù)
const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;
// 如果隊(duì)列仍有空間
if( uxMessagesWaiting < pxQueue->uxLength )
{
const int8_t cTxLock = pxQueue->cTxLock;
// 互斥鎖不能在中斷中使用
// 不需要考慮互斥鎖類(lèi)型信號(hào)量
// 增加未讀消息數(shù)量
pxQueue->uxMessagesWaiting = uxMessagesWaiting + 1;
// 隊(duì)列沒(méi)有被鎖定 喚醒阻塞等待的最高優(yōu)先級(jí)任務(wù)
if( cTxLock == queueUNLOCKED )
{
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE )
{
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{
if( pxHigherPriorityTaskWoken != NULL )
{
// 中斷中調(diào)用的這個(gè)函數(shù)名,高優(yōu)先級(jí)任務(wù)就緒
// 設(shè)置參數(shù)奇适,表示需要切換任務(wù)
*pxHigherPriorityTaskWoken = pdTRUE;
}
}
}
}
else
{
// 如果隊(duì)列被鎖定坟比, 不能在此修改事件鏈表
// 增加計(jì)數(shù), 解鎖的時(shí)候處理
pxQueue->cTxLock = ( int8_t ) ( cTxLock + 1 );
}
xReturn = pdPASS;
}
else
{
// 隊(duì)列滿 直接返回
xReturn = errQUEUE_FULL;
}
}
portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus );
return xReturn;
}
任務(wù)中釋放
在任務(wù)中釋放信號(hào)量調(diào)用的 API 是
#define xSemaphoreGive( xSemaphore ) \
xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ),\
NULL, \
semGIVE_BLOCK_TIME, \
queueSEND_TO_BACK )
可以看到實(shí)際調(diào)用的函數(shù)是 xQueueGenericSend
嚷往, 這個(gè)函數(shù)在隊(duì)列一章做了比較詳細(xì)地介紹過(guò)葛账,此處不貼源碼。
對(duì)于信號(hào)量间影, 由于沒(méi)有消息內(nèi)容注竿, 所以傳遞的指針設(shè)置為 NULL, 不會(huì)執(zhí)行任務(wù)拷貝函數(shù)魂贬,在函數(shù)中判斷隊(duì)列是否滿巩割, 如果沒(méi)有滿, 就增加未讀消息數(shù)的變量付燥, 查看是否有任務(wù)等待信號(hào)而被阻塞宣谈,恢復(fù)最高優(yōu)先級(jí)的任務(wù)。 如果任務(wù)滿键科, 按照設(shè)定的阻塞時(shí)間阻塞掛起任務(wù)等待闻丑。
計(jì)數(shù)信號(hào)量
二進(jìn)制信號(hào)量是長(zhǎng)度為1的隊(duì)列漩怎, 計(jì)數(shù)信號(hào)量則是長(zhǎng)度可以大于1的信號(hào)量, 當(dāng)設(shè)置長(zhǎng)度為1嗦嗡, 其行為和二進(jìn)制型號(hào)量一樣勋锤。
當(dāng)任務(wù)調(diào)用 API 釋放信號(hào)量, 信號(hào)量未讀計(jì)數(shù)加1侥祭, 任務(wù)調(diào)用接收函數(shù)處理信號(hào)量叁执, 則對(duì)應(yīng)減1,初始化信號(hào)量計(jì)數(shù)為0矮冬。
所以谈宛, 使用上, 計(jì)數(shù)信號(hào)量和二進(jìn)制信號(hào)量是差不多胎署。
查看計(jì)數(shù)信號(hào)量創(chuàng)建宏 :
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) \
xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
相比二進(jìn)制信號(hào)量吆录, 計(jì)數(shù)信號(hào)量創(chuàng)建時(shí)需要設(shè)置兩個(gè)參數(shù),一個(gè)是最大的計(jì)數(shù)值琼牧, 另一個(gè)初始化計(jì)數(shù)值恢筝。
實(shí)際函數(shù)是在隊(duì)列中實(shí)現(xiàn), 對(duì)應(yīng)查看隊(duì)列中該函數(shù)是如何實(shí)現(xiàn)的巨坊, 看到其代碼如下 :
QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount )
{
QueueHandle_t xHandle;
configASSERT( uxMaxCount != 0 );
configASSERT( uxInitialCount <= uxMaxCount );
// 申請(qǐng)隊(duì)列滋恬, 深度由第一個(gè)參數(shù)決定
// item size 也是為 0
xHandle = xQueueGenericCreate( uxMaxCount,
queueSEMAPHORE_QUEUE_ITEM_LENGTH,
queueQUEUE_TYPE_COUNTING_SEMAPHORE );
if( xHandle != NULL )
{
// 初始化信號(hào)量計(jì)數(shù)值
( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount;
}
return xHandle;
}
計(jì)數(shù)型號(hào)量其他地方使用同二進(jìn)制一樣,不繼續(xù)討論抱究。
互斥鎖
當(dāng)一個(gè)任務(wù)訪問(wèn)一個(gè)資源時(shí), 需要獲取令牌带斑, 在其使用期間鼓寺,其他任務(wù)不能使用該資源, 使用完后勋磕, 釋放令牌妈候, 其他任務(wù)可以訪問(wèn), 保證資源在一段時(shí)間只能由一個(gè)任務(wù)讀取修改挂滓。
與二進(jìn)制信號(hào)量最大的不同在于苦银, 互斥信號(hào)量帶有優(yōu)先級(jí)繼承的機(jī)制,這個(gè)機(jī)制用于減低優(yōu)先級(jí)反轉(zhuǎn)的影響赶站。
舉個(gè)例子幔虏, 三個(gè)任務(wù)優(yōu)先級(jí)從大到小依次 A > B > C, 某種情況下, 任務(wù)C 獲取了互斥鎖贝椿, 之后任務(wù)A 請(qǐng)求拿鎖被掛起想括, 任務(wù)C 繼續(xù)運(yùn)行, 如果沒(méi)有優(yōu)先級(jí)繼承烙博, 任務(wù)B 就緒瑟蜈,由于優(yōu)先級(jí)高于當(dāng)前的任務(wù)C烟逊, 所以開(kāi)始運(yùn)行, 這樣導(dǎo)致任務(wù)C 無(wú)法及時(shí)放鎖铺根,進(jìn)而導(dǎo)致任務(wù)A 無(wú)法運(yùn)行宪躯, 但是任務(wù)A 的優(yōu)先級(jí)比B 高, 這就是優(yōu)先級(jí)反轉(zhuǎn)位迂。
如果加入優(yōu)先級(jí)繼承访雪, 任務(wù)C 拿鎖, 任務(wù)A 請(qǐng)求拿鎖被掛起時(shí)囤官, 由于C < A, 通過(guò)繼承機(jī)制冬阳, 提高C 的優(yōu)先級(jí),使其等于 A党饮, 這樣肝陪, 以上任務(wù)B 就無(wú)法搶占C, 任務(wù)C 結(jié)束釋放鎖讓后恢復(fù)其本來(lái)優(yōu)先級(jí)刑顺, 任務(wù)A 開(kāi)始運(yùn)行氯窍。
創(chuàng)建互斥信號(hào)量
使用互測(cè)鎖前需要?jiǎng)?chuàng)建互斥鎖, 需要調(diào)用 API 的定義 :
#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
查找對(duì)應(yīng)的實(shí)現(xiàn)函數(shù) xQueueCreateMutex
源碼 蹲堂, 函數(shù)調(diào)用了隊(duì)列創(chuàng)建函數(shù)創(chuàng)建了一個(gè)深度為1狼讨, item size 為 0 的隊(duì)列, 到這里看起來(lái)和二進(jìn)制信號(hào)量一樣柒竞。
隊(duì)列創(chuàng)建后政供,調(diào)用了專(zhuān)門(mén)初始化互斥信號(hào)量的函數(shù)初始化。
QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType )
{
Queue_t *pxNewQueue;
const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0;
pxNewQueue = ( Queue_t * ) xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType );
prvInitialiseMutex( pxNewQueue );
return pxNewQueue;
}
初始化互斥信號(hào)量朽基,
static void prvInitialiseMutex( Queue_t *pxNewQueue )
{
if( pxNewQueue != NULL )
{
// 初始化互斥信號(hào)量的參數(shù)
// 互斥信號(hào)量當(dāng)前有效
pxNewQueue->pxMutexHolder = NULL;
// 標(biāo)記類(lèi)型
pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;
// 遞歸信號(hào)量用
pxNewQueue->u.uxRecursiveCallCount = 0;
// 發(fā)送一個(gè)消息到隊(duì)列
// 這樣布隔,第一個(gè)拿信號(hào)量的任務(wù)不會(huì)被掛起
( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );
}
}
拿鎖
任務(wù)拿鎖可以通過(guò)調(diào)用 API 定義如下
#define xSemaphoreTake( xSemaphore, xBlockTime ) \
xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), NULL, ( xBlockTime ), pdFALSE )
如果任務(wù)調(diào)用此函數(shù)時(shí)互斥鎖有效,則拿到鎖后返回稼虎。
這個(gè)函數(shù)在隊(duì)列文章中介紹過(guò)衅檀,該函數(shù)中, 對(duì)于互斥鎖有一些特殊處理霎俩,主要是實(shí)現(xiàn)了優(yōu)先級(jí)繼承機(jī)制哀军。
在一個(gè)任務(wù)拿鎖后, 其他任務(wù)嘗試拿鎖失敗打却,如果設(shè)置了阻塞時(shí)間杉适,則該任務(wù)會(huì)被阻塞,在進(jìn)入阻塞前学密, 函數(shù)會(huì)判斷當(dāng)前任務(wù)的優(yōu)先級(jí)是否高于擁有鎖任務(wù)的優(yōu)先級(jí)淘衙,如果高于, 則會(huì)先提高擁有鎖任務(wù)的優(yōu)先級(jí)腻暮。
實(shí)現(xiàn)的代碼如下
點(diǎn)擊源碼
#if ( configUSE_MUTEXES == 1 )
{
if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )
{
taskENTER_CRITICAL();
{
// 判斷是否需要修改拿鎖任務(wù)優(yōu)先級(jí)
vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );
}
taskEXIT_CRITICAL();
}
}
#endif
查看 vTaskPriorityInherit
函數(shù)的實(shí)現(xiàn)彤守, 該函數(shù)比較當(dāng)前任務(wù)(拿鎖失敗毯侦,阻塞等待)和拿鎖任務(wù)優(yōu)先級(jí), 進(jìn)行優(yōu)先級(jí)繼承處理具垫。
void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder )
{
TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;
// 判斷是否有任務(wù)拿著鎖
if( pxMutexHolder != NULL )
{
// 判斷當(dāng)前任務(wù)的優(yōu)先級(jí)是否比拿鎖任務(wù)高
if( pxTCB->uxPriority < pxCurrentTCB->uxPriority )
{
// 修改拿鎖任務(wù)在事件鏈表項(xiàng)的優(yōu)先級(jí)值
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 ) pxCurrentTCB->uxPriority );
}
//如果拿鎖任務(wù)在就緒鏈表侈离, 移動(dòng)拿鎖任務(wù)到新優(yōu)先級(jí)任務(wù)鏈表
if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxTCB->uxPriority ] ),
&( pxTCB->xStateListItem ) ) != pdFALSE )
{
// 先從舊就緒鏈表移除
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
}
// 修改優(yōu)先級(jí), 優(yōu)先級(jí)繼承筝蚕, 并加入到新的優(yōu)先級(jí)就緒鏈表
pxTCB->uxPriority = pxCurrentTCB->uxPriority;
prvAddTaskToReadyList( pxTCB );
}
else
{
// 如果拿鎖任務(wù)沒(méi)有在就緒鏈表卦碾, 直接修改優(yōu)先級(jí)值即可
pxTCB->uxPriority = pxCurrentTCB->uxPriority;
}
}
}
}
互斥鎖不能在中斷使用, 因?yàn)橹袛嗪瘮?shù)沒(méi)有優(yōu)先級(jí)繼承起宽,同時(shí)洲胖, 中斷函數(shù)不能阻塞。
放鎖
任務(wù)使用資源后坯沪, 需要釋放互斥鎖绿映,這樣其他任務(wù)才能正常拿鎖使用資源。
釋放鎖的接口定義如下 :
#define xSemaphoreGive( xSemaphore ) \
xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )
宏實(shí)際調(diào)用的函數(shù)實(shí)際也解析過(guò)了腐晾, 上面提到拿鎖的時(shí)候叉弦, 任務(wù)堵塞會(huì)進(jìn)行優(yōu)先級(jí)繼承處理, 因此藻糖,當(dāng)一個(gè)任務(wù)獲取了互斥鎖淹冰, 在其使用期間, 其本身優(yōu)先級(jí)可能已經(jīng)被提高過(guò)了巨柒, 當(dāng)其釋放鎖的時(shí)候樱拴, 需要恢復(fù)到原來(lái)的優(yōu)先級(jí)。還鎖的操作是向隊(duì)列發(fā)送返回一個(gè)消息洋满,在拷貝消息內(nèi)容的函數(shù)疹鳄,判斷隊(duì)列是互斥鎖時(shí), 會(huì)調(diào)用優(yōu)先級(jí)繼承解除函數(shù)芦岂, 恢復(fù)任務(wù)的優(yōu)先級(jí)。
優(yōu)先級(jí)恢復(fù)函數(shù)如下垫蛆,
BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder )
{
TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;
BaseType_t xReturn = pdFALSE;
if( pxMutexHolder != NULL )
{
// 當(dāng)前任務(wù)是拿鎖任務(wù)
// 這個(gè)隊(duì)列是互斥鎖類(lèi)型
configASSERT( pxTCB == pxCurrentTCB );
configASSERT( pxTCB->uxMutexesHeld );
( pxTCB->uxMutexesHeld )--;
// 拿鎖任務(wù)的優(yōu)先級(jí)被修改了
if( pxTCB->uxPriority != pxTCB->uxBasePriority )
{
// 鎖徹底釋放
if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 )
{
// 從鏈表移除任務(wù)
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
taskRESET_READY_PRIORITY( pxTCB->uxPriority );
}
// 恢復(fù)優(yōu)先級(jí)
pxTCB->uxPriority = pxTCB->uxBasePriority;
// 恢復(fù)任務(wù)TCB 其他和優(yōu)先級(jí)相關(guān)的參數(shù)
listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority );
// 重新插入到就緒鏈表
prvAddTaskToReadyList( pxTCB );
// 發(fā)生優(yōu)先級(jí)繼承
// 說(shuō)明有高優(yōu)先級(jí)等鎖禽最, 所以提示需要任務(wù)切換
xReturn = pdTRUE;
}
}
}
return xReturn;
}
任務(wù)發(fā)生優(yōu)先級(jí)繼承, 本身優(yōu)先級(jí)被修改提高袱饭,任務(wù)原始優(yōu)先級(jí)會(huì)保存在 pxTCB->uxBasePriority
中川无, 恢復(fù)的時(shí)候使用。
優(yōu)先級(jí)繼承恢復(fù)后虑乖, 可知有更高優(yōu)先級(jí)任務(wù)阻塞等待鎖懦趋,所以需要返回提示任務(wù)切換, 對(duì)應(yīng)隊(duì)列發(fā)送函數(shù)中的特殊處理一段 , 根據(jù)內(nèi)存拷貝函數(shù) prvCopyDataToQueue
返回值判斷是否需要觸發(fā)任務(wù)切換疹味, 因?yàn)槿蝿?wù)拷貝函數(shù)調(diào)用了上面這個(gè)函數(shù)恢復(fù)優(yōu)先級(jí)仅叫,需不需要觸發(fā)任務(wù)切換的返回值就是由這個(gè)函數(shù)提供的帜篇。
遞歸互斥鎖
獲取遞歸互斥量的任務(wù)可以重復(fù)獲取該遞歸互斥量。使用xSemaphoreTakeRecursive() 函數(shù)成功獲取幾次遞歸互斥量诫咱,對(duì)應(yīng)的就要使用xSemaphoreGiveRecursive()函數(shù)返還幾次笙隙,在此之前遞歸互斥量都處于無(wú)效狀態(tài), 其他任務(wù)無(wú)法獲取坎缭, 必須等待獲取的任務(wù)釋放完畢竟痰。
遞歸互斥鎖創(chuàng)建調(diào)用接口 :
#define xSemaphoreCreateRecursiveMutex() \
xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )
實(shí)際調(diào)用函數(shù)同普通互斥鎖一樣。
獲取遞歸信號(hào)量
遞歸信號(hào)量在同一個(gè)任務(wù)可以多次拿取掏呼, 其調(diào)用的接口不同其他信號(hào)量的 xSemaphoreTake
坏快, 而是如下宏 :
#define xSemaphoreTakeRecursive( xMutex, xBlockTime ) \
xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )
查看具體實(shí)現(xiàn)函數(shù) xQueueTakeMutexRecursive
, 看看有什么不同憎夷。
BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait )
{
BaseType_t xReturn;
Queue_t * const pxMutex = ( Queue_t * ) xMutex;
configASSERT( pxMutex );
if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() )
{
// 如果嘗試拿鎖的任務(wù)是當(dāng)前拿了鎖的任務(wù)莽鸿, 則遞增拿鎖次鎖
( pxMutex->u.uxRecursiveCallCount )++;
xReturn = pdPASS;
}
else
{
// 其他任務(wù)嘗試拿鎖, 同使用普通互斥鎖一樣
xReturn = xQueueGenericReceive( pxMutex, NULL, xTicksToWait, pdFALSE );
// 阻塞等待
// 如果恢復(fù)后岭接,在超時(shí)時(shí)間內(nèi)拿到鎖
// 遞增計(jì)數(shù)富拗, 第一次拿鎖!鸣戴!
if( xReturn != pdFAIL )
{
( pxMutex->u.uxRecursiveCallCount )++;
}
}
return xReturn;
}
相比普通互斥鎖啃沪,遞歸互斥鎖允許同一個(gè)任務(wù)多次拿鎖, 所以其拿鎖接口會(huì)判斷拿鎖任務(wù)是否是擁有鎖的任務(wù)窄锅,如果是创千, 則遞增拿鎖次數(shù), 其他任務(wù)處理則和普通互斥鎖一樣入偷,阻塞等待追驴。
釋放遞歸信號(hào)量
響應(yīng)的, 遞歸互斥鎖釋放調(diào)用的接口定義 :
#define xSemaphoreGiveRecursive( xMutex ) \
xQueueGiveMutexRecursive( ( xMutex ) )
#endif
查看實(shí)際實(shí)現(xiàn)的函數(shù) :
BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex )
{
BaseType_t xReturn;
Queue_t * const pxMutex = ( Queue_t * ) xMutex;
configASSERT( pxMutex );
// 判斷嘗試放鎖的任務(wù)是否拿著鎖
if( pxMutex->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() )
{
// 遞歸互斥鎖疏之, 拿幾個(gè)需要對(duì)應(yīng)放幾個(gè)
( pxMutex->u.uxRecursiveCallCount )--;
// 計(jì)數(shù)清零殿雪,說(shuō)明可以真正放鎖
if( pxMutex->u.uxRecursiveCallCount == ( UBaseType_t ) 0 )
{
// 返回鎖
( void ) xQueueGenericSend( pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK );
}
xReturn = pdPASS;
}
else
{
// 當(dāng)前任務(wù)不是持有鎖的任務(wù), 無(wú)法釋放
xReturn = pdFAIL;
}
// 還不能真正放鎖
return xReturn;
}
對(duì)應(yīng)拿鎖的處理锋爪, 以上的函數(shù)也就很好理解了丙曙。
FreeRTOS 信號(hào)量記錄到此結(jié)束。