学习目标:freertos任务调度
学习内容:
- 任务的状态
- 就绪列表的结构
- 任务的结构
- 任务的调度原理
- 调度方式
- 任务的切换原理
- systick中断与pendsv中断
任务的状态
就绪态:任务创建建后就处于就绪态,就绪态可以变成运行态和挂起态,不能直接变成阻塞态
运行态:任务此时抢占CPU
阻塞态:当任务调用vTaskDelay相关的阻塞函数时将会从运行态转换为阻塞态
挂起态:调用vTaskSuspend()和vTaskResume()时任务可以从其他状态变为挂起态,从挂起态调用vtaskResume时将直接冲挂起态变为就绪态
就绪列表:
- 由于rtos中的任务是动态创建的,创建的新任务将插入到就绪列表的最前面,因此就绪列表是一个动态链表结构。就绪列表中存放的是任务控制块。且不同优先级的任务控制块组成一个链表。结构如下图所示。
任务的结构
任务控制块(TCB):任务的数据结构,记录任务的各种属性描述;
任务堆栈:创建任务时在ARM中分配一片内存,用于存储运行地址,函数参数等,维持任务的正常运行;
任务函数:任务具体的执行过程,有用户定义;
调度方式
抢占调度:高优先级的任务抢占低优先级的任务,当高优先级的任务阻塞时,低优先级的任务才能得到执行。
时间片调度:任务之间轮流执行一段时间
携程调度:主要用在小容量芯片上
任务的切换原理
PendSv异常:freertos利用pendsv异常来处理上下文切换,在多任务环境下,内核每次切换任务时,都会进入pendsv中段服务函数,进行切换任务栈操作。
在freertos中的两种指针介绍:
主堆栈指针(MSP)
复位后缺省使用的堆栈指针,用于操作系统内核以及异常处理例程(包括中断服务例程);
进程堆栈指针(PSP)
由用户的应用程序代码使用。
在中断服务函数使用MSP作为堆栈指针,如果工程中没有特殊设置(即非RTOS工程)整个工程都会默认使用MSP。如果工程使用了RTOS,则除了中断服务函数外,其他任务使用PSP作为堆栈指针。
FreeRtos的任务切换在PendSV中断服务函数中完成的,该中断服务函数在port.c中。
- 函数的工作内容主要包含一下内容:
1、保存现场
2、找到当前就绪任务中优先级较高的
3、恢复现场
systick中断和pendsv中断
问题:
看过Cortex-m3/m4操作系统RTOS内核代码的伙伴们都知道,OS中的任务调度实现方式如下:
1、开启SysTick中断,也就是系统滴答定时器中断。然后在SysTick中断中触发PendSV中断,实际的任务切换是在PendSV的中断服务函数中完成的。
2、PendSV的中断服务函数一般用汇编来写。
那为什么不直接在SysTick的中断服务函数里完成任务切换呢?
答: 如果将任务切换放在systick中,但systick的中断优先级设置为最高时,如果来了一个串口中断,刚接收一半,此时发生了任务切换,任务切换需要一定的时间,此时总线上已经没有数据了,可能找出数据丢失。
如果将systick中断优先级设置为最低如果来了高中断优先级的中断,中断处理执行较长的时间,此时就不能保证systick 的1ms心跳时钟的准确性了。
一般把systick的优先级设置为最高:保证操作系统时间的正确性
pendsv中断优先级设置为最低:保证在有其他中断执行时不进行任务切换