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 1
UBaseType_t uxItemSize; // 4 0
volatile 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 ); // 1
prvInitialiseMutex( pxNewQueue ); // 2
return 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 NULL
static 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; // 2
traceCREATE_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 (or
the 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 created
then pxCurrentTCB will be NULL. */
if( pxCurrentTCB != NULL )
{
// + 1
( pxCurrentTCB->uxMutexesHeld )++;
}
// 返回TCB
return 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 {
/* 没能成功获取互斥量,所以不能安全的访问共享资源 */
}
}
}