前言
本篇文章主要带大家深入分析空闲任务和Tick中断的作用。
一、空闲任务源码分析
在启动调度器时会创建出空闲任务:
/* 启动调度器 */ vTaskStartScheduler();
在空闲任务中会调用到prvCheckTasksWaitingTermination();函数。
该函数会检查正在等待终止的任务列表,这是一组任务,它们已经执行完毕,但它们的资源(如堆栈空间和其他数据结构)还没有被完全释放。这个函数负责清理这些已经终止的任务的资源,以便可以重新使用这些资源。
当配置了configUSE_PREEMPTION
可抢占时和configIDLE_SHOULD_YIELD
时,空闲任务会调用taskYIELD()函数主动释放CPU资源,让其他任务运行。
当配置了configUSE_IDLE_HOOK
为1时,可以使用空闲任务的钩子函数:vApplicationIdleHook()。
二、Tick中断深入分析
systick中断分析:
xPortSysTickHandler函数
是SysTick定时器中断处理程序的一部分,它主要负责处理RTOS的时基计数、任务切换请求以及中断优先级的管理,以确保系统的稳定运行和任务切换的执行。这是实现实时任务调度的关键组成部分。
xTaskIncrementTick函数分析
用于检查调度器是否被挂起。如果调度器没有被挂起,才执行后续的操作。
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
这里增加系统的时基计数器 xTickCount,并将结果保存在 xConstTickCount 中,表示增加了一个时基节拍。
将 xConstTickCount 的值更新到系统的时基计数器 xTickCount 中。
const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1; /* Increment the RTOS tick, switching the delayed and overflowed * delayed lists if it wraps to 0. */ xTickCount = xConstTickCount;
这是一个条件语句,检查是否发生了时基计数器的溢出。如果 xConstTickCount 的值变为 0,表示发生了溢出。
如果发生了溢出,调用 taskSWITCH_DELAYED_LISTS 函数,用于处理延时任务列表的切换。
if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. */ { taskSWITCH_DELAYED_LISTS(); }
检查一个时钟计数变量 xConstTickCount 是否大于等于 xNextTaskUnblockTime。如果条件成立,意味着现在是可以解除任务阻塞的时刻。
if( xConstTickCount >= xNextTaskUnblockTime )
判断阻塞链表是否为空,如果为空那么就没有需要解除阻塞的任务,这时会将xNextTaskUnblockTime 设置为一个无限大的值,这样的话就不需要再次进行判断是否需要接触阻塞了。
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) { /* The delayed list is empty. Set xNextTaskUnblockTime * to the maximum possible value so it is extremely * unlikely that the * if( xTickCount >= xNextTaskUnblockTime ) test will pass * next time through. */ xNextTaskUnblockTime = portMAX_DELAY; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ break; }
如果需要接触阻塞的话,需要先获取延时任务列表中头部任务的控制块指针 pxTCB。
然后获取这个任务当中的xStateListItem 链表项的值,该值表示任务应该在何时解除阻塞。
pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );
如果当前值小于阻塞链表中第一个任务的值,那么更新xNextTaskUnblockTime,并且退出,此时不需要解除阻塞状态。
if( xConstTickCount < xItemValue ) { /* It is not time to unblock this item yet, but the * item value is the time at which the task at the head * of the blocked list must be removed from the Blocked * state - so record the item value in * xNextTaskUnblockTime. */ xNextTaskUnblockTime = xItemValue; break; /*lint !e9011 Code structure here is deedmed easier to understand with multiple breaks. */ }
xTaskIncrementTick 函数的主要功能是增加系统的时基计数,处理时基计数的溢出,以及检查是否有任务的阻塞时间已到期,如果有,则标记需要进行任务切换。这是实现 FreeRTOS 的任务调度和超时管理的关键部分。
总结
本篇文章主要为大家讲解了空闲任务和Tick中断深入分析,大家可以尝试自己分析一遍FreeRTOS相关的源码。