FreeRTOS互斥锁

信号量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;
}
发布了208 篇原创文章 · 获赞 90 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/lushoumin/article/details/88372000