19.5 任务控制块
同样都是使用队列的结构体,只不过表示的意义不同。
typedef struct QueueDefinition{int8_t *pcHead; // pcHead 指向队列消息存储区起始位置,即第一个消息空间。int8_t *pcTail; // pcTail 指向队列消息存储区结束位置地址。int8_t *pcWriteTo; // pcWriteTo 指向队列消息存储区下一个可用消息空间。union{int8_t *pcReadFrom;UBaseType_t uxRecursiveCallCount; // 1} u;List_t xTasksWaitingToSend;List_t xTasksWaitingToReceive;volatile UBaseType_t uxMessagesWaiting; // 2 1 存在的消息个数UBaseType_t uxLength; // 3 1UBaseType_t uxItemSize; // 4 0volatile int8_t cRxLock;volatile int8_t cTxLock;#if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )uint8_t ucStaticallyAllocated;#endif#if ( configUSE_QUEUE_SETS == 1 )struct QueueDefinition *pxQueueSetContainer;#endif#if ( configUSE_TRACE_FACILITY == 1 )UBaseType_t uxQueueNumber;uint8_t ucQueueType;#endif} xQUEUE;typedef xQUEUE Queue_t;
- pcReadFrom 与 uxRecursiveCallCount 是一对互斥变量, 使用联合体用来确保两个互斥的结构体成员不会同时出现。 当结构体用于队列时, pcReadFrom 指向出队消息空间的最后一个,见文知义,就是读取消息时候是从 pcReadFrom 指向的空间读取消息内容。 当结构体用于互斥量时, uxRecursiveCallCount 用于计数,记录递归互斥量被“调用” 的次数。
- 如果控制块结构体是用于消息队列: uxMessagesWaiting 用来记录当前消息队列的消息个数; 如果控制块结构体被用于互斥量的时候, 这个值就表示有效互斥量个数,这个值是 1 则表示互斥量有效,如果是 0 则表示互斥量无效.
- 如果控制块结构体是用于消息队列: uxLength 表示队列的长度,也就是能存放多少消息; 如果控制块结构体被用于互斥量的时候, uxLength 表示最大的信号量可用个数, uxLength 最大为 1
- 如果控制块结构体是用于消息队列: uxItemSize 表示单个消息的大小; 如果控制块结构体被用于互斥量的时候, 则无需存储空间,为 0 即可。
19.6 互斥量函数接口详解
19.6.1 互斥量创建函数xSemaphoreCreateMutex()
xSemaphoreCreateMutex()用于创建一个互斥量, 并返回一个互斥量句柄。 该句柄的原型是一个 void 型的指针,在使用之前必须先由用户定义一个互斥量句柄。要想使用该函数必须在 FreeRTOSConfig.h 中把宏 configSUPPORT_DYNAMIC_ALLOCATION 定义为 1,即开启动态内存分配, 其实该宏在 FreeRTOS.h 中默认定义为 1, 即所有 FreeRTOS 的对象在创建的时候都默认使用动态内存分配方案, 同时还需在 FreeRTOSConfig.h 中把configUSE_MUTEXES 宏定义打开, 表示使用互斥量。
函数原型:xSemaphoreCreateMutex()
#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 )#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )#endif
创建互斥量其实是调用xQueueCreateMutex函数:
#if( ( configUSE_MUTEXES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )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 ); // 1prvInitialiseMutex( pxNewQueue ); // 2return pxNewQueue;}#endif /* configUSE_MUTEXES */
只有将宏 configUSE_MUTEXES 定义为 1 才会编译这个函数。
- 其实互斥量的创建也是调用 xQueueGenericCreate()函数进行创建。
uxQueueLength 为 1 表示创建的队列长度为 1。
uxMutexSize 的值为 0,表示创建的消息空间(队列项)大小为 0,因为这个所谓的“消息队列”其实并不是用于存储消息的,而是被用作互斥量
ucQueueType 表示的是创建队列的类型
#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U )#define queueQUEUE_TYPE_SET ( ( uint8_t ) 0U )#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U )#define queueQUEUE_TYPE_COUNTING_SEMAPHORE ( ( uint8_t ) 2U )#define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U )#define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U )
调用函数prvInitialiseMutex() 初始化互斥信号量:
#define pxMutexHolder pcTail#define uxQueueType pcHead#define queueQUEUE_IS_MUTEX NULLstatic void prvInitialiseMutex( Queue_t *pxNewQueue ){if( pxNewQueue != NULL ){pxNewQueue->pxMutexHolder = NULL;pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX;/* In case this is a recursive mutex. */pxNewQueue->u.uxRecursiveCallCount = 0; // 2traceCREATE_MUTEX( pxNewQueue );/* Start with the semaphore in the expected state. */( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK );}else{traceCREATE_MUTEX_FAILED();}}
- FreeRTOS 用宏定义的方式来重新定义了结构体中的 pcTail 与 pcHead 成员变量,更方便阅读。为什么要这样子呢?我们知道, pcTail 与 pcHead 用于指向消息存储区域的, 但是如果队列用作互斥量, 那么我们就无需理会消息存储区域了, 因为都没有消息存储区域,但是互斥量有个很重要的特性,那就是优先级继承机制,所以,我们要知道持有互斥量的任务是哪一个,因为只有持有互斥量的任务才能得到互斥量的所有权,所以, pxMutexHolder就被用于指向持有互斥量的任务控制块, 现在初始化的时候, 就初始化为 NULL,表示没有任务持有互斥量。 uxQueueType 表示队列的类型, 设置为 queueQUEUE_IS_MUTEX(NULL),表示的是用作互斥量。
- 如果是递归互斥量 的 话 , 还需要联合体成员 量u.uxRecursiveCallCount 初始化一下
- 调用 xQueueGenericSend()函数释放互斥量,在创建成功的时候互斥量默认是有效的。说明现在又1个互斥量是可用的。

使用实例:
SemaphoreHandle_t MuxSem_Handle;void vATask( void * pvParameters ){/* 创建一个互斥量 */MuxSem_Handle= xSemaphoreCreateMutex();if (MuxSem_Handle!= NULL ) {/* 互斥量创建成功 */}}
19.6.2 递归互斥量创建函数xSemaphoreCreateRecursiveMutex()
xSemaphoreCreateRecursiveMutex()用于创建一个递归互斥量, 不是递归的互斥量由函数 xSemaphoreCreateMutex() 或 xSemaphoreCreateMutexStatic()创建。
递归互斥量,它可以被同一个任务获取很多次, 获取多少次就需要释放多少次。递归信号量与互斥量一样,都实现了优先级继承机制,可以降低优先级反转的危害。
configUSE_RECURSIVE_MUTEXES
实际调用函数xQueueCreateMutex() 。
其实 xSemaphoreCreateRecursiveMutex()实际调用的函数就是 xQueueCreateMutex()函数。
使用实例:
SemaphoreHandle_t xMutex;void vATask( void * pvParameters ){/* 创建一个递归互斥量 */xMutex = xSemaphoreCreateRecursiveMutex();if ( xMutex != NULL ) {/* 递归互斥量创建成功 */}}
19.6.3 互斥量删除函数vSemaphoreDelete()
直接调用 vSemaphoreDelete()函数进行删除即可
19.6.4 互斥信号量获取函数xSemaphoreTake()
Receive
这个函数就有互斥量优先级继承相关的实现。
当互斥量处于开锁的状态, 任务才能获取互斥量成功,当任务持有了某个互斥量的时候,其它任务就无法获取这个互斥量,需要等到持有互斥量的任务进行释放后,其他任务才能获取成功, 任务通过互斥量获取函数来获取互斥量的所有权。
任务对互斥量的所有权是独占的,任意时刻互斥量只能被一个任务持有,如果互斥量处于开锁状态,那么获取该互斥量的任务将成功获得该互斥量,并拥有互斥量的使用权。
如果互斥量处于闭锁状态,获取该互斥量的任务将无法获得互斥量, 任务将被挂起,在任务被挂起之前,会进行优先级继承,如果当前任务优先级比持有互斥量的任务优先级高,那么将会临时提升持有互斥量任务的优先级。
#define xSemaphoreTake( xSemaphore, xBlockTime ) \xQueueGenericReceive( ( QueueHandle_t ) ( xSemaphore ), \NULL, \(xBlockTime ), \pdFALSE )
其实就是消息队列获取函数,只不过如果是使用了互斥量的时候,这个函数会稍微有点不一样,因为互斥量本身的优先级继承机制,所以,在这个函数里面会使用宏定义进行编译,如果获取的对象是互斥量,那么这个函数就拥有优先级继承算法,如果获取对象不是互斥量,就没有优先级继承机制。
BaseType_t xQueueGenericReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait, const BaseType_t xJustPeeking ){BaseType_t xEntryTimeSet = pdFALSE;TimeOut_t xTimeOut;int8_t *pcOriginalReadPosition;Queue_t * const pxQueue = ( Queue_t * ) xQueue;for( ;; ){taskENTER_CRITICAL();{const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting;/* 看看队列中有没有消息 */if( uxMessagesWaiting > ( UBaseType_t ) 0 ){/*防止仅仅是读取消息,而不进行消息出队操作*/pcOriginalReadPosition = pxQueue->u.pcReadFrom;/* 拷贝消息到用户指定存放区域 pvBuffer */prvCopyDataFromQueue( pxQueue, pvBuffer );if( xJustPeeking == pdFALSE ){traceQUEUE_RECEIVE( pxQueue );/* 获取了消息,当前消息队列的消息个数需要减一 */pxQueue->uxMessagesWaiting = uxMessagesWaiting - 1;/* 如果系统支持使用互斥量 */#if ( configUSE_MUTEXES == 1 ){/* 如果队列类型是互斥量 */if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ){/* 获取当前任务控制块 */pxQueue->pxMutexHolder = ( int8_t * ) 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();}}else /* 这个是不释放消息 */{traceQUEUE_PEEK( pxQueue );pxQueue->u.pcReadFrom = pcOriginalReadPosition;if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ){if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ){/* The task waiting has a higher priority than this task. */queueYIELD_IF_USING_PREEMPTION();}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}taskEXIT_CRITICAL();return pdPASS;}else /* 消息队列中没有消息可读 */{if( xTicksToWait == ( TickType_t ) 0 ){/* The queue was empty and no block time is specified (orthe block time has expired) so leave now. */taskEXIT_CRITICAL();traceQUEUE_RECEIVE_FAILED( pxQueue );return errQUEUE_EMPTY;}else if( xEntryTimeSet == pdFALSE ){/* 初始化阻塞超时结构体变量,初始化进入阻塞的时间 xTickCount 和溢出次数 xNumOfOverflows */vTaskSetTimeOutState( &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();{/* 进行优先级继承 */vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );}taskEXIT_CRITICAL();}else{mtCOVERAGE_TEST_MARKER();}}#endif/* 将当前任务添加到队列的等待接收列表中以及阻塞延时列表,阻塞时间为用户指定的超时时间 xTicksToWait */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();/* 如果队列还是空的,返回错误代码 errQUEUE_EMPTY */if( prvIsQueueEmpty( pxQueue ) != pdFALSE ){traceQUEUE_RECEIVE_FAILED( pxQueue );return errQUEUE_EMPTY;}else{mtCOVERAGE_TEST_MARKER();}}}}/*-----------------------------------------------------------*/
如果互斥量有效,调用获取互斥量函数后结构体成员变量 uxMessageWaiting 会减 1,然后将队列结构体成员指针 pxMutexHolder 指向任务控制块, 表示这个互斥量被哪个任务持有, 只有这个任务才拥有互斥量的所 有权 ,并且该任务的控制块结构体成员uxMutexesHeld 会加 1,表示任务已经获取到互斥量。
void *pvTaskIncrementMutexHeldCount( void ){/* If xSemaphoreCreateMutex() is called before any tasks have been createdthen pxCurrentTCB will be NULL. */if( pxCurrentTCB != NULL ){// + 1( pxCurrentTCB->uxMutexesHeld )++;}// 返回TCBreturn pxCurrentTCB;}
如果此时互斥量是无效状态并且用户指定的阻塞时间为 0,则直接返回错误码(errQUEUE_EMPTY)
- 而如果用户指定的阻塞超时时间不为 0,则当前任务会因为等待互斥量有效而进入阻塞状态,在将任务添加到延时列表之前,会判断当前任务和拥有互斥量的任务优先级哪个更高,如果当前任务优先级高,则拥有互斥量的任务继承当前任务优先级, 也就是我们说的优先级继承机制
vTaskPriorityInherit() 真正的继承机制
void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder ){TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;if( pxMutexHolder != NULL ){/* 判断当前任务与持有互斥量任务的优先级 *//* 如果获取锁的任务的优先级小于当前要获取所的任务的优先级 */if( pxTCB->uxPriority < pxCurrentTCB->uxPriority ){/*如果该事件列表项的值未用于其它操作 则设置优先级 */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 ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */}else{mtCOVERAGE_TEST_MARKER();}/* 如果被提升优先级的任务处于就绪列表中 */if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ){/* 先将任务从就绪列表中移除,待优先级继承完毕就重新插入就绪列表中 */if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ){taskRESET_READY_PRIORITY( pxTCB->uxPriority );}else{mtCOVERAGE_TEST_MARKER();}// 优先级继承pxTCB->uxPriority = pxCurrentTCB->uxPriority;prvAddTaskToReadyList( pxTCB );}else // 在等待列表中{/* 暂时提升持有互斥量任务的优先级,提升到与当前任务优先级一致*/pxTCB->uxPriority = pxCurrentTCB->uxPriority;}traceTASK_PRIORITY_INHERIT( pxTCB, pxCurrentTCB->uxPriority );}else{mtCOVERAGE_TEST_MARKER();}}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_MUTEXES *//*-----------------------------------------------------------*/
至此,获取互斥量的操作就完成了,如果任务获取互斥量成功,那么在使用完毕需要立即释放,否则很容易造成其他任务无法获取互斥量,因为互斥量的优先级继承机制是只能将优先级危害降低,而不能完全消除。同时还需注意的是,互斥量是不允许在中断中操作的,因为优先级继承机制在中断是无意义的。
使用实例:
static void HighPriority_Task(void* parameter){BaseType_t xReturn = pdTRUE;/* 定义一个创建信息返回值,默认为 pdTRUE */while(1) {printf("HighPriority_Task 获取信号量\n");//获取互斥量 MuxSem,没获取到则一直等待xReturn = xSemaphoreTake(MuxSem_Handle,/* 互斥量句柄 */portMAX_DELAY); /* 等待时间 */if (pdTRUE == xReturn)printf("HighPriority_Task Runing\n");LED1_TOGGLE;printf("HighPriority_Task 释放信号量!\r\n");xSemaphoreGive( MuxSem_Handle );//释放互斥量vTaskDelay(1000);}}
19.6.5 递归互斥量获取函数xSemaphoreTakeRecursive()
互斥量之前必须由xSemaphoreCreateRecursiveMutex()这个函数创建 
#if ( configUSE_RECURSIVE_MUTEXES == 1 )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->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) /*lint !e961 Cast is not redundant as TaskHandle_t is a typedef. */{/* u.uxRecursiveCallCount 自加,表示调用了多少次递归互斥量获取 */( pxMutex->u.uxRecursiveCallCount )++;xReturn = pdPASS;}else{/* 如果持有递归互斥量的任务不是当前任务,就只能等待递归互斥量被释放 */xReturn = xQueueGenericReceive( pxMutex, NULL, xTicksToWait, pdFALSE );if( xReturn != pdFAIL ){/* 获取递归互斥量成功,记录递归互斥量的获取次数 */( pxMutex->u.uxRecursiveCallCount )++;}else{traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex );}}return xReturn;}#endif /* configUSE_RECURSIVE_MUTEXES */
递归互斥量可以在一个任务中多次获取,当第一次获取递归互斥量时,队列结构体成员指针 pxMutexHolder 指向获取递归互斥量的任务控制块,当任务再次尝试获取这个递归互斥量时,如果任务就是拥有递归互斥量所有权的任务,那么只需要将记录获取递归次数的 成 员 变 量 u.uxRecursiveCallCount 加1即可,不需要再操作队列。
/* 使用互斥量 */void vAnotherTask( void * pvParameters ){/* ... 做其他的事情 */if ( xMutex != NULL ) {/* 尝试获取递归信号量。如果信号量不可用则等待 10 个 ticks */if(xSemaphoreTakeRecursive(xMutex,( TickType_t)10)==pdTRUE ) {/* 获取到递归信号量,可以访问共享资源 *//* ... 其他功能代码 *//* 重复获取递归信号量 */xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );/* 释放递归信号量,获取了多少次就要释放多少次 */xSemaphoreGiveRecursive( xMutex );xSemaphoreGiveRecursive( xMutex );xSemaphoreGiveRecursive( xMutex );/* 现在递归互斥量可以被其他任务获取 */} else {/* 没能成功获取互斥量,所以不能安全的访问共享资源 */}}}
19.6.6 互斥量释放函数xSemaphoreGive()
send
使用该函数接口时,只有已持有互斥量所有权的任务才能释放它, 当任务调用xSemaphoreGive()函数时会将互斥量变为开锁状态,等待获取该互斥量的任务将被唤醒。如果任务的优先级被互斥量的优先级翻转机制临时提升,那么当互斥量被释放后, 任务的优先级将恢复为原本设定的优先级。
#define xSemaphoreGive( xSemaphore ) \xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), \NULL, \semGIVE_BLOCK_TIME, \queueSEND_TO_BACK )
互斥量、信号量的释放就是调用 xQueueGenericSend()函数,但是互斥量的处理还是有一些不一样的地方,因为它有优先级继承机制,在释放互斥量的时候我们需要恢复任务的初始优先级,所以,下面我们来看看具体在哪恢复任务的优先级,其实就是prvCopyDataToQueue()这个函数,该函数在 xQueueGenericSend()中被调用。
#if ( configUSE_MUTEXES == 1 ){if ( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ){/* The mutex is no longer being held. */xReturn = xTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder );pxQueue->pxMutexHolder = NULL;} else {mtCOVERAGE_TEST_MARKER();}
真正恢复任务的优先级函数其实是调用 xTaskPriorityDisinherit(), 而且系统会将结构体的 pxMutexHolder 成员变量指向 NULL,表示暂时没有任务持有改互斥量, 对结构体成员 uxMessagesWaiting 加 1 操作就代表了释放互 斥量 , 表示此时互斥量是有效的, 其他任务可以来获取。
BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ){TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;BaseType_t xReturn = pdFALSE;// 只有当有任务持有互斥量的时候,才会进行释放互斥量的操作。// 而且必须是持有互斥量的任务才允许释放互斥量,其他任务都没有权利去操作被任务持有// 的互斥量if( pxMutexHolder != NULL ){configASSERT( pxTCB == pxCurrentTCB );configASSERT( pxTCB->uxMutexesHeld );// 在获取的时候加了1,现在要--( pxTCB->uxMutexesHeld )--;// 判断是否发生了优先级的翻转 也就是当前的优先级和uxBasePriority不相等if( pxTCB->uxPriority != pxTCB->uxBasePriority ){/* 如果任务没有持有其他互斥量 */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;}#endif /* configUSE_MUTEXES */
被释放前的互斥量是处于无效状态, 被释放后互斥量才变得有效,除了结构体成员变量 uxMessageWaiting 加 1 外,还要判断持有互斥量的任务是否有优先级继承,如果有的话,要将任务的优先级恢复到初始值。当然,该任务必须在没有持有其它互斥量的情况下,才能将继承的优先级恢复到原始值。然后判断是否有任务要获取互斥量并且进入阻塞状态,有的话解除阻塞,最后返回成功信息(pdPASS)。
使用实例:
void vATask( void * pvParameters ){/* 创建一个互斥量用于保护共享资源 */xSemaphore = xSemaphoreCreateMutex();if ( xSemaphore != NULL ) {// 这个好像不需要if ( xSemaphoreGive( xSemaphore ) != pdTRUE ) {/*如果要释放一个互斥量,必须先有第一次的获取*/}/* 获取互斥量,不等待 */if ( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) ) {/* 获取到互斥量,可以访问共享资源 *//* ... 访问共享资源代码 *//* 共享资源访问完毕,释放互斥量 */if ( xSemaphoreGive( xSemaphore ) != pdTRUE ) {/* 互斥量释放失败,这可不是我们希望的 */}}}}
19.6.7 递归互斥量释放函数xSemaphoreGiveRecursive()
xSemaphoreGiveRecursive()是一个用于释放递归互斥量的宏
#if( configUSE_RECURSIVE_MUTEXES == 1 )#define xSemaphoreGiveRecursive( xMutex ) \xQueueGiveMutexRecursive( ( xMutex ) )#endif
xSemaphoreGiveRecursive()函数用于释放一个递归互斥量。已经获取递归互斥量的任务可以重复获取该递归互斥量。使用 xSemaphoreTakeRecursive() 函数成功获取几次递归互斥量,就要使用 xSemaphoreGiveRecursive()函数返还几次,在此之前递归互斥量都处于无效状态, 别的任务就无法获取该递归互斥量。 使用该函数接口时,只有已持有互斥量所有权的任务才能释放它,每释放一次该递归互斥量,它的计数值就减 1。当该互斥量的计数值为 0 时(即持有任务已经释放所有的持有操作), 互斥量则变为开锁状态,等待在该互斥量上的任务将被唤醒。 如果任务的优先级被互斥量的优先级翻转机制临时提升,那么当互斥量被释放后, 任务的优先级将恢复为原本设定的优先级。
#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->pxMutexHolder == ( void * ) xTaskGetCurrentTaskHandle() ) /*lint !e961 Not a redundant cast as TaskHandle_t is a typedef. */{traceGIVE_MUTEX_RECURSIVE( pxMutex );/* 调用次数的计数值减一 */( pxMutex->u.uxRecursiveCallCount )--;/* 如果计数值减到 0 */if( pxMutex->u.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 /* configUSE_RECURSIVE_MUTEXES */
使用示例:
{/* 创建一个递归互斥量用于保护共享资源 */xMutex = xSemaphoreCreateRecursiveMutex();}void vAnotherTask( void * pvParameters ){/* 其他功能代码 */if ( xMutex != NULL ) {/* 尝试获取递归互斥量,如果不可用则等待 10 个 ticks */if(xSemaphoreTakeRecursive(xMutex,( TickType_t ) 10 )== pdTRUE) {/* 获取到递归信号量,可以访问共享资源 *//* ... 其他功能代码 *//* 重复获取递归互斥量 */xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 );/* 释放递归互斥量,获取了多少次就要释放多少次 */xSemaphoreGiveRecursive( xMutex );xSemaphoreGiveRecursive( xMutex );xSemaphoreGiveRecursive( xMutex );/* 现在递归互斥量可以被其他任务获取 */} else {/* 没能成功获取互斥量,所以不能安全的访问共享资源 */}}}
19.7 函数调用关系总结
19.7.1 创建
19.7.2 获取
19.9.3 释放

