RTOS(实时操作系统)的调度策略是决定哪个任务在何时获得CPU使用权的核心机制。理解这些策略,是进行嵌入式多任务开发的基础。
从裸机(前后台架构)开发过渡到RTOS开发,对于很多经验不足的工程师可能是一项艰难作。
但使用RTOS有许多优势,例如简化应用集成,支持任务抢占调度,当开发人员使用复杂的32位微控制器,且可以获取足够的Flash和RAM空间时,使用RTOS开始变得有意义。
本文给大家分享一下RTOS的几常见调度策略。
1、抢占式调度
抢占式调度是RTOS中最核心、最常见的策略,确保了系统的实时性和响应能力。
核心思想:每个任务都有一个固定的优先级。调度器会始终运行当前处于就绪态且优先级最高的任务。一旦一个更高优先级的任务变为就绪态(例如,它等待的中断发生了),它就会立即抢占当前正在运行的低优先级任务的CPU使用权。
大概流程:

比如:FreeRTOS通过配置和任务创建实现
cpp
// 1. 在 FreeRTOSConfig.h 中确保抢占式调度开启
#define configUSE_PREEMPTION 1 // 1: 开启抢占式调度
// 2. 创建两个不同优先级的任务
void vHighPriorityTask(void *pvParameters)
{
for (;;)
{
// 执行关键操作
vTaskDelay(pdMS_TO_TICKS(100)); // 模拟任务阻塞
}
}
void vLowPriorityTask(void *pvParameters)
{
for (;;)
{
// 执行非关键操作
}
}
void main()
{
// 创建高优先级任务 (优先级为 2)
xTaskCreate(vHighPriorityTask, "High", 256, NULL, 2, NULL);
// 创建低优先级任务 (优先级为 1)
xTaskCreate(vLowPriorityTask, "Low", 256, NULL, 1, NULL);
vTaskStartScheduler(); // 启动调度器
}
FreeRTOS中每个任务的时间片长度是固定的,为一个时间节拍;而μC/OS-III中每个任务的时间片长度可变,可在任务创建时指定。
2、时间片轮询调度
这种策略主要用于处理多个相同优先级的任务,保证它们都能得到执行机会.
前后台系统实现通常基于轮询调度技术,它是一种很自然的软件编写方式,只需向超级循环中添加新代码,相当于增加了一个新的任务。
RTOS中的轮询调度策略,是允许多个任务可以分配同一个优先级别。调度程序基于时钟监控任务时间,任务处于相关优先级,按照先进先出的原则执行分配到的时间片,时间到了,即使当前任务还没有完成,任务也将CPU时间传递给下一个任务。在下一个分配到的时间段内,该任务将从它停止的位置继续执行。

常用的RTOS,如μC/OS-III(Cs/OS3)、FreeRTOS都支持时间片轮询算法。FreeRTOS中每个任务的时间片长度是固定的,为一个时间节拍;而μC/OS-III中每个任务的时间片长度可变,可在任务创建时指定。
比如:FreeRTOS通过配置和任务创建实现
cpp
// 1. 在 FreeRTOSConfig.h 中确保时间片轮转开启
#define configUSE_TIME_SLICING 1 // 1: 开启时间片轮转
#define configTICK_RATE_HZ 1000 // 时钟节拍频率 1000Hz,即时间片为 1ms
// 2. 创建两个相同优先级的任务
void vTaskA(void *pvParameters)
{
for (;;)
{
// 执行任务A的工作
}
}
void vTaskB(void *pvParameters)
{
for (;;)
{
// 执行任务B的工作
}
}
void main() {
// 创建两个优先级相同的任务 (假设优先级为 1)
xTaskCreate(vTaskA, "TaskA", 256, NULL, 1, NULL);
xTaskCreate(vTaskB, "TaskB", 256, NULL, 1, NULL);
vTaskStartScheduler(); // 启动调度器
}
3、基于优先级的协同调度
这是一种非抢占式的调度策略,所有任务"商量着来"。
核心思想:一个任务会一直运行,直到它主动释放CPU。释放的方式通常是调用特定的让出函数(如taskYIELD()),或者使自己进入阻塞态(如等待信号量或调用vTaskDelay())
在RTOS中,协同调度是基于优先级的非抢占调度方法。任务按优先级排序,并且是事件驱动类型的,一旦正在运行的任务完成,或者任务主动调用OS服务放弃CPU,就绪运行的优先级最高的任务才可以获得CPU使用权。

比如:
cpp
// 1. 在 FreeRTOSConfig.h 中关闭抢占式调度
#define configUSE_PREEMPTION 0 // 0: 关闭抢占式调度,即为协作式[reference:31][reference:32]
void vCooperativeTask(void *pvParameters)
{
for (;;)
{
// 执行任务工作...
// 主动让出CPU,让其他同优先级或低优先级任务运行
taskYIELD();
}
}
4、总结
RTOS对于嵌入式软件开发人员来说是一个强大的工具。RTOS应用中,每个任务都有单独的任务控制块,其中包含堆栈、优先级和ID等参数。每个任务都可以被看作是单独的应用程序。RTOS还提供了各种各样的同步和通信工具,如信号量、互斥信号量和消息队列,简化应用的开发。
但任务切换将消耗CPU时间,即使在32位处理器上,RTOS也会造成时间损失。任务切换比中断占用更多的时间。如果是一个很小的应用程序,但需要充分利用处理器资源,在这种情况下,需仔细考虑RTOS的可行性。
| 策略 | 实时性 | 公平性 | 复杂度 | 典型应用场景 |
|---|---|---|---|---|
| 抢占式调度 | 高 | 低(低优先级可能饥饿) | 中 | 硬实时 任务,如电机控制、紧急中断响应 |
| 时间片轮转 | 中 | 高 | 低 | 同优先级任务的公平调度,如多通道数据采集 |
| 协作式调度 | 低 | 低 | 低 | 低功耗 设备、任务间耦合紧密的场景 |
每种调度算法都有其优缺点,了解RTOS常用的调度算法,可以帮助我们做出合适的选择。