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

freeRtos源码分析之调度原理

1.任务切换的本质

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

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

freeRtos源码分析之任务调度原理_第1张图片

2.任务的几种状态

freeRtos源码分析之任务调度原理_第2张图片

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

3.任务控制块TCB

freeRtos源码分析之任务调度原理_第3张图片
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的值进行初始化。
freeRtos源码分析之任务调度原理_第4张图片
在M3中configRECORD_STACK_HIGH_ADDRESS宏被定义成了1所以说明栈是高地址向低地址移动的(向下增长)。
在调用了pxPortInitialiseStack函数对任务的栈进行初始化,及保存CPU对应寄存器地址,函数原型。

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

freeRtos源码分析之任务调度原理_第5张图片
可以看到任务函数的地址最终被指向了PC寄存器,也就是当前程序运行的位置,修改PC寄存器的值即可让程序运行到指定位置。

5.任务调度

5.1任务怎么管理

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

5.2启动任务调度函数

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

函数原型

static portTASK_FUNCTION( prvIdleTask, pvParameters )
  • 当前优先级为0的就绪链表中如果有大于1个任务,那么空闲任务会主动触发一次任务切换。
    freeRtos源码分析之任务调度原理_第8张图片
    freeRtos源码分析之任务调度原理_第9张图片

  • 如果有任务删除了自己,那么空闲任务会释放该任务对应的资源。
    freeRtos源码分析之任务调度原理_第10张图片

最终vTaskStartScheduler函数会调用xPortStartScheduler函数,在该函数中将PendSV和SysTick中断设置为低优先级。
freeRtos源码分析之任务调度原理_第11张图片
然后调用 vPortSetupTimerInterrupt函数设置SysTick中断产生的间隔。
freeRtos源码分析之任务调度原理_第12张图片
然后又调用prvStartFirstTask函数将SP指向中断向量表,然后进入SVC执行第一个任务函数。
freeRtos源码分析之任务调度原理_第13张图片
freeRtos源码分析之任务调度原理_第14张图片

5.3任务间如何切换

在xPortSysTickHandler中断服务函数中调用xTaskIncrementTick函数去判断是否要进行任务切换,如果切换就会返回pdTure,同时产生PendSV中断进行任务切换。
freeRtos源码分析之任务调度原理_第15张图片
freeRtos源码分析之任务调度原理_第16张图片

你可能感兴趣的:(RTOS,stm32,arm)