信号量API函数实际上都是宏,它使用现有的队列机制。这些宏定义在semphr.h文件中。如果使用信号量或者互斥量,需要包含semphr.h头文件。
信号量包括二值信号量、计数信号量、互斥信号量和递归互斥信号量。和普通队列比起来,信号量虽然没有队列项实体,但是信号量值等同于队列项个数。
互斥锁和递归互斥锁:互斥锁是用来保证共享数据操作的完整性,同时只能有一个任务访问共享数据。递归互斥锁和普通互斥锁比起来,同一个任务可以多次获取递归互斥锁,在释放同等次数之后才能解锁。
在分析互斥锁之前,先搞清楚两个概念,优先级反转和优先级继承
优先级反转:互斥锁被低优先级的任务持有,高优先级任务等待解锁。这时中等优先级任务一直运行,这导致高优先级任务在等待低优先级任务,而低优先级任务无法执行。这种高优先级等待中优先级的情况,叫做优先级反转。
优先级继承:为了解决优先级反转而提出优化机制,让低优先级任务临时继承高优先级任务的优先级。在低优先级释放互斥锁之后,还要恢复原来的优先级。
先看一下任务TCB
因为优先级继承机制,在互斥锁释放后需要恢复优先级,uxBasePriority在优先级继承期间被用来保存任务优先级
uxMutexesHeld表示任务持有了多少个互斥锁
/* 任务TCB */
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; /* 栈顶地址 */
......
ListItem_t xStateListItem; /* 状态列表项:运行、就绪、挂起、阻塞 */
ListItem_t xEventListItem; /* 事件列表项 */
UBaseType_t uxPriority; /* 优先级 */
StackType_t *pxStack; /* 栈指针 */
char pcTaskName[configMAX_TASK_NAME_LEN]; /* 任务名 */
......
#if (configUSE_MUTEXES == 1)
UBaseType_t uxBasePriority; /* 任务基础优先级 */
UBaseType_t uxMutexesHeld; /* 互斥锁持有数量 */
#endif
......
}tskTCB;
typedef tskTCB TCB_t;
创建互斥锁
互斥锁实际上是调用了队列的创建函数,创建好之后调用prvInitialiseMutex来初始化一些互斥锁特有的变量
#define xSemaphoreCreateMutex() xQueueCreateMutex(queueQUEUE_TYPE_MUTEX)
/* 创建互斥锁 */
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;
}
prvInitialiseMutex函数,将互斥锁初始化为没有被任何任务持有,并且处于解锁状态(队列中有一个队列项)
/* 初始化互斥锁 */
static void prvInitialiseMutex(Queue_t *pxNewQueue)
{
if(pxNewQueue != 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();
}
}
获取互斥锁
互斥锁也叫互斥信号量,获取互斥锁和获取信号量使用同一个函数
和普通信号量不同的是:
1.获取成功时,将互斥锁持有者设为当前任务,当前任务持有互斥锁数量加一
2.因互斥锁被其它任务持有而阻塞时,为了防止优先级反转现象,进行优先级继承处理
3.因互斥锁被其它任务持有而阻塞,超时之后,因为可能进行了优先级继承,剥夺原先继承的优先级(剥不剥夺看优先级是否继承自该任务)
从源代码看,调用xTaskPriorityInherit函数来进行优先级继承;在超时之后,使用prvGetDisinheritPriorityAfterTimeout和vTaskPriorityDisinheritAfterTimeout函数来恢复优先级
#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);
/* 信号量值减一 */
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
/* 等待释放信号量而阻塞的任务列表不为空 */
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;
}
/* 信号量值为0 */
else
{
/* 等待时间为0 */
if(xTicksToWait == (TickType_t)0)
{
#if (configUSE_MUTEXES == 1)
{
configASSERT(xInheritanceOccurred == pdFALSE);
}
#endif
/* 退出临界区 */
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)
{
/* 检查信号量值是否为0,为0 */
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
/* 将任务插入等待获取信号量而阻塞的任务列表中 */
vTaskPlaceOnEventList(&(pxQueue->xTasksWaitingToReceive), xTicksToWait);
/* 解锁队列 */
prvUnlockQueue(pxQueue);
/* 解除调度器挂起,没有切换过任务 */
if(xTaskResumeAll() == pdFALSE)
{
/* 请求切换任务 */
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* 队列不为空,while下一个循环时取走队列项 */
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
traceQUEUE_RECEIVE_FAILED(pxQueue);
/* 返回队列为空错误 */
return errQUEUE_EMPTY;
}
/* 队列不为空,while下一个循环时取走队列项 */
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
}
释放互斥锁
从源代码来看,看不出和普通信号量处理有什么区别。
但是,因为互斥锁可能存在优先级继承的问题,因此释放互斥锁时肯定需要恢复优先级。
事实上,恢复优先级在prvCopyDataToQueue函数中进行。
#define xSemaphoreGive(xSemaphore) xQueueGenericSend((QueueHandle_t)(xSemaphore), NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK)
/* 发送队列项 */
BaseType_t xQueueGenericSend(QueueHandle_t xQueue, const void *const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition)
{
BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired;
TimeOut_t xTimeOut;
Queue_t *const pxQueue = xQueue;
configASSERT(pxQueue);
configASSERT(!((pvItemToQueue == NULL) && (pxQueue->uxItemSize != (UBaseType_t)0U)));
configASSERT(!((xCopyPosition == queueOVERWRITE) && (pxQueue->uxLength != 1)));
#if ((INCLUDE_xTaskGetSchedulerState == 1) || (configUSE_TIMERS == 1))
{
configASSERT(!((xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) && (xTicksToWait != 0)));
}
#endif
for(;;)
{
/* 进入临界区 */
taskENTER_CRITICAL();
{
/* 目前已插入队列项数小于最大可插入队列数或者覆盖型插入 */
if((pxQueue->uxMessagesWaiting < pxQueue->uxLength) || (xCopyPosition == queueOVERWRITE))
{
traceQUEUE_SEND(pxQueue);
#if (configUSE_QUEUE_SETS == 1)
{
UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting;
xYieldRequired = prvCopyDataToQueue(pxQueue, pvItemToQueue, xCopyPosition);
if(pxQueue->pxQueueSetContainer != NULL)
{
if((xCopyPosition == queueOVERWRITE) && (uxPreviousMessagesWaiting != (UBaseType_t)0))
{
mtCOVERAGE_TEST_MARKER();
}
else if(prvNotifyQueueSetContainer(pxQueue, xCopyPosition) != pdFALSE)
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
if(listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE)
{
if(xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE)
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else if(xYieldRequired != pdFALSE)
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
#else
{
/* 根据不同的入队方式,将队列项数据拷贝到队列中 */
xYieldRequired = prvCopyDataToQueue(pxQueue, pvItemToQueue, xCopyPosition);
/* 等待接收队列项而阻塞的任务列表不为空 */
if(listLIST_IS_EMPTY(&(pxQueue->xTasksWaitingToReceive)) == pdFALSE)
{
/* 将任务从事件列表中移除一个任务,并挂接到就绪列表 */
if(xTaskRemoveFromEventList(&(pxQueue->xTasksWaitingToReceive)) != pdFALSE)
{
/* 请求切换任务 */
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* 等待接收队列项而阻塞的任务列表为空 */
else if(xYieldRequired != pdFALSE)
{
queueYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
/* 退出临界区 */
taskEXIT_CRITICAL();
/* 成功 */
return pdPASS;
}
/* 目前队列已经满了,且不是覆盖型插入 */
else
{
/* 阻塞时间为0 */
if(xTicksToWait == (TickType_t)0)
{
taskEXIT_CRITICAL();
traceQUEUE_SEND_FAILED(pxQueue);
/* 返回队列已满错误 */
return errQUEUE_FULL;
}
/* 当前节拍状态还未记录 */
else if(xEntryTimeSet == pdFALSE)
{
/* 记录当前节拍状态 */
vTaskInternalSetTimeOutState(&xTimeOut);
/* 当前节拍状态已经记录 */
xEntryTimeSet = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
/* 退出临界区 */
taskEXIT_CRITICAL();
/* 挂起调度器 */
vTaskSuspendAll();
/* 锁定队列 */
prvLockQueue(pxQueue);
/* 检查任务是否超时,并未超时 */
if(xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE)
{
/* 检查队列是否已满,已经满了 */
if(prvIsQueueFull(pxQueue) != pdFALSE)
{
traceBLOCKING_ON_QUEUE_SEND(pxQueue);
/* 将任务挂接到等待发送而阻塞的任务列表中,并将任务挂接到延时列表中 */
vTaskPlaceOnEventList(&(pxQueue->xTasksWaitingToSend), xTicksToWait);
/* 解锁队列 */
prvUnlockQueue(pxQueue);
/* 解除调度器挂起 */
if(xTaskResumeAll() == pdFALSE)
{
/* 请求切换 */
portYIELD_WITHIN_API();
}
}
/* 刚好队列出现空位,下一次while循环重新插入 */
else
{
/* 解锁队列 */
prvUnlockQueue(pxQueue);
/* 解除调度器挂起 */
(void)xTaskResumeAll();
}
}
/* 已经超时或者超时之后 */
else
{
/* 解锁队列 */
prvUnlockQueue(pxQueue);
/* 解除调度器挂起 */
(void)xTaskResumeAll();
traceQUEUE_SEND_FAILED(pxQueue);
/* 队列已满 */
return errQUEUE_FULL;
}
}
}
下面看一下prvCopyDataToQueue函数,看看是怎么恢复优先级的
从源代码来看是通过xTaskPriorityDisinherit函数来恢复优先级
/* 将队列项拷贝到队列中 */
static BaseType_t prvCopyDataToQueue(Queue_t *const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition)
{
BaseType_t xReturn = pdFALSE;
UBaseType_t uxMessagesWaiting;
/* 队列中队列项数 */
uxMessagesWaiting = pxQueue->uxMessagesWaiting;
/* 队列项大小为0 */
if(pxQueue->uxItemSize == (UBaseType_t)0)
{
#if (configUSE_MUTEXES == 1)
{
/* 队列类型为互斥锁 */
if(pxQueue->uxQueueType == queueQUEUE_IS_MUTEX)
{
/* 剥夺继承优先级 */
xReturn = xTaskPriorityDisinherit(pxQueue->u.xSemaphore.xMutexHolder);
/* 互斥锁持有者设置为空 */
pxQueue->u.xSemaphore.xMutexHolder = NULL;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
}
/* 队列项大小不为0,从队列尾部插入 */
else if(xPosition == queueSEND_TO_BACK)
{
/* 将队列项插入队列 */
(void)memcpy((void *)pxQueue->pcWriteTo, pvItemToQueue, (size_t)pxQueue->uxItemSize);
/* 将队列项写入指针向后偏移一个队列项 */
pxQueue->pcWriteTo += pxQueue->uxItemSize;
/* 队列项写入指针已经偏移到队列尾部了 */
if(pxQueue->pcWriteTo >= pxQueue->u.xQueue.pcTail)
{
/* 将队列项写入指针偏移到头部 */
pxQueue->pcWriteTo = pxQueue->pcHead;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* 队列项大小不为0,从队列头部插入/覆盖插入 */
else
{
/* 将队列项插入队列 */
(void)memcpy((void *)pxQueue->u.xQueue.pcReadFrom, pvItemToQueue, (size_t)pxQueue->uxItemSize);
/* 将队列项读出指针向前偏移一个队列项 */
pxQueue->u.xQueue.pcReadFrom -= pxQueue->uxItemSize;
/* 队列项读出指针已经偏移到队列头部了 */
if(pxQueue->u.xQueue.pcReadFrom < pxQueue->pcHead)
{
/* 将队列项读出指针偏移到尾部 */
pxQueue->u.xQueue.pcReadFrom = (pxQueue->u.xQueue.pcTail - pxQueue->uxItemSize);
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 覆盖式插入 */
if(xPosition == queueOVERWRITE)
{
/* 队列项个数大于0 */
if(uxMessagesWaiting > (UBaseType_t)0)
{
/* 因为覆盖了一个队列项,所以队列项数减一 */
--uxMessagesWaiting;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* 队列项数加一 */
pxQueue->uxMessagesWaiting = uxMessagesWaiting + (UBaseType_t)1;
return xReturn;
}
创建递归互斥锁
递归互斥锁和互斥锁的创建过程是一样的,只是互斥锁的类型不同
#define xSemaphoreCreateMutex() xQueueCreateMutex(queueQUEUE_TYPE_MUTEX)
获取递归互斥锁
和互斥锁不同的是,同一任务可以多次获取递归互斥锁。因此,主要步骤为:
1.如果当前任务不持有该互斥锁,则申请持有互斥锁,递归次数加一
2.如果当前任务已经持有该互斥锁,则直接递归次数加一
/* 获取递归互斥锁 */
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())
{
/* 递归次数加一 */
(pxMutex->u.xSemaphore.uxRecursiveCallCount)++;
xReturn = pdPASS;
}
/* 互斥锁持有者不为当前任务 */
else
{
/* 持有该互斥锁 */
xReturn = xQueueSemaphoreTake(pxMutex, xTicksToWait);
if(xReturn != pdFAIL)
{
/* 递归次数加一 */
(pxMutex->u.xSemaphore.uxRecursiveCallCount)++;
}
else
{
traceTAKE_MUTEX_RECURSIVE_FAILED(pxMutex);
}
}
return xReturn;
}
释放递归互斥锁
和互斥锁不同的是,同一任务可以多次获取递归互斥锁,解除同等次数才能彻底释放。因此,主要步骤为:
1.递归次数减一
2.当递归次数减完,彻底释放互斥锁
/* 释放递归互斥锁 */
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);
/* 递归次数减一 */
(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;
}
优先级继承和剥夺
从上面的分析,我们知道优先级继承和剥夺函数如下
优先级继承:xTaskPriorityInherit
互斥锁阻塞超时后,恢复优先级:prvGetDisinheritPriorityAfterTimeout和vTaskPriorityDisinheritAfterTimeout
解放互斥锁,恢复优先级:xTaskPriorityDisinherit
下面我们一个一个来分析,首先是xTaskPriorityInherit
1.当前任务优先级和互斥锁持有任务优先级进行比较,将互斥锁持有任务的优先级更新为更高的那个优先级
2.将互斥锁持有任务按照新的优先级重新放到就绪列表中
/* 任务优先级继承 */
BaseType_t xTaskPriorityInherit(TaskHandle_t const pxMutexHolder)
{
TCB_t *const pxMutexHolderTCB = pxMutexHolder;
BaseType_t xReturn = pdFALSE;
/* 互斥锁持有者不为空 */
if(pxMutexHolder != NULL)
{
/* 互斥锁持有者优先级小于当前任务优先级 */
if(pxMutexHolderTCB->uxPriority < pxCurrentTCB->uxPriority)
{
/* 将互斥锁持有者的事件列表项值(任务优先级)设置为当前任务事件列表项值(任务优先级) */
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)
{
/* 检查就绪列表中是否有任务,如果没有将该优先级从当前任务优先级记录中清除 */
taskRESET_READY_PRIORITY(pxMutexHolderTCB->uxPriority);
}
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;
}
prvGetDisinheritPriorityAfterTimeout和vTaskPriorityDisinheritAfterTimeout
用法
UBaseType_t uxHighestWaitingPriority;
/* 获取剩下所有等待互斥锁任务的最高优先级 */
uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout(pxQueue);
/* 超时之后剥夺继承优先级 */
vTaskPriorityDisinheritAfterTimeout(pxQueue->u.xSemaphore.xMutexHolder, uxHighestWaitingPriority);
prvGetDisinheritPriorityAfterTimeout获取超时后可能需要剥夺的优先级
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;
}
vTaskPriorityDisinheritAfterTimeout超时后剥夺优先级
1.先计算剥夺继承当前任务优先级后,需要重新设置的继承优先级
2.检查互斥锁持有者的优先级是否继承自当前任务
3.如果互斥锁持有者的优先级是否继承自当前任务,那么就需要重新设置的继承优先级
4.设置新的优先级,并将互斥锁持有者从原就绪列表中转移到新的就绪列表中
注意:FreeRTOS中处理的并不完美,如果互斥锁持有者如果持有不止一把锁的话,即使互斥锁持有者的优先级是否继承自当前任务也不进行剥夺,这可能导致互斥锁的持有任务优先级并不完全准确。
/* 超时之后剥夺继承优先级 */
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, pxTCB->uxBasePriority);
/* 保存任务优先级 */
uxPriorityUsedOnEntry = pxTCB->uxPriority;
/* 互斥锁持有者优先级设置为剩下等待互斥锁任务的最高优先级 */
pxTCB->uxPriority = uxPriorityToUse;
/* 将事件列表值也改成剩下等待互斥锁任务的最高优先级 */
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)
{
taskRESET_READY_PRIORITY(pxTCB->uxPriority);
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 重新添加到新优先级就绪列表中 */
prvAddTaskToReadyList(pxTCB);
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
xTaskPriorityDisinherit,剥夺优先级继承
1.先判断是否继承了优先级
2.如果继承了优先级,恢复基础优先级,并将任务根据新优先级从原就绪列表中转移到新的就绪列表
注意:FreeRTOS中处理的并不完美,如果互斥锁持有者如果持有不止一把锁的话,并不会剥夺继承优先级,这可能导致互斥锁的持有任务优先级并不完全准确。
/* 剥夺继承优先级 */
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)
{
/* 任务持有锁数量为0 */
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;
}