FreeRTOS【3】任务调度算法
重要概念
在运行的任务,被称为"正在使用处理器",它处于运行状态。在单处理系统中,任何时间里只能有一个任务处于运行状态。
非运行状态的任务,它处于这 3 中状态之一:阻塞(Blocked)、暂停(Suspended)、就绪(Ready)。
就绪态的任务,可以被调度器挑选出来切换为运行状态,调度器永远都是挑选最高优先级的就绪态任务并让它进入运行状态。
阻塞状态的任务,它在等待"事件",当事件发生时任务就会进入就绪状态。
事件分为两类:时间相关的事件、同步事件。
所谓时间相关的事件,就是设置超时时间:在指定时间内阻塞,时间到了就进入就绪状态。使用时间相关的事件,可以实现周期性的功能、可以实现超时功能。
同步事件就是:某个任务在等待某些信息,别的任务或者中断服务程序会给它发送信息。怎么"发送信息"?方法很多,有:任务通知(task notification)、队列(queue)、事件组(event group)、信号量(semaphoe)、互斥量(mutex)等。这些方法用来发送同步信息,比如表示某个外设得到了数据。
配置调度算法
所谓调度算法,就是怎么确定哪个就绪态的任务可以切换为运行状态。
通 过 配 置 文 件 FreeRTOSConfig.h 的 两 个 配 置 项 来 配 置 调 度 算 法 :
- configUSE_PREEMPTION、
- configUSE_TIME_SLICING。
- configUSE_TICKLESS_IDLE,它是一个高级选项,用于关闭 Tick中断来实现省电,后续单独讲解。 现在configUSE_TICKLESS_IDLE 设为 0,先不使用这个功能。
调度算法的行为主要体现在两方面:高优先级的任务先运行、同优先级的就绪态任务如何被选中。调度算法要确保同优先级的就绪态任务,能"轮流"运行,策略是"轮转调度"(Round Robin Scheduling)。轮转调度并不保证任务的运行时间是公平分配的,我们还可以细化时间的分配方法。
从 3 个角度统一理解多种调度算法:
- 可否抢占?高优先级的任务能否优先执行(配置项: configUSE_PREEMPTION)
- 可以:被称作"可抢占调度"(Pre-emptive),高优先级的就绪任务马上执行,下面再细化。
- 不可以:不能抢就只能协商了,被称作"合作调度模式"(Co-operativeScheduling) ,这种模式下:当前任务执行时,更高优先级的任务就绪了也不能马上运行,只能等待当前任务主动让出 CPU 资源。 其他同优先级的任务也只能等待(更高优先级的任务都不能抢占,平级的更应该老实点)。
- 可抢占的前提下,同优先级的任务是否轮流执行(配置configUSE_TIME_SLICING)
- 轮流执行:被称为"时间片轮转"(Time Slicing),同优先级的任务轮流执行,你执行一个时间片、我再执行一个时间片
- 不轮流执行:英文为"without Time Slicing",当前任务会一直执行,直到主动放弃、或者被高优先级任务抢占。(如果任务1一直执行直到高任务抢占,高任务放弃执行时候,就会执行任务1同级别的其他任务了)
- 在"可抢占"+"时间片轮转"的前提下,进一步细化:空闲任务是否让步于用户任务(配置项: configIDLE_SHOULD_YIELD)
- 空闲任务低人一等,每执行一次循环,就看看是否主动让位给用户任务
- 空闲任务跟用户任务一样,大家轮流执行,没有谁更特殊。
以下是整理后的 Markdown 表格格式:
配置项 | A (常用) | B (很少用) | C (很少用) | D (很少用) | E (几乎不用) |
---|---|---|---|---|---|
模式 | 可抢占+时间片轮转+空闲任务让步 | 可抢占+时间片轮转+空闲任务不让步 | 可抢占+非时间片轮转+空闲任务让步 | 可抢占+非时间片轮转+空闲任务不让步 | 合作调度 |
configUSE_PREEMPTION | 1 | 1 | 1 | 1 | 0 |
configUSE_TIME_SLICING | 1 | 1 | 0 | 0 | x |
configIDLE_SHOULD_YIELD | 1 | 0 | 1 | 0 | x |