在前面提到,如果只是创建任务的话任务是执行不起来的,需要将其放入任务调度器中供系统调度,这样才能保证任务的执行,而任务调度的关键就在于vTaskStartScheduler()函数。
要注意的是,FreeRTOS中系统每时每刻都需要运行一个任务,这时空闲任务就显得必不可少了。空闲任务不可以被挂起或者删除,同时优先级也是所有任务中最低的,以防和其它任务抢CPU的使用权。当然,FreeRTOS贴心的帮各位实现了,不需要花费额外的精力自己去创一个。
在启动任务的过程中,不同的硬件架构会导致函数的代码改变。这里以ARM Cortex-M系列处理器为例(另外两个分别是A系列和R系列,这两个系列分别面向了更高端的应用,而M系列面向低端应用,尤其是那些对成本和功耗都比较敏感的应用)。在Cortex-M3中,任务启动与切换分别用到了SVC、PendSV、SysTick。
SVC被称为系统服务调用,用于任务的启动,部分操作系统只允许用户间接访问硬件,此时就需要通过SVC向系统服务函数发出呼叫,系统的SVC异常服务例程执行后,完成用户的呼叫请求。
PendSV成为可挂起系统调用,用于任务的切换,它可以被挂起,当高优先级中断运行时,PendSV会延迟执行,直到高优先级中断运行完毕。
SysTick用于产生时钟,提供时间片,多任务的优先级相同时,可以让SysTick中断,将时间片交给下一个任务。
不难看出,调度器是一个基于优先级的全抢占式调度。当然,凡事总有例外,中断处理函数、调度器锁定的代码以及禁止中断的代码是无法抢占的。
细心的读者应该也发现了,我在前面几次提到过挂起这个词,其实这就是任务的状态之一,之前提到的任务就绪也属于任务状态的范畴。
任务状态总共分为四种,就绪、运行、挂起、阻塞,每个任务都是在这四个状态中循环往复的。
就绪就是任务具备执行能力,随时可以被调度器调度,此时任务处于就绪列表中。
运行就是任务正在占用处理器。
阻塞状态比较特殊,此时任务在等待时序或者外部中断,此时的任务虽然不处于就绪列表中,但是仍然可以被调度器看见。
挂起与阻塞不同,挂起的任务就消失在了调度器的视野里,一般这种任务是较长时间不会执行的任务,但是总有什么时候会用到。
任务状态之间的切换规律如下:任务在初次创建完成后就进入就绪列表,处于就绪状态,等待任务调度器调度;任务切换后,该优先级的任务开始执行,此时处于运行状态;当有更高优先级的任务就绪时,该任务会被打断并重新进入就绪列表中排队。若是该任务遇到了时延,则会进入阻塞状态,此时就绪列表中就不会有该任务,直到阻塞恢复,再重新进入就序列表。上述的任何一种状态的任务都有可能被挂起,此时只需要调用vTaskSuspend()函数即可。
