FreeRTOS 任务调度 任务创建

本文详细介绍了FreeRTOS的任务调度实现,包括任务状态、任务创建(静态与动态)、任务控制块(TCB)和链表数据结构。重点讨论了任务创建的xTaskCreateStatic和xTaskCreate函数,以及如何将新任务插入就绪链表,确保高优先级任务得到及时执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

@(嵌入式)

Freertos
FreeRtos

简述

FreeRTOS 的任务调度在 Source/include/task.c 中实现,包含了任务的创建、切换、挂起、延时和删除等所有功能。涉及到的链表组织见文章 FreeRTOS 任务调度 List 组织 。任务切换实现代码量比较大,因此关于任务调度这一块会分几个文章来描述,这一篇主要分析任务的创建的调用与实现。

分析的源码版本是 v9.0.0
(为了方便查看,github 上保留了一份源码Source目录下的拷贝)

任务状态

TaskState

系统运行过程,任务可能处于以下各种状态,各个状态之间切换的关系如上图所示。
* Running
运行状态, 当前正在执行,占有处理器的任务
* Ready
就绪状态,准备被运行的任务,没有被挂起和阻塞,但不是当前正在执行的任务,等待更高优先级任务或者同等级任务时间片结束释放处理器
* Blocked
阻塞状态,任务在等待一个事件而进入阻塞状态,比如延时、获取信号量等
* Suspended
挂起状态,任务由于调用 vTaskSuspend() 而被挂起不能被执行, 直到调用 xTaskResume() 重新恢复

使用示例

FreeRTOS 中创建任务并开始调度的基本框架如下 :

void vATaskFunction( void *pvParameters )
{
    for( ;; )
    {
    // -- 任务代码 --
    }
    // 任务不能有任何 返回
    // 对自行结束的任务,退出前需要自行清理
    vTaskDelete( NULL );
}

void main(void)
{
    static unsigned char ucParameterToPass;  
    xTaskHandle xHandle;  
    xTaskCreate( vATaskFunction, /*任务实现函数*/
                "TASK_NAME", /*任务名,方便调试*/
                STACK_SIZE,  /*任务堆栈大小 *StackType_t*/
                &ucParameterToPass, /*任务运行时的参数*/ 
                tskIDLE_PRIORITY, /*任务优先级*/
                &xHandle );  /*回传任务句柄,供其他地方引用任务*/
    // 其他任务和拉拉杂杂的初始化
    // 启动任务调度器 loop ....
}

任务创建函数中, 设置的栈大小单位由使用平台的 StackType_t 决定,不同平台栈指针对齐有自己的要求。
回传的句柄(指向TCB的指针)一般用于在其他任务中发送消息通知给任务,或者删除任务时引用。
任务成功创建后返回 pdPASS, 否则失败回传错误码。

另外,删除任务,可以通过其他任务中调用 voidvTaskDelete进行删除,此时该任务会从各种链表中移除,并且内存会被马上回收; 但是如果是任务自己调用删除,则其内存回收需要由空闲任务来完成(毕竟当前正在使用这些资源)。
使用 voidvTaskDelete 的前提是在 FreeRTOSConfig.h 设置 INCLUDE_vTaskDelete 为1(Tips !! API 在使用前最后需要看看是否需要设置对应的宏定义)。


叙述完上层的调用,后续介绍背后具体是如何实现的。

数据结构

TCB

任务调度离不开任务控制块(TCB), 用于存储任务的状态信息、运行时环境等。源代码见 tskTaskControlBlock, 以下具体介绍下这个数据结构。

