FreeRTOS 因为早期的开源免费商用(遵循MIT开源许可协议),占据了 RTOS 绝大部分市场。
随着多核MCU的普及,FreeRTOS也紧跟潮流,支持多核调度。今天就来分享一下 FreeRTOS 单核、多核的调度策略。
单核调度是所有RTOS都具有的功能,也是必备的最基础的功能。FreeRTOS的单核调度策略和市面上很多RTOS相同或类似,也就是:默认使用固定优先级的抢占式调度策略,对同等优先级的任务执行时间切片轮询调度。
固定优先级:指调度器不会永久更改任务的优先级, 但可能会因优先级继承而暂时提高任务的优先级。
抢占式:指调度器始终运行优先级最高且可运行的 RTOS 任务, 无论任务何时能够运行。例如, 如果中断服务程序 (ISR) 更改了优先级最高且可运行的任务, 调度器会停止当前正在运行的低优先级任务 并启动高优先级任务——即使这发生在同一个时间片内 。这种情况下可以说高优先级任务 “抢占”了低优先级任务。
轮询调度:指具有相同优先级的任务轮流进入运行状态。
时间切片:指调度器会在每个 tick 中断上在同等优先级任务之间进行切换, tick 中断之间的时间构成一个时间切片。(tick 中断是 RTOS 用来衡量时间的周期性中断。)
配置调度策略
FreeRTOSConfig.h是整个系统的配置文件,这里有两个调度相关的配置。
1、configUSE_PREEMPTION
#define configUSE_PREEMPTION 1
如果 configUSE_PREEMPTION 为 0,则表示抢占已关闭, 而且只有当运行状态的任务进入“阻塞”或“挂起”状态, 或运行状态任务调用 taskYIELD(), 或中断服务程序 (ISR) 手动请求上下文切换时,才会发生上下文切换。
这个配置在早期版本是没有的,后面新增的功能。同样,默认配置 1
#define configUSE_TIME_SLICING 1
如果 configUSE_TIME_SLICING 为 0,则表示时间切片已关闭, 因此调度器不会在每个 tick 中断上在同等优先级的任务之间切换。
MCU多核调度分为:对称多处理 (SMP) 和 非对称多处理 (AMP)。使用 FreeRTOS 的对称多处理 (SMP) 指的是一个 FreeRTOS 实例可以跨多个处理器核心调度 RTOS 任务。 由于只有一个 FreeRTOS 实例在运行,一次只能使用 FreeRTOS 的一个“移植”, 因此每个核心必须具有相同的处理器架构并共用相同的内存空间。针对(SMP)对称多处理,同样在FreeRTOSConfig.h配置文件中有相关的配置。a、configRUN_MULTIPLE_PRIORITIES如果 configRUN_MULTIPLE_PRIORITIES 设置为 0,则只有在多个任务具有相同优先级的情况下, 调度器才会同时运行多个任务。这可以修复基于下列假设编写的代码:一次将只运行一个任务,但同时必须牺牲 SMP 配置带来的一些好处。b、configUSE_CORE_AFFINITY如果 configUSE_CORE_AFFINITY 设置为 1, 则 vTaskCoreAffinitySet() API 函数可用于定义某个任务可以在哪些核心上运行以及不可以在哪些核心上运行。使用该方法,应用程序编写者可以防止同时执行假设了自身执行顺序的两个任务 。使用 FreeRTOS 的非对称多处理 (AMP) 是指多核设备的每个核心都单独运行自己的 FreeRTOS 实例。这些核心并不都需要具有相同架构, 但如果 FreeRTOS 实例之间需要进行通信,则需要共享一些内存。每个核心都会运行自己的 FreeRTOS 实例, 因此任何给定核心上的调度算法与上文的单核系统调度算法完全相同。你可以使用流缓冲区或消息缓冲区作为核间通信原语, 这样一来,一个核心上的任务可以进入“阻塞”状态, 以等待另一个核心发来的数据或事件。实现双核之间通信基本原理:发送和接收任务位于非对称多处理器(AMP)配置中的多核微控制器(MCU)的不同内核上,这意味着每个内核都运行自己的FreeRTOS程序。同时,一个内核在另一个内核中具有生成中断的能力,以及两个内核都有访问的内存区域(共享内存)。消息缓冲区以每个内核上运行在应用程序已知的地址置在共享内存中,如下图所示:理想情况下,还将有一个内存保护单元(MPU),以确保只能通过内核的消息缓冲区API来访问消息缓冲区,并最好将共享内存标记为不可被其他程序占用。