云计算百科
云计算领域专业知识百科平台

FreeRTOS调度器详解:任务优先级、时间片轮转与抢占式调度

调度器

1. 为什么需要调度器

1.1 单核 CPU 的限制

由于单核 CPU 的物理限制,在 FreeRTOS 中,同一时刻只能有一个任务处于 Running 状态。

从较长时间范围观察,这些任务可能都在推进,因此表现为并发;但在任意一个具体 CPU 执行时刻,仍然只能有一个任务处于 Running 状态。

多个任务都想运行

但 CPU 同一时刻只能执行一个任务

必须决定现在把 CPU 交给谁

“决定现在把 CPU 交给谁”的机制,就是调度机制。

调度器要解决的问题,就是在多个处于 Ready 状态的任务中选择一个运行,使其进入 Running 状态。


1.2 为什么不能让每个任务自己决定是否运行

假设没有统一调度器,让每个任务自己决定什么时候运行,并不可行。

因为每个任务只知道自己的业务需求,但它们不知道完整的系统状态,例如:

  • 还有哪些任务处于 Ready
  • 其他任务的优先级是多少
  • 当前正在运行哪个任务
  • 是否需要立即切换任务
  • 同优先级任务应该怎样处理
  • 是否允许当前任务继续运行

如果让任务自己决定,就会出现多个分散的决策者,而 CPU 只有一个执行权,因此必须有唯一、统一的决策机制。


1.3 调度器解决的本质问题

当多个任务都具备运行条件,而 CPU 同一时刻只能运行一个任务时,调度器负责决定 CPU 执行权归谁。

FreeRTOS 运行在单核 MCU 上,同一时刻只能有一个任务占用 CPU。当多个任务同时处于 Ready 状态时,需要调度器根据调度规则选择其中一个任务进入 Running 状态,从而完成 CPU 执行权的分配。

用更工程化的表达:

输入:
当前系统中具备运行资格的任务集合

处理:
按照 FreeRTOS 的调度规则进行选择

输出:
下一个应当处于 Running 状态的任务

暂时可以写成抽象模型:

Scheduler(Ready 任务集合)

选择一个任务

该任务获得 CPU 执行权

这里的“调度器”不是普通应用任务,而是 FreeRTOS 内核中的调度机制。

它如何被触发、如何找到任务、怎样完成任务切换,都属于后续章节。


2. 相关问题

2.1 常见错误理解

错误一:任务创建后就会同时运行

错误。

任务创建后通常先具备进入 Ready 状态的条件;真正由哪个任务占用 CPU,需要调度机制决定。


错误二:Ready 等于正在运行

错误。

Ready:
有资格运行,但正在等待 CPU

Running:
已经获得 CPU,正在执行

在单核系统中可以存在多个 Ready 任务,但通常只有一个任务处于 Running 状态。


错误三:调度器让 CPU 运行得更快

错误。

调度器不会提高 CPU 主频,也不会让一条指令执行得更快。

它解决的是:

有限 CPU 时间如何分配给多个任务

而不是:

如何提高 CPU 硬件性能


错误四:每个任务自己决定什么时候运行

错误。

任务可以通过延时、等待事件等方式改变自己的状态,但 CPU 最终执行哪个 Ready 任务,由内核调度规则决定。


错误五:调度器就是一个 while(1) 任务

这种理解不准确。

调度器不是你创建的普通业务任务,例如:

void Scheduler_Task(void *pvParameters);

它属于 FreeRTOS 内核机制。

具体如何进入调度流程、如何选择任务、如何借助 PendSV 完成切换,将在后续章节中逐步学习。


2.2 面试基础表达

FreeRTOS 为什么需要调度器?

STM32F103 是单核 MCU,同一时刻只能执行一个任务,但系统中可能同时存在多个 Ready 任务。

任务状态只能说明任务当前是否具备运行条件,不能解决多个 Ready 任务之间由谁获得 CPU 的问题。

因此 FreeRTOS 需要调度器,根据既定调度规则从 Ready 任务中选出一个,使其进入 Running 状态并获得 CPU 执行权。

调度器实现的是多任务之间的 CPU 时间分配,并不会让单核 CPU 真正并行执行多个任务。


3. 调度器的职责

管理 CPU 执行权,并在多个任务之间进行分配。

3.1 判断任务是否具备运行资格

这里不是选择谁,而是筛选候选者,即筛选处于 Ready 状态的任务。

任务状态负责提供信息,调度器利用这些信息。


3.2 根据调度规则选择任务

在 Ready 状态的任务中进行选择,此时涉及:

  • 优先级
  • 时间片
  • 抢占规则

调度器负责做选择,但选择依据由调度策略决定。


3.3 维护当前运行任务

系统必须知道:

当前 CPU 正在执行谁,也就是当前处于 Running 状态的任务是谁。

当任务被切换出去,之后再切换回来时,还必须知道之前运行到了哪里。

因此 FreeRTOS 内部需要保存当前任务相关信息。

这里先建立概念:

调度器:
管理任务执行关系

TCB:
保存任务相关信息


4. 任务与调度器的关系

可以建立以下模型:

应用层
——————————–
ADC 任务
CAN 任务
OLED 任务
Fault 任务

FreeRTOS 内核
——————————–
任务状态管理
TCB 管理
调度器

CPU 硬件
——————————–
Cortex-M3

关系:

任务:我要完成什么工作。

调度器:CPU 现在给谁。

CPU:执行被选择任务的指令。


4.1 FreeRTOS 调度器的作用是什么?

FreeRTOS 运行在单核 MCU 上,同一时刻只能有一个任务执行。

系统中可能同时存在多个 Ready 任务,因此需要调度器管理 CPU 执行权。

调度器根据任务状态和调度规则,从具备运行条件的任务中选择一个任务进入 Running 状态。

调度器本身不负责执行任务业务逻辑,只负责任务之间 CPU 资源的分配。


5. 调度器触发

5.1 调度器不是持续运行

调度器只在需要重新选择任务时参与调度判断。

调度器不是持续运行,而是在需要重新分配 CPU 执行权时进入调度流程。


5.2 重新调度条件

常见情况包括:

  • 当前任务主动放弃 CPU
  • 当前任务进入阻塞状态,让其他任务获得 CPU
  • 新的任务进入 Ready,需要重新判断继续当前任务还是运行新任务
  • 系统周期节拍到来
  • 例如:

    系统 Tick 到来

    FreeRTOS 时间推进

    检查延时任务是否到期


    5.3 调度触发总结

    任务状态变化

    ┌────────────┼────────────┐
    │ │ │
    主动延时 等待事件 时间到期
    │ │ │
    ↓ ↓ ↓

    调度机制触发

    重新判断 CPU 给谁


    5.4 FreeRTOS 什么时候触发调度?

    FreeRTOS 不会一直运行调度器,而是在系统状态发生变化、需要重新分配 CPU 执行权时进入调度流程。

    例如:

    • 当前任务主动调用延时或等待接口进入 Blocked
    • 其他任务由 Blocked 变为 Ready
    • 系统 Tick 导致延时任务到期恢复 Ready

    这些情况都可能触发调度。

    调度器随后根据调度规则重新选择运行任务。


    6. 任务调度机制

    6.1 任务优先级

    在嵌入式系统中,不同任务的重要程度和实时性要求不同。

    例如 BMS 中,故障检测任务需要比显示任务更及时执行。

    如果所有任务平等竞争 CPU,就无法保证关键任务及时响应。

    因此 FreeRTOS 为任务提供优先级属性,使调度器在多个任务同时具备运行条件时,可以根据任务优先级进行选择。


    6.2 为什么需要优先级

    当多个任务进入 Ready 后,调度器必须选择一个任务运行。

    因此需要通过任务优先级告诉调度器:

    多个任务之间的调度优先关系。

    任务优先级需要合理规划。

    对系统实时性和安全性影响更大的任务,通常需要更高的调度优先级。

    优先级的本质可以理解为:

    给任务增加一个调度优先级标签,让调度器能够区分多个任务。


    6.3 优先级数字规则

    FreeRTOS 中:

    数字越大,优先级越高。

    优先级数量由 configMAX_PRIORITIES 决定。

    例如:

    #define configMAX_PRIORITIES 5

    表示系统共有 5 个优先级:

    0
    1
    2
    3
    4


    6.4 调度器如何根据优先级选择任务

    当系统需要进行任务调度时,FreeRTOS 首先从当前处于 Ready 状态的任务中进行选择。

    然后比较这些任务的优先级:

    多个 Ready 任务

    比较优先级

    选择最高优先级任务

    任务进入 Running

    获得 CPU

    如果多个任务优先级相同,则需要依靠同优先级调度机制进行处理。


    7. 同优先级调度

    7.1 时间片轮转

    FreeRTOS 对于同优先级任务,可以采用:

    时间片轮转(Time Slicing)

    即多个相同优先级的 Ready 任务轮流获得 CPU 执行时间。


    7.2 时间片和 vTaskDelay() 的区别

    时间片不是任务延时。

    vTaskDelay() 是任务主动进入阻塞,而时间片属于内核的调度机制。

    对比项vTaskDelay()时间片
    谁主动 任务 内核
    原因 等待 分配 CPU
    状态变化 Running → Blocked Running → Ready
    目的 等待一段时间 同优先级任务轮流运行

    7.3 时间片配置

    时间片相关配置:

    #define configUSE_TIME_SLICING 1

    表示开启时间片。

    #define configUSE_TIME_SLICING 0

    表示关闭时间片。


    7.4 如果两个 FreeRTOS 任务优先级相同,会如何运行?

    当多个任务处于 Ready 状态且优先级相同时,FreeRTOS 可以通过时间片轮转机制让这些任务共享 CPU 执行时间。

    每个任务运行一个时间片后,调度器可以切换到同优先级的其他任务。


    7.5 不建议大量任务设置同一个优先级

    1. 实时性难保证

    2. 系统行为难预测

    3. 失去优先级设计意义


    8. 抢占式调度

    8.1 抢占基本概念

    抢占是指:

    一个正在运行的任务,可以被一个更高优先级、已经处于 Ready 状态的任务夺走 CPU 执行权。

    抢占主要解决:

    高优先级任务及时响应的问题。


    8.2 抢占开关

    开启抢占:

    #define configUSE_PREEMPTION 1

    关闭抢占:

    #define configUSE_PREEMPTION 0


    8.3 抢占式调度

    抢占式调度是指:

    当一个高优先级任务进入 Ready 状态,而当前运行任务优先级较低时,调度器可以暂停当前任务,将 CPU 执行权交给高优先级任务。

    这样可以保证关键任务更加及时地响应,提高系统实时性。


    9. 发生抢占的核心条件

    条件 1:存在更高优先级的 Ready 任务。

    条件 2:系统进入调度判断点。

    9.1 调度判断点

    FreeRTOS 需要有机会判断当前是否应该切换任务。

    常见来源包括:

    Tick

    任务主动阻塞

    ISR 唤醒任务

    手动请求调度


    9.2 常见抢占情况

    情况 1:
    系统 Tick 触发调度判断

    情况 2:
    当前任务主动进入 Blocked

    情况 3:
    中断唤醒高优先级任务

    情况 4:
    任务主动请求调度


    9.3 抢占完整流程

    高优先级任务进入 Ready

    发生调度触发事件

    FreeRTOS 检查:

    是否存在更高优先级 Ready 任务?



    触发任务切换

    高优先级任务 Running


    9.4 高优先级任务不一定立即运行的情况

    可能包括:

    • 没有进入调度判断
    • 抢占被关闭
    • 处于临界区
    • 受到中断上下文限制

    例如:

    taskENTER_CRITICAL();

    进入临界区后,会对调度和中断产生限制。


    9.5 FreeRTOS 什么时候发生抢占?

    当一个高优先级任务进入 Ready 状态,并且系统进入调度判断流程时,FreeRTOS 会检查该任务的优先级。

    如果该任务优先级高于当前运行任务,并且满足抢占条件,那么当前任务会被抢占,高优先级任务获得 CPU 执行权。

    常见触发来源包括:

    • 系统 Tick
    • ISR 唤醒任务
    • 当前任务主动阻塞
    • 主动请求任务切换
    赞(0)
    未经允许不得转载:网硕互联帮助中心 » FreeRTOS调度器详解:任务优先级、时间片轮转与抢占式调度
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!