FreeRTOS 任务调度 任务创建

@(嵌入式)

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 )
        
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值