typedef struct tskTaskControlBlock
{
    // 任务栈顶指针
    volatile StackType_t *pxTopOfStack;
    // 启用MPU 的情况下设置 
    #if ( portUSING_MPU_WRAPPERS == 1 )
        // 设置任务访问内存的权限
        xMPU_SETTINGS xMPUSettings;
    #endif

    // 状态链表项(Ready, Blocked, Suspended)
    // 任务处于不同状态 该项会被插入到对应的链表, 供链表引用任务
    ListItem_t xStateListItem;
    // 事件链表项
    // 比如任务延时挂起等,被插入到延时链表中,到时间或事件发生,链表引用唤醒任务
    ListItem_t xEventListItem;
    // 任务优先级 0 最低
    UBaseType_t uxPriority;
    // 任务栈内存起始地址
    StackType_t *pxStack;           
    // 任务名, 字符串, 一般供调试时使用
    char pcTaskName[ configMAX_TASK_NAME_LEN ];

    // 对于向上生长的栈, 用于指明栈的上边界,用于判断是否溢出
    #if ( portSTACK_GROWTH > 0 )
        StackType_t *pxEndOfStack;
    #endif

    // 边界嵌套计数
    #if ( portCRITICAL_NESTING_IN_TCB == 1 )
        UBaseType_t uxCriticalNesting;
    #endif

    #if ( configUSE_TRACE_FACILITY == 1 )
        // 调试, 标识这个任务是第几个被创建
        // 每创建一个任务, 系统有个全局变量就会加一, 并赋值给这个新任务
        UBaseType_t uxTCBNumber; 
        // 调试 供用户设置特定数值 
        UBaseType_t uxTaskNumber;
     #endif

    #if ( configUSE_MUTEXES == 1 )
        // 涉及互斥锁下的优先级继承(避免优先级反转), queue 那边介绍
        // 当优先级被临时提高(继承了拿锁被堵的高优先级任务)时,这个变量保存任务实际的优先级
        UBaseType_t uxBasePriority;     
        UBaseType_t uxMutexesHeld;
    #endif

    #if ( configUSE_APPLICATION_TASK_TAG == 1 )
        TaskHookFunction_t pxTaskTag;
    #endif

    #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 )
        // 存储一些本地数据的指针
        void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ];
    #endif

    #if( configGENERATE_RUN_TIME_STATS == 1 )
        // 记录任务运行状态下的总时间
        uint32_t ulRunTimeCounter;
    #endif

    #if ( configUSE_NEWLIB_REENTRANT == 1 )</
### FreeRTOS任务调度原理 #### 任务状态转换 在FreeRTOS中,任务可以在运行态、就绪态、阻塞态和挂起态之间进行转换。当一个更高优先级的任务变为可执行状态时,当前正在运行的任务会被中断并保存其上下文环境,以便稍后恢复执行[^1]。 #### 就绪链表的作用 为了高效管理和快速查找处于就绪状态下的各个不同优先级的任务列表,FreeRTOS维护了一个全局的就绪链表。每当有新的任务进入就绪队列或是现有任务的状态发生变化时,该链表都会相应更新以反映最新的情况[^2]。 #### 可剥夺型与非可剥夺型内核的区别 FreeRTOS支持两种类型的内核——可剥夺型(preemptive)和不可剥夺型(non-preemptive)。对于前者而言,在任何时候只要存在更高等级的任务准备好了就可以立即抢占CPU资源;而对于后者,则只有等到当前活动中的进程主动释放处理器控制权之后才会考虑下一个待处理的工作项[^3]。 #### 调度算法细节 默认情况下采用的是基于固定优先级的抢占式调度方法,并且在同一级别内部实现了轮转法来分配时间片给各成员共享使用。“固定优先级”指的是在整个生命周期里除了因特殊原因触发外一般不会改变对象本身所设定的重要性程度【注:这里的特殊情况通常指代了所谓的‘优先级继承’机制】。这意味着一旦定义好初始条件以后就不会轻易变动除非必要情形下才允许临时调整权重大小从而影响整体排序逻辑。 ```c // 示例代码展示如何创建一个新的任务 void vCreateTask(void (*pvTaskCode)( void * ), const char *const pcName, unsigned short usStackDepth, void *pvParameters, UBaseType_t uxPriority, TaskHandle_t *pxCreatedTask ) { // 创建任务的具体实现... } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值