FreeRTOS调度器挂起与解除

通过分析任务切换,我们知道任务切换的两种方法:系统节拍器中断、调用portYIELD产生PendSV中断。

在系统节拍器中断中,如果调度器被挂起,仅仅将调度器挂起时间加一(在解除挂起后需要补偿这些节拍),并不会检查是否有任务需要切换。

/* 系统节拍加一 */
BaseType_t xTaskIncrementTick(void)
{
	BaseType_t xSwitchRequired = pdFALSE;
	
	/* 调度器没有被挂起 */
	if(uxSchedulerSuspended == (UBaseType_t)pdFALSE)
	{
		......
	}
	/* 调度器被挂起 */
	else
	{
		/* 挂起时间加一 */
		++uxPendedTicks;
	}

        /* 前面有程序因为各种原因,要求延迟到现在切换 */
	if(xYieldPending != pdFALSE)
	{
		/* 请求切换任务,最终进入PendSV异常,是否切换上下文还是在于PendSV */
		xSwitchRequired = pdTRUE;
	}

	return xSwitchRequired;
}

在PendSV中断中,如果调度器被挂起,则不进行上下文切换,通过xYieldPending将任务切换延迟到下一个节拍。

/* 任务切换上下文 */
void vTaskSwitchContext(void)
{
	/* 调度器被挂起 */
	if(uxSchedulerSuspended != (UBaseType_t)pdFALSE)
	{
		/* 等到下一次节拍的时候再切换上下文 */
		xYieldPending = pdTRUE;
	}
	/* 调度器没有挂起 */
	else
	{
        ......
	}
}

也就是说,只要将调度器挂起,就肯定不会进行任务切换。

挂起调度器非常简单,只要让uxSchedulerSuspended不等于pdFALSE(0)即可。调度器可以多次挂起,但是对应的也要进行多次解除挂起。

/* 挂起调度器 */
void vTaskSuspendAll(void)
{
	/* 挂起层数加一 */
	++uxSchedulerSuspended;
}

当完全解除调度器挂起时,需要进行如下工作:

检查在调度器挂起期间是否有任务进入就绪态,有则要将其从挂起期间就绪任务列表中移除,重新挂接到就绪任务列表

更新下一个需要解除阻塞的任务,的解除时间

要对之前调度器挂起期间产生的节拍进行补偿

对于延迟切换任务到下一个节拍的请求,在这里提供一次切换机会

/* 解除调度器挂起 */
BaseType_t xTaskResumeAll( void )
{
	TCB_t *pxTCB = NULL;
	BaseType_t xAlreadyYielded = pdFALSE;

	configASSERT(uxSchedulerSuspended);

	/* 进入临界区 */
	taskENTER_CRITICAL();
	{
		/* 调度器挂起层数减一 */
		--uxSchedulerSuspended;

		/* 调度器挂起完全解除 */
		if(uxSchedulerSuspended == (UBaseType_t)pdFALSE)
		{
			/* 系统当前任务数大于0 */
			if(uxCurrentNumberOfTasks > (UBaseType_t)0U)
			{
				/* 挂起时进入就绪的任务列表不为空 */
				while(listLIST_IS_EMPTY(&xPendingReadyList) == pdFALSE)
				{
					/* 从挂起时进入就绪的任务列表中取出一个任务 */
					pxTCB = listGET_OWNER_OF_HEAD_ENTRY((&xPendingReadyList));
					/* 将任务从事件列表中移除 */
					(void)uxListRemove(&(pxTCB->xEventListItem));
					/* 将任务从状态列表中移除 */
					(void)uxListRemove(&(pxTCB->xStateListItem));
					/* 将任务加入就绪任务列表 */
					prvAddTaskToReadyList(pxTCB);

					/* 如果任务优先级高于当前任务优先级,则请求在下一个节拍时切换 */
					if(pxTCB->uxPriority >= pxCurrentTCB->uxPriority)
					{
						xYieldPending = pdTRUE;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}

				/* 有任务在挂起期间就绪 */
				if(pxTCB != NULL)
				{
					/* 更新下一个要解除阻塞的时间 */
					prvResetNextTaskUnblockTime();
				}

				{
					/* 调度器挂起时间 */
					UBaseType_t uxPendedCounts = uxPendedTicks;

					/* 调度器挂起时间大于1个节拍 */
					if(uxPendedCounts > (UBaseType_t)0U)
					{
						/* 将所有节拍重新补上 */
						do
						{
							/* 节拍数加一,如果需要切换任务,则请求在下一个节拍时切换 */
							if(xTaskIncrementTick() != pdFALSE)
							{
								xYieldPending = pdTRUE;
							}
							else
							{
								mtCOVERAGE_TEST_MARKER();
							}
							--uxPendedCounts;
						}while(uxPendedCounts > (UBaseType_t)0U);

						uxPendedTicks = 0;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}

				/* 原来请求在下一个节拍时切换的任务,在这里直接请求切换 */
				if(xYieldPending != pdFALSE)
				{
					#if (configUSE_PREEMPTION != 0)
					{
						/* 已经切换过任务 */
						xAlreadyYielded = pdTRUE;
					}
					#endif
					taskYIELD_IF_USING_PREEMPTION();
				}
				else
				{
					mtCOVERAGE_TEST_MARKER();
				}
			}
		}
		else
		{
			mtCOVERAGE_TEST_MARKER();
		}
	}
	/* 退出临界区 */
	taskEXIT_CRITICAL();

	/* 返回是否已经切换过任务 */
	return xAlreadyYielded;
}

猜你喜欢

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