目录
[二、FreeRTOS 四大任务状态](#二、FreeRTOS 四大任务状态)
[1、运行态 Running:](#1、运行态 Running:)
[2、就绪态 Ready:](#2、就绪态 Ready:)
[3、阻塞态 Blocked:](#3、阻塞态 Blocked:)
[4、挂起态 Suspended:](#4、挂起态 Suspended:)
[1、高优先级任务 while 循环无延时。](#1、高优先级任务 while 循环无延时。)
[3、任务调用 vTaskDelay 之后 CPU 占用率没有降下来。](#3、任务调用 vTaskDelay 之后 CPU 占用率没有降下来。)
前言
本篇重点剖析 FreeRTOS 内核调度逻辑:四大任务状态切换、优先级抢占机制、时间片轮转以及空闲任务。把这一块理解透彻之后再去学习队列、信号量,才能够真正处理多任务同步互斥带来的各类工程问题。
一、调度器到底做什么
FreeRTOS 调度器就是操作系统内核里负责挑选任务的模块。
单片机是单核 CPU,同一时刻只能够执行一份任务代码。调度器不断从就绪任务列表选出最合适的任务交给 CPU 执行。整体遵循两条核心规则:高优先级任务优先执行;同优先级就绪任务开启时间片轮转。
这里有一个很重要误区:多任务并不是真正物理并行运行。单核 MCU 只是高速来回切换任务,切换速度极快,给人的感官效果好像多个任务同时跑。
二、FreeRTOS 四大任务状态
1、运行态 Running:
任务此刻占有 CPU 资源,正在执行自身任务函数。单核单片机任意时间点最多只能有一个任务处于运行态。一旦发生任务切换,当前任务立刻退出运行态。
2、就绪态 Ready:
任务运行条件全部满足,可以随时投入运行,但是此刻 CPU 正在运行另一个优先级更高的任务。任务万事俱备,只等待调度器选中自己。新创建出来的任务默认进入就绪态。
3、阻塞态 Blocked:
任务主动放弃 CPU 进入等待状态,最典型场景就是调用 vTaskDelay 延时。处于阻塞态的任务不再消耗 CPU 时间,调度器直接跳过该任务,转而执行其它就绪任务。新手高频踩坑:任务内部使用 for 循环空延时等待,而不是 vTaskDelay,任务会一直停留在就绪态,死死占用 CPU 不放。
4、挂起态 Suspended:
调用 vTaskSuspend 把任务挂起。挂起之后任务直接停止,不会参与调度,哪怕没有任何延时,也得不到 CPU 执行机会,需要调用 vTaskResume 恢复才可以重新参与调度。
简单梳理状态流转:新建任务→就绪态;调度器选中→切换为运行态;调用延时 / 等待信号量→进入阻塞;延时结束回到就绪;调用挂起函数进入挂起;解除挂起回到就绪。
三、优先级抢占机制
FreeRTOS 默认开启优先级抢占。只要出现一个更高优先级任务变为就绪态,调度器立刻发生任务切换,暂停当前正在跑的低优先级任务,立刻执行高优先级任务。
举个工程例子:任务 A 优先级 2 正在运行,此刻任务 B 优先级 3 解除延时进入就绪,系统马上抢占 CPU,任务 B 开始运行。
这里一个非常常见工程坑:高优先级任务如果一直处于就绪态,永远不会让出 CPU,低优先级任务会彻底饿死,永远得不到执行机会。比如高优先级任务 while 循环里面没有 vTaskDelay,不停空跑,低优先级任务完全无法运行。很多人遇到优先级失效,实际就是发生任务饿死现象。
四、时间片轮转机制
抢占模式下,如果多个任务拥有完全相同优先级,就会启用时间片轮转。
每个任务分配固定长度时间片,任务用完自己时间片之后,即便还没有做完事情,也要让出 CPU,交给同优先级下其它就绪任务执行。
注意配置项 configUSE_TIME_SLICING,如果关闭这个宏,同优先级任务就不会做时间片轮转,一旦某个同优先级任务拿到 CPU 就会一直跑下去。实际项目一般保持打开。
时间片不等于延时,时间片只是分配 CPU 使用时长,任务如果中途进入阻塞态,会直接放弃剩余时间片。
五、空闲任务是什么
系统启动调度器之后会自动创建空闲任务,优先级是 0,也就是整个系统最低优先级。
当所有用户任务全部进入阻塞或者挂起状态,没有其它就绪任务的时候,CPU 就跑空闲任务。
空闲任务有几点工程注意事项:
- 千万不要在空闲任务里面做耗时业务逻辑;
- 如果开启 configUSE_IDLE_HOOK 空闲钩子函数,钩子函数内部不能调用会造成阻塞的 API,例如 vTaskDelay;空闲钩子里面一旦阻塞,系统行为就异常;
- 低功耗场景经常利用空闲钩子,在空闲任务里面进入单片机休眠。
六、工程实战现象总结
1、高优先级任务 while 循环无延时。
- 后果:一直占用 CPU,低优先级任务彻底饿死。,
- 解决:合适时机释放 CPU,调用阻塞类 API;
2、同优先级任务之间互不交替执行。
- 排查方向:检查 configUSE_TIME_SLICING 宏是否打开;
3、任务调用 vTaskDelay 之后 CPU 占用率没有降下来。
- 排查:确认是 vTaskDelay,不要自己写 for 循环空等待;
4、任务挂起之后,系统好像整体卡住。
- 排查:确认是否把全部任务都挂起,如果全部任务挂起,系统只会跑空闲任务。