(文章目录)


前言

本篇文章主要带大家深入分析空闲任务和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相关的源码。