FreeRTOS 的任务具有如下几种状态:
运行 | Running |
---|---|
就绪 | Ready |
阻塞 | Blocked |
挂起 | Suspended |
除了运行状态之外的状态统称为非运行状态。因为 FreeRTOS 是为单CPU设计的系统,在任何时刻最多只能允许一个任务处在运行状态,哪怕看起来好像有多个任务同时在运行——这只是多个任务不停地切换带来的效果。当一个任务从运行状态切换到非运行状态时,执行时的现场——CPU寄存器被保存在任务的私有堆栈中;在重新回到运行状态时,再从堆栈中恢复之间保存的寄存器。这是任务调度的最基本功能。
就绪的任务
FreeRTOS 任务的就绪状态表示任务目前没有被执行,但随时可以被执行。当下一次任务切换时机到来时,FreeRTOS 将从就绪任务的列表中选择优先级最高的任务,切换成运行状态。
任务优先级是任务的一个属性,FreeRTOS 用整型数表示优先级,最低优先级为0, 最高为 configMAX_PRIORITIES 宏定义的值减去1. 创建任务的时候需要指定一个优先级,在创建之后也可以再通过 vTaskPrioritySet() 函数更改。优先级的意义是,某一个任务处于就绪状态时,只要还存在比它优先级高的任务也处于就绪状态,它就得不到执行的机会。
当有两个以上相同优先级的任务处于就绪状态时,它们会轮流地得到机会执行(但不保证执行时间是平均分配的),系统不会偏向其中任一个。
任务切换的时机
FreeRTOS 在以下情况下必然发生任务切换:
(1) 运行中的任务调用 vTaskSuspend() 将自己挂起。
(2) 运行中的任务调用了延时函数,要求等待一段时间后再执行。
(3)运行中的任务需要等待一个未发生的同步事件。
因为是当前任务主动暂停执行,FreeRTOS 需要将它切换到挂起或者阻塞状态,然后从就绪状态的任务列表中选择最高优先级的任务来执行。若已经没有其它需要执行的任务了,还会执行系统自带的,具有最低优先级的 Idle task. 还有一个特殊的情况是
(4) 运行中的任务调用了 taskYIELD() 主动要求切换任务。
这时候如果就绪任务列表中有同一或更高优先级的任务,将发生任务切换,否则会返回当前任务继续执行。
如果 FreeRTOS 配置中有 configUSE_PREEMPITON=1, 则进行抢占式任务调度。抢占的意义是在任务就绪列表中新加入的任务(包括新建任务、从挂起状态恢复的任务、从阻塞状态退出的任务),以及被修改了优先级的任务,具有比当前运行任务更高的优先级时,立即切换到那个更高优先级的任务执行。而先前运行的任务不知道自己的执行在何时被暂停,故称为“被抢占”。
抢占式调度保证了最高优先级的任务一旦运行条件成熟,就会立即得到处理器资源。不过如果最高优先级的任务不止一个时,默认它们是不会相互抢占的,除非再配置了 configUSE_TIME_SLICING=1, 由 FreeRTOS 进行时间片管理。启用时间片管理之后,相同优先级的就绪任务轮流执行,每个任务每次最多执行一个固定的周期,基本上让处理器资源平均分配。
小结一下,在抢占式调度下任务切换的时机有:
(5) 运行中的任务被更高优先级的任务抢占。
(6) 运行中的任务执行到一个时间片末尾,被同一优先级的任务抢占。