1.3.1.1 任务
本文将详细介绍freeRTOS的任务相关知识,主要分为以下四个部分:
- 任务状态
- 任务优先级
- 如何实现(写)一个任务
- 空闲任务及其钩子函数
任务状态
任务目前存在四种状态,分为:运行、就绪、阻塞、挂起;
运行 这是任务在执行的时候的状态,处在运行态意味着任务获得CPU的使用全,对于单核CPU,此时不存在其他运行态的任务。
就绪 处在就绪态意味着这个任务是可以执行的,比如某个事件发生、队列数据到来、所请求的资源有效等。但是,因为此时有一个相同优先级或者更高优先级的任务正在运行,此时任务无法执行而处于就绪态。
阻塞 如果一个任务正在等待一个时间到来或者外部的事件到来,比如一个调用
vTaskDelay()
函数的任务,在延时时间到来之前,任务将处在阻塞状态。当然,任务在等待队列、信号量、事件组、通知、信号量事件时都会处在阻塞态。通常,这些等待都会设置一个超时timeout
时间,当等待超时是将任务从阻塞态推出,防止任务被无线挂起。处在阻塞态的任务不占用任何CPU时间。挂起 挂起态无法进入运行态,同时不像阻塞态有超时时间,挂起态无超时时间,除非调用
vTaskResume()
推出挂起态。相应的调用vTaskSuspend()
将一个任务挂起。挂起态可以描述成"冻结",除非"解冻",否则无法再有机会被执行到。
下图是各个状态之间的相互转换:
任务优先级
每个任务在创建的时候都要指定一个从0到configMAX_PRIORITIES-1的优先级,与其他RTOS不同之处在于,优先级数值越大,优先级越低。configMAX_PRIORITIES
定义在freeRTOSConfig.h
文件中。
configMAX_PRIORITIES
的值可以是任意值,也就是说freeRTOS的任务数量不受限制。事实上,因为freeRTOS允许任务共享同一个优先级,configMAX_PRIORITIES
的大小不会限制到任务数量。但是对于实际应用来说,因为freeRTOS中即使未用到的优先级,任然会占用一定的内存,因此优先级的最大数值,应与实际应用贴近。
在freeRTOSConfig.h
文件中,定义了configUSE_PORT_OPTIMISED_TASK_SELECTION
,拥有某些存在任务选择优化机制的架构,此时最大优先级不能超过32。
freeRTOS中的空闲任务IDEL Task
总是最低优先级,因此它的优先级为0。
freeRTOS的调度器总是确保当前得到CPU时间的任务的优先级是最高的,换句话说,就绪态中高优先级的任务,总是会优先进入运行态。
对于共用同一优先级的任务,如果configUSE_TIME_SLICING
未定义或者定义为"1",则freeRTOS将使用时间片轮询的机制去调度这些任务。
任务的实现(如何写一个任务)
首先,任务具有如下形式:
void vATaskFunction( void *pvParameters ){
for( ;; )
{-- Task application code here. --
}
/* 通常,任务是不能从实现它们的地方跳出的,在
新版本的freeRTOS中,试图这样做会产生一个断言,
如果确实有必要这样做,可以调用下面的语句删除任务*/
vTaskDelete( NULL );}
所有的任务在申明的时候都应该使用上面的形式,即无返回值,参数是一个void *
类型的指针。因为所有的任务都不应该有返回,是一个死循环。同时,将参数申明成void *
类型的指针的好处是可以传递任意类型的参数。
参数使用xTaskCreat()
或者xTaskCreatStatic()
创建,使用vTaskDelete()
删除。
官方源码中提供另外一种通过宏来实现任务的方法,使用portTASK_FUNCTION_PROTO
,其实就是宏替换,比如:
portTASK_FUNCTION( vATaskFunction, pvParameters ){
for( ;; )
{-- Task application code here. --
}}
空闲任务及其钩子函数
空闲任务
空闲任务在freeRTOS调度器启动的时候,将会被自动创建。创建空闲任务的目的是为了保证系统在任何时候都会有一个任务在执行。当然,空闲任务的优先级最低,防止降低应用程序的实时性。空闲任务主要做的工作是去释放那些被删除的任务的所占有的内存,也就是垃圾回收。除此之外,空闲任务不包含其他功能因此,如果不使用vTaskDelete()
删除任务,空闲任务在它的执行期间会处在一个饥饿的状态。其他任务可以与空闲任务共享优先级,但是会有一些需要注意的问题,详细的会在介绍freeRTOSConfig.h
这个文件中介绍,见"进阶部分的内容"。
空闲任务的钩子函数
所谓钩子函数,即空闲任务每次执行期间都会被调用的函数,用户可以使用空闲任务的钩子函数进行一些如系统状态检测、休眠操作等。
值得注意的是,在使用钩子函数的时候,一是需要执行的时间短,而是不能导致空闲任务被挂起,比如调用vTaskDelay()
。前者会导致系统的实时性变差,后者会导致系统无法满足任意时刻都至少有一个任务在运行。
为了使用钩子函数,比如满足如下条件:
- 设置
configUSE_IDLE_HOOK
为1 - 定义钩子函数:
void vApplicationIdelHoo(void)
推荐在低功耗应用中,使用钩子函数使系统进入低功耗模式。