目录
源码实现主要参考消息队列章节,因为底层源码是一样的,所以本章笔记侧重点在信号量、互斥量概念。
源码部分与消息队列重叠的函数不分析。
参考:李柱明博客
同步,执行完一个再到下一个,一条逻辑流。
异步,执行者着这个的时候也可执行另外一个,不止一条互相独立的逻辑流。
资源保护,控制资源的被访问权限。
在一个多任务并发系统中,可能存在多任务对共享资源的并发访问,这样可能会导致数据不可控、非预期。
所以我们需要对共享资源进行保护,而任务同步可以实现对共享资源访问进行保护,维护数据一致性,产出结果达预期。
实现任务同步常见的组件有信号量、互斥量、锁等等。
同步和互斥的区别:
信号量(Semaphore)也是实现任务间通信的机制的一种。
可实现任务间同步、临界资源的互斥访问。
信号量的核心其实就是一个非负值,表示当前资源数量:
但是在freertos内核,一个信号量除了核心的非负值外,还需要其他数据来维护当前信号量的特性、运作。
如读、写阻塞链表,实现其阻塞机制,信号量内部中断锁,实现其中断访问特性。
信号量细分:
二值信号量既可以用于临界资源访问也可以用于同步功能。
二值信号量和互斥量其信号量最大都是1,只有0和1两种状态,使用逻辑也类似,但是也有区别,主要在内部实现特性和上层应用场景方面:
例子运行条件:
运行过程描述如下:
该图源自安富莱
创建信号量:
获取信号量:
释放信号量:
semGIVE_BLOCK_TIME
个节拍,要么直接返回释放失败。计数信号量的最大资源大于1,主要用于计数。
获取信号量,信号量值减1;释放信号量,信号量值加1。
计数信号通常用于两种情况:
计算事件:
资源管理:
和二值信号量类似,只是资源最大值不止1。
互斥量是包含优先级继承机制的二值信号量。
而二值信号量是实现同步(任务之间或任务与中断之间)的更好选择,互斥量是实现简单互斥的更好选择。
互斥量就像保护资源的令牌一样。
当一个任务希望访问该资源时,它必须首先获得令牌。
当它使用完该资源时,它必须“归还”令牌——允许其他任务访问相同的资源。
互斥锁不应该在中断中使用,因为:
优先级继承:高优先级任务TH在等待低优先级的任务TL继承占用的竞争资源时,为了使TH能够尽快获得调度运行,由操作系统把TL的优先级提高到TH的优先级,从而让TL以TH的优先级参与调度,尽快让TL执行并释放调TH欲获得的竞争资源,然后TL的优先级调整到继承前的水平,此时TH可获得竞争资源而继续执行。
在FreeRTOS操作系统中为了降低优先级翻转问题利用了优先级继承算法。
不过优先级继承也不能解决优先级反转。
它只是在某些情况下将其影响降到最低。
举个栗子:
三个任务:任务A优先级10,任务B优先级5,任务C优先级1。
在任务C占用互斥量时,任务A就绪,也需要该互斥量,此时任务C的优先级会继承任务A的优先级,从优先级1跃升到优先级10。就算当前任务B就绪了,也不能打断任务C,因为优先级比10底。
和二值信号量类似,比二值信号量多个优先级继承机制。
就是互斥量具有递归性。
递归使用的互斥量可以被其所有者反复“获取”。
互斥量只有在所有者为每个成功的xSemaphoreTakeRecursive()
请求调用xSemaphoreGiveRecursive()
之后才会再次可用。
即是互斥量被同一个任务连续申请成功N次,就需要释放N次才算真正释放该互斥量。
互斥量类型的信号量不能在中断服务程序中使用。
因为:
参考互斥量运作机制,比互斥量多个递归性。
就是逻辑上获取一个已经被占用且逻辑上不可能被释放的锁而阻塞,永久阻塞。
避免死锁需要遵循的规则:
xSemaphoreCreateBinary()
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xSemaphoreCreateBinary() xQueueGenericCreate( ( UBaseType_t ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE )
#endif
就是创建一个类型queueQUEUE_TYPE_BINARY_SEMAPHORE
、是队列成员为1、不含数据区的队列。
xSemaphoreCreateCounting( uxMaxCount, uxInitialCount )
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
#endif
#if ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount,
const UBaseType_t uxInitialCount )
{
QueueHandle_t xHandle = NULL;
if( ( uxMaxCount != 0 ) && /* 最大信号量不能少于1,这是常识 */
( uxInitialCount <= uxMaxCount ) ) /* 初始值也不能超过最大信号量值 */
{
/* 创建一个类型为queueQUEUE_TYPE_COUNTING_SEMAPHORE、队列成员为uxMaxCount、且不含数据区的队列 */
xHandle = xQueueGenericCreate( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE );
if( xHandle != NULL )
{
/* 初始化当前可用资源值 */
( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount;
traceCREATE_COUNTING_SEMAPHORE();
}
else
{
traceCREATE_COUNTING_SEMAPHORE_FAILED();
}
}
else
{
configASSERT( xHandle );
mtCOVERAGE_TEST_MARKER();
}
return xHandle;
}
#endif
xSemaphoreCreateMutex()
:
#if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 )
#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
#endif
#if ( ( configUSE_MUTEXES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) )
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;
}
#endif
#if ( configUSE_MUTEXES == 1 )
static void prvInitialiseMutex( Queue_t * pxNewQueue )
{
if( pxNewQueue != NULL )
{
/* 在调用xQueueGenericCreate()创建队列的时候,默认都是队列,联合体初始化的是QueuePointers_t xQueue。
所以需要在这里初始化回SemaphoreData_t xSemaphore,这个成员非常重要,是实现优先级继承机制和递归互斥量的必要数据。
用于互斥量。 */
/* 互斥量持有者。初始化为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();
}
}
#endif /* configUSE_MUTEXES */
#if ( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configUSE_RECURSIVE_MUTEXES == 1 ) )
#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex( queueQUEUE_TYPE_RECURSIVE_MUTEX )
#endif
获取和释放信号量都是区分任务和中断调用的API的,其主要区别也是中断调用是不能阻塞。
这里主要分析任务调用。(中断调用,按区别理解下就可以了)
二值信号量、计数信号量、互斥量都是使用xSemaphoreTake()
获取信号量。
而递归互斥量使用xSemaphoreTakeRecursive()
获取互斥量。
#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;
#endif
/* 参数校验 */
configASSERT( ( pxQueue ) );
/* 信号量是不带数据区的 */
configASSERT( pxQueue->uxItemSize == 0 );
#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) )
{
/* 调度器挂起是不能以阻塞式调用 */
configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) );
}
#endif
/* 循环方式。是实现阻塞机制的逻辑方式 */
for( ; ; )
{
taskENTER_CRITICAL(); /* 进入临界 */
{
/* 备份下当前信号量值 */
const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting;
/* 信号量大于0,说明还有资源,可以被获取占用 */
if( uxSemaphoreCount > ( UBaseType_t ) 0 )
{
traceQUEUE_RECEIVE( pxQueue );
/* 信号量减1 */
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 /* 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();
}
/* 退出临界 */
taskEXIT_CRITICAL();
return pdPASS; /* 返回获取成功 */
}
else /* 如果信号量为空,没有可用资源。互斥量的话需要处理优先级继承机制。 */
{
if( xTicksToWait == ( TickType_t ) 0 ) /* 不阻塞 */
{
#if ( configUSE_MUTEXES == 1 )
{
/* 参数校验。不阻塞是不会因当前获取而发生优先级继承的。 */
configASSERT( xInheritanceOccurred == pdFALSE );
}
#endif /* configUSE_MUTEXES */
/* 退出临界,返回获取失败。 */
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 ) /* 阻塞未超时 */
{
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 /* if ( configUSE_MUTEXES == 1 ) */
/* 当前任务进入阻塞,从就绪链表中抽离,插入到延时链表中,并把该任务插入当前信号量的获取阻塞链表中 */
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();
/* 再次判断下是否真的没有资源,现在有资源还来得及 */
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(); /* 退出临界 */
}
}
#endif /* configUSE_MUTEXES */
traceQUEUE_RECEIVE_FAILED( pxQueue );
return errQUEUE_EMPTY; /* 返回获取失败 */
}
else /* 在超时了,退出前发现有资源,可以进入下一个循环获取 */
{
mtCOVERAGE_TEST_MARKER();
}
}
} /*lint -restore */
}
#if ( configUSE_RECURSIVE_MUTEXES == 1 )
#define xSemaphoreTakeRecursive( xMutex, xBlockTime ) xQueueTakeMutexRecursive( ( xMutex ), ( xBlockTime ) )
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() ) /* 如果递归互斥量已经被占用了,持有者是当前任务的话,可以获取递归互斥量成功 */
{
/* 递归深度加1 */
( pxMutex->u.xSemaphore.uxRecursiveCallCount )++;
xReturn = pdPASS; /* 返回获取成功 */
}
else /* 递归互斥量没有被占用,或者递归互斥量已经被占用,但是持有者不是当前任务 */
{
/* 获取互斥量处理 */
xReturn = xQueueSemaphoreTake( pxMutex, xTicksToWait );
if( xReturn != pdFAIL ) /* 获取成功 */
{
/* 递归深度加1 */
( pxMutex->u.xSemaphore.uxRecursiveCallCount )++;
}
else
{
traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex );
}
}
return xReturn;
}
#endif
获取和释放信号量都是区分任务和中断调用的API的,其主要区别也是中断调用是不能阻塞。
这里主要分析任务调用。(中断调用,按区别理解下就可以了)
二值信号量、计数信号量、互斥量都是使用xSemaphoreGive()
获取信号量。
而递归互斥量使用xSemaphoreGiveRecursive()
获取互斥量。
注意:互斥量和递归互斥量只有持有者才有权限释放。
参考消息队列章节。
#define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( QueueHandle_t ) ( xSemaphore ), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK )
#if ( configUSE_RECURSIVE_MUTEXES == 1 )
#define xSemaphoreGiveRecursive( xMutex ) xQueueGiveMutexRecursive( ( xMutex ) )
#endif
#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->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle() ) /* 如果递归互斥量已经被占用了,持有者是当前任务的话,可以被释放 */
{
traceGIVE_MUTEX_RECURSIVE( pxMutex );
/* 递归深度建1 */
( 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;
}
#endif
vSemaphoreDelete()
用于删除一个信号量,包括二值信号量,计数信号量,互斥量和递归互斥量。
#define vSemaphoreDelete( xSemaphore ) vQueueDelete( ( QueueHandle_t ) ( xSemaphore ) )
注意:当持有者持有多个互斥量时,不能通过单个互斥量来解除或者重置优先级继承的优先级,只能选择忽略。这种情况也会存在优先级翻转。
优先级继承机制主要数据:Queue_t->u->xQueue
。
typedef struct SemaphoreData
{
TaskHandle_t xMutexHolder; /* 当前互斥量的持有者 */
UBaseType_t uxRecursiveCallCount; /* 当前互斥量被递归调用的深度 */
} SemaphoreData_t;
在互斥量被其它任务占用,当前高优先级任务因为该互斥量而进入阻塞时,会发生优先继承,互斥量持有者的任务优先级会跃升到阻塞在当前接收阻塞链表中最高,且比持有者高的任务优先级。
xTaskPriorityInherit()
:
#if ( configUSE_MUTEXES == 1 )
BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder )
{
TCB_t * const pxMutexHolderTCB = pxMutexHolder;
BaseType_t xReturn = pdFALSE;
/* 互斥锁已被占用 */
if( pxMutexHolder != NULL )
{
/* 持有者优先级比当前任务优先级低,需要更新优先级继承 */
if( pxMutexHolderTCB->uxPriority < pxCurrentTCB->uxPriority )
{
/* 持有者的事件节点值没有被其它IPC占用(如事件组组件),方可设置为优先级相关的值 */
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 ) /* 解除任务状态 */
{
/* 更新任务优先级位图 */
portRESET_READY_PRIORITY( pxMutexHolderTCB->uxPriority, uxTopReadyPriority );
}
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;
}
#endif /* configUSE_MUTEXES */
互斥量持有者真正释放互斥量时,方可解除优先级继承
正常由持有者解除:xTaskPriorityDisinherit()
:
#if ( configUSE_MUTEXES == 1 )
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 ) /* 继承过 */
{
/* 只有当持有者这个任务不再持有任何互斥量时,才能解除优先级继承。
因为只解除当前的互斥量,但是当前优先级继承可能继承的是其它互斥量的,所以不能直接解除。 */
if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 ) /* 持有者不再占有任何互斥量 */
{
/* 解除任务状态 */
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
/* 更新优先级位图 */
portRESET_READY_PRIORITY( pxTCB->uxPriority, uxTopReadyPriority );
}
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;
}
}
}
return xReturn;
}
#endif /* configUSE_MUTEXES */
获取互斥量阻塞阻塞超时时会检查重置优先级继承。
高优先级任务阻塞超时而解除:
prvGetDisinheritPriorityAfterTimeout()
vTaskPriorityDisinheritAfterTimeout()
prvGetDisinheritPriorityAfterTimeout()
:
#if ( configUSE_MUTEXES == 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;
}
#endif /* configUSE_MUTEXES */
vTaskPriorityDisinheritAfterTimeout()
:
只有持有者只持有当前互斥量才能重置优先级继承,因为如果持有者持有多个互斥量时,并不能只参考当前互斥量来重置优先级。
#if ( configUSE_MUTEXES == 1 )
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, uxPriorityToUse );
/* 备份持有者优先级 */
uxPriorityUsedOnEntry = pxTCB->uxPriority;
/* 更新持有者优先级 */
pxTCB->uxPriority = uxPriorityToUse;
/* 持有者的事件节点值没有被其它IPC占用(如事件组组件),方可设置为优先级相关的值 */
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 ) /* 解除任务状态 */
{
/* 更新就绪任务位图 */
portRESET_READY_PRIORITY( pxTCB->uxPriority, uxTopReadyPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 重新插入就绪链表 */
prvAddTaskToReadyList( pxTCB );
}
else /* 不是就绪态就不用管 */
{
mtCOVERAGE_TEST_MARKER();
}
}
else /* 持有者持有多个互斥量,也不需要重置优先级继承 */
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_MUTEXES */
手机扫一扫
移动阅读更方便
你可能感兴趣的文章