freeRtos源码分析之任务调度原理

freeRtos源码分析之调度原理

1.任务切换的本质

​ 在FreeRtos中任务切换的本质是函数调用,CPU在指定时间内执行不同的函数,从微观上看每个任务都是顺序执行的,但是CPU运算能力很强,可以在很短时间内完成指令的执行,从宏观上看每个任务相当与同时在执行。

1.1 在ram-M3架构下函数调用原理

在这里插入图片描述

2.任务的几种状态

在这里插入图片描述

  • 运行态:running
  • 就绪态:ready
  • 阻塞:blocked,等待某件事(时间、事件)
  • 暂停:suspend,休息去了

3.任务控制块TCB

在这里插入图片描述
TCB_t用来表示一个任务,它的重要成员如下:

  • pxTopOfStack:栈顶。
  • xStateListItem:通过它把当前任务放入某个状态链表(Ready, Blocked, Suspended)
  • xEventListItem:比如任务在等待队列A,则通过xEventListItem把自己放入队列A的链表
  • uxPriority:任务的原始优先级
  • pxStack:栈的起始位置
  • pxEndOfStack:栈底,有效地址
  • uxBasePriority:任务的当前优先级
  • pcTaskName:任务名称

4.任务创建函数

可以从任务创建含函数追述到任务控制块的成员都被指向了那。函数原型

BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
                            const char * const pcName, 
                            const configSTACK_DEPTH_TYPE usStackDepth,
                            void * const pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t * const pxCreatedTask )

在xTaskCreate函数中先对TCB控制块进行初始化。

pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );

然后对栈进行初始化

pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );

然后调用了prvInitialiseNewTask函数进行初始化。函数原型

static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
                                  const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
                                  const uint32_t ulStackDepth,
                                  void * const pvParameters,
                                  UBaseType_t uxPriority,
                                  TaskHandle_t * const pxCreatedTask,
                                  TCB_t * pxNewTCB,
                                  const MemoryRegion_t * const xRegions )

在prvInitialiseNewTask函数中对pxTopOfStack进行判断处理,即根据栈的增长方向对pxTopOfStack和pxEndOfStack的值进行初始化。
在这里插入图片描述
在M3中configRECORD_STACK_HIGH_ADDRESS宏被定义成了1所以说明栈是高地址向低地址移动的(向下增长)。
在调用了pxPortInitialiseStack函数对任务的栈进行初始化,及保存CPU对应寄存器地址,函数原型。

StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
                                     TaskFunction_t pxCode,
                                     void * pvParameters )

在这里插入图片描述
可以看到任务函数的地址最终被指向了PC寄存器,也就是当前程序运行的位置,修改PC寄存器的值即可让程序运行到指定位置。

5.任务调度

5.1任务怎么管理

在freeRtos中是通过链表来管理任务的
在这里插入图片描述
在任务创建函数中最后调用了prvAddNewTaskToReadyList函数添加到就绪链表中,在进行链表操作时进入了临界区,保证不会被打断。
在这里插入图片描述
在这里插入图片描述
在该函数中如果调度器没有运行,当前任务控制块也就是当前任务一直是优先级最高的任务先执行,如果是同优先级,那么就是最后一个被创建的任务第一个执行。同时uxTopReadPriorty的值表示就绪链表中优先级最高的那个任务。

5.2启动任务调度函数

在创建完任务后,通过调用vTaskStartScheduler函数启动任务调度,该函数会创建一个空闲任务,空闲任务主要做两个事情

函数原型

static portTASK_FUNCTION( prvIdleTask, pvParameters )
  • 当前优先级为0的就绪链表中如果有大于1个任务,那么空闲任务会主动触发一次任务切换。
    在这里插入图片描述
    在这里插入图片描述

  • 如果有任务删除了自己,那么空闲任务会释放该任务对应的资源。
    在这里插入图片描述

最终vTaskStartScheduler函数会调用xPortStartScheduler函数,在该函数中将PendSV和SysTick中断设置为低优先级。
在这里插入图片描述
然后调用 vPortSetupTimerInterrupt函数设置SysTick中断产生的间隔。
在这里插入图片描述
然后又调用prvStartFirstTask函数将SP指向中断向量表,然后进入SVC执行第一个任务函数。
在这里插入图片描述
在这里插入图片描述

5.3任务间如何切换

在xPortSysTickHandler中断服务函数中调用xTaskIncrementTick函数去判断是否要进行任务切换,如果切换就会返回pdTure,同时产生PendSV中断进行任务切换。
在这里插入图片描述
在这里插入图片描述

### FreeRTOS 任务切换原理及实现方式 #### 1. 时间片轮转调度 FreeRTOS 使用时间片轮转的方式来进行多任务调度。当多个任务具有相同的优先级时,这些任务会按照一定的时间间隔轮流占用 CPU 资源[^1]。具体来说,在每一个时钟节拍结束时,当前正在运行的任务会被暂停,并将其上下文保存到对应的任务控制块 (TCB) 中。随后,系统会选择下一个同优先级的任务并恢复其上下文继续执行。 #### 2. 上下文切换过程 在任务切换过程中,系统的操作可以分为以下几个方面: - **保存当前任务的上下文**:这一步骤涉及将寄存器中的数据存储到当前任务对应的堆栈中。 - **调用 `vTaskSwitchContext` 函数**:该函数负责选择下一个需要运行的任务。它基于任务的状态列表以及优先级信息决定谁将是下一任运行者[^2]。 - **恢复目标任务的上下文**:一旦选定新的任务,则从它的 TCB 结构体读取之前保存好的状态参数,并加载回处理器寄存器以便重新启动此任务。 #### 3. 任务控制块(TCB) 每个任务都有自己的 TCB 对象用于管理相关信息,比如栈指针位置(`pxTopOfStack`)、链表项(xStateListItem),还有初始分配给它的内存区域起点(pxStack)[^3]。通过初始化第一个任务(Task1),我们可以看到如何设置这些字段以准备首次调度: ```c typedef struct tskTaskControlBlock { volatile StackType_t *pxTopOfStack; /* 栈顶 */ ListItem_t xStateListItem; /* 任务节点 */ StackType_t *pxStack; /* 任务栈起始地址 */ char pcTaskName[configMAX_TASK_NAME_LEN];/* 任务名称,字符串形式 */ } tskTCB; ``` #### 4. 高效计算逻辑 对于某些特定功能如确定最高有效位的位置等运算需求,FreeRTOS 利用了底层硬件特性(__clz指令),从而实现了更快更高效的算法性能优化[^4]。这样的设计使得即使是在资源受限环境下也能保持良好的响应速度和服务质量。 ```c #define portBIT_POSITION_IN_REGISTER(ulValue) ((31UL - __CLZ((uint32_t)(ulValue)))) ``` 上述代码片段展示了一个典型例子——定义宏portBIT_POSITION_IN_REGISTER用来找出某个非零无符号整数值中最左边‘1’所在的具体索引编号。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值