5.8 空闲任务及其钩子函数
空闲任务及其钩子函数
1 空闲任务的作用
空闲任务(Idle Task)是 FreeRTOS 在启动调度器时自动创建的一个特殊任务。
它有两个重要作用:
- 当系统中没有其他就绪任务时,保证调度器始终有任务可以运行
- 释放被删除任务所占用的内存
也就是说,空闲任务的存在可以保证:
即使所有用户任务都阻塞了,系统也不会无任务可运行。
2 空闲任务的特点
空闲任务具有以下特点:
- 由 FreeRTOS 自动创建
- 优先级最低,通常为 0
- 不会阻碍用户任务运行
- 要么处于就绪态,要么处于运行态
- 永远不会进入阻塞态
因为空闲任务优先级最低,所以只要有用户任务进入就绪态,调度器就会立即切换去执行用户任务。
也就是说:
空闲任务只会在没有其他更高优先级任务可运行时才执行。
3 为什么必须要有空闲任务
在一个较好的 FreeRTOS 程序中,很多用户任务通常都是事件驱动的,大部分时间会处于阻塞状态。
例如:
- 等待按键事件
- 等待队列数据
- 等待信号量
- 等待定时到期
这样就可能出现一种情况:
所有用户任务都暂时不能运行。
但调度器必须始终能找到一个可运行任务,因此 FreeRTOS 必须提供一个永远可运行的空闲任务。
所以,空闲任务的存在本质上是为了保证:
调度器任何时候都有任务可调度。
4 空闲任务与任务删除的关系
如果程序中使用了:
vTaskDelete()来删除任务,那么被删除任务所占用的内存,并不是立即由调用者直接释放,而是由空闲任务负责后续回收。
因此要注意:
如果空闲任务得不到执行机会,被删除任务的内存就无法及时释放。
这也是为什么在使用 vTaskDelete() 时,需要保证系统中不要让高优先级任务一直霸占 CPU,否则空闲任务无法运行。
5 什么是空闲任务钩子函数
FreeRTOS 允许用户给空闲任务添加一个钩子函数,称为:
Idle Hook Function
空闲任务每执行一次循环时,就会调用一次这个钩子函数。
对应的函数名是:
vApplicationIdleHook()也就是说:
空闲任务钩子函数,本质上就是在空闲任务运行时,额外执行的用户代码。
6 空闲任务钩子函数的作用
空闲任务钩子函数通常可以用来做一些低优先级的后台工作,例如:
- 执行一些优先级很低的后台任务
- 测量系统空闲时间,从而估算 CPU 使用率
- 在系统空闲时进入低功耗模式
- 执行一些需要反复检测但又不重要的处理
因此,钩子函数的特点是:
适合做不紧急、低优先级、后台连续执行的工作。
7 空闲任务钩子函数的限制
虽然空闲任务钩子函数很方便,但它有重要限制:
- 不能让空闲任务进入阻塞态
- 不能调用会导致任务延时或挂起的函数
- 执行时间要尽量短
- 如果系统会删除任务,那么钩子函数更要高效,不能长时间卡住空闲任务
因为如果空闲任务一直卡在钩子函数中,就可能影响:
- 被删除任务内存的释放
- 系统整体调度的流畅性
所以,空闲任务钩子函数一般只适合放少量、简单、快速执行的代码。
8 使用空闲任务钩子函数的前提
要想使用空闲任务钩子函数,需要满足两个条件:
8.1 在 FreeRTOSConfig.h 中使能钩子功能
需要把下面的宏定义设置为 1:
#define configUSE_IDLE_HOOK 18.2 实现钩子函数
用户需要自己编写这个函数:
void vApplicationIdleHook(void)
{
/* 用户代码 */
}只有同时满足这两个条件,空闲任务运行时才会调用这个钩子函数。
9 小结
空闲任务是 FreeRTOS 自动创建的最低优先级任务,它的主要作用是保证调度器始终有任务可运行,并负责释放被删除任务的内存。
空闲任务钩子函数 vApplicationIdleHook() 是用户挂接到空闲任务中的扩展函数,适合执行低优先级后台工作、统计系统空闲率或进入低功耗模式。
使用钩子函数前,需要在 FreeRTOSConfig.h 中把 configUSE_IDLE_HOOK 设置为 1,并实现 vApplicationIdleHook() 函数。同时要注意,钩子函数不能阻塞,执行时间也应尽量短。
