FreeRTOS调度器解挂(xTaskResumeAll)的理解

先上源码,如下:

BaseType_t xTaskResumeAll( void )
{
TCB_t *pxTCB = NULL;
BaseType_t xAlreadyYielded = pdFALSE;

	/* If uxSchedulerSuspended is zero then this function does not match a
	previous call to vTaskSuspendAll(). */
	configASSERT( uxSchedulerSuspended );

	/* It is possible that an ISR caused a task to be removed from an event
	list while the scheduler was suspended.  If this was the case then the
	removed task will have been added to the xPendingReadyList.  Once the
	scheduler has been resumed it is safe to move all the pending ready
	tasks from this list into their appropriate ready list. */
	taskENTER_CRITICAL();
	{
		--uxSchedulerSuspended;

		if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
		{
			if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )
			{
				/* Move any readied tasks from the pending list into the
				appropriate ready list. */
				while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE )
				{
					pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) );
					( void ) uxListRemove( &( pxTCB->xEventListItem ) );
					( void ) uxListRemove( &( pxTCB->xStateListItem ) );
					prvAddTaskToReadyList( pxTCB );

					/* If the moved task has a priority higher than the current
					task then a yield must be performed. */
					if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
					{
						xYieldPending = pdTRUE;
					}
					else
					{
						mtCOVERAGE_TEST_MARKER();
					}
				}

				if( pxTCB != NULL )
				{
					/* A task was unblocked while the scheduler was suspended,
					which may have prevented the next unblock time from being
					re-calculated, in which case re-calculate it now.  Mainly
					important for low power tickless implementations, where
					this can prevent an unnecessary exit from low power
					state. */
					prvResetNextTaskUnblockTime();
				}

				/* If any ticks occurred while the scheduler was suspended then
				they should be processed now.  This ensures the tick count does
				not	slip, and that any delayed tasks are resumed at the correct
				time. */
				{
					UBaseType_t uxPendedCounts = uxPendedTicks; /* Non-volatile copy. */

					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;
}

 xTaskResumeAll这个函数使用的时候,一定是有一个挂起函数vTaskSuspendAll在前面用到了,二者一个是将调度器挂起,一个是恢复调度器。

当然挂起调度器太简单了,就是对全局变量uxSchedulerSuspended的自加,而这个变量一旦大于0,以后所有的任务的状态的迁移都需要先看看它的脸色行事,它只要不是零,任务就没法切换,这个挂起的操作就只说这么多。

关键的在于解挂的内容怎么会有这么多。这里好理解的笔者也不想多说,什么将变量uxSchedulerSuspended自减回去这种,大家都明白。笔者花时间去想的东西是如下这段:

                {
					UBaseType_t uxPendedCounts = uxPendedTicks; /* Non-volatile copy. */

					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();
					}
				}

这个里面是在做什么呢?注释翻译过来是:

如果在调度器挂起这段时间产生滴答定时器的计时并且在这段时间有任务解除阻塞,由于调度器的挂起导致没法切换任务,当恢复调度器的时候应立即处理这些任务。这样确保了滴答定时器的计数不会滑动,并且任何在延时的任务都会在正确的时间恢复。

这句话里面不好理解就在于,他说在调度器挂起这段时间产生滴答定时器的计时并且在这段时间有任务解除阻塞,实际上是说,正常来说,假设调度器挂起的这段时间,没有挂起调度器,那么滴答定时器的处理函数会按部就班的进行,那么xTaskIncrementTick函数也会按部就班的执行,我们在看xTaskIncrementTick代码的时候,里面有一个if判断,就是当调度器没有被挂起的时候,记录时钟的全局变量是xTickCount,那么如果调度器被挂起了呢,这时候变成了uxPendedTicks这个变量。这样做是因为在调度器被挂起的时候,不能在xTaskIncrementTick这个函数里面执行任务切换要做的事,包括删除那些到时间的延时任务,然后把他们加入到就绪列表中,因为挂起了任务调度器,哪怕你是从阻塞态到了就绪态的条件,所有的任务都因为调度器被挂起了,那你怎么去从阻塞态返回到就绪态呢,正常来说也该是先到挂起态啊,正点原子有个例子就说明了这个问题:

所以说,在xTaskResumeAll中,只有当uxSchedulerSuspended自减以后,xTaskIncrementTick才会执行包含xTickCount那个分支的操作,也就是删除延时阻塞的任务,加入到就绪列表中等等操作。那这个操作不是因为调度器的挂起而没做吗?所以才要在调度器解挂的过程中,去把这些事做了,也就是在调度器挂起的期间,耽误的这些时间,xTickCount没有自加,以及那些阻塞时间到了的延时队列中的任务的删除操作和加入就绪队列中的加入操作再做到。做几次呢,就是说xTickCount这个时钟节拍,在调度器挂起的时间花了多少个时钟节拍呢,以及在这个期间,有多少延时任务是到了时间还没去做状态列表的迁移呢?这个次数都记录在xTaskIncrementTick中uxSchedulerSuspended !=0的那个分支,也就是else中由uxPendedTicks这个全局变量记录下来了,这也就解释了为什么要用do while循环执行xTaskIncrementTick那么多次,其实就是把调度器挂起过程中xTaskIncrementTick应该执行的if循环在调度器恢复后再执行掉。

  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值