摘要:FreeRTOS任务调度机制分析 本文系统阐述了FreeRTOS实时操作系统的任务调度原理。首先介绍了5种任务状态及其转换规则,重点区分了阻塞态与挂起态的核心差异。其次详细说明了Tick时长配置方法,指出1ms是兼顾精度与性能的最佳平衡点。文章深入剖析了5种任务切换触发方式,包括时间片轮转、主动放弃CPU、高优先级抢占等。通过三种典型场景的时序图分析,直观展示了同优先级任务轮转、高优先级任务抢占以及任务延时的调度过程。最后描述了Tick中断处理流程,包括时间基准更新和任务调度执行的关键步骤。本文为开发者理解

目录

一、任务状态:

1.FreeRTOS 任务核心状态(5 种)

2. 阻塞态 vs 挂起态(最易混淆)

3. 就绪态 vs 运行态

4.总结:

二、一个Tick时间多长:

三、任务切换方式:

(1)Tick中断:

(2)任务执行完毕;

(3)任务调用vTaskDelay()/vTaskSuspend()主动放弃 CPU;

(4)任务等待队列 / 信号量 / 事件组等同步对象;

(5)高优先级任务被唤醒(比如中断中调用xSemaphoreGiveFromISR())。

四、几种常见情况的时间片拆解:

1.只有几个同优先级的任务切换时

2.有优先级高的任务抢占时

3.有延时的情况:

五、任务切换过程:


一、任务状态:

1.FreeRTOS 任务核心状态(5 种)

FreeRTOS 的任务状态可分为 4 种基础状态 + 1 种最终状态,每种状态有明确的进入 / 退出条件,且状态切换完全由调度器或 API 调用触发:

状态名称 核心定义 进入条件(触发方式) 退出条件(恢复方式)
运行态 当前正在 CPU 上执行的任务(单核仅 1 个) 调度器选中该任务(优先级最高的就绪任务) 1. 被更高优先级任务抢占2. 主动放弃 CPU(延时 / 等待)3. 执行完毕 / 被删除
就绪态 任务具备执行条件,但因有更高优先级任务运行,暂时未被调度 1. 任务创建完成2. 延时 / 等待事件到期3. 挂起被解除4. 中断 / 同步对象唤醒 1. 被调度器选中(转为运行态)2. 主动进入阻塞 / 挂起3. 被删除
阻塞态 任务因等待某个事件(延时、队列、信号量等)无法执行,不占用 CPU 资源 1. 调用vTaskDelay()(延时)2. 调用xQueueReceive()(等队列)3. 调用xSemaphoreTake()(等信号量) 1. 等待的事件发生(延时到期 / 收到队列数据)2. 等待超时3. 任务被删除
挂起态 任务被手动挂起,无自动退出机制,完全脱离调度器管理 调用vTaskSuspend()(挂起自身 / 其他任务) 调用vTaskResume()/vTaskResumeFromISR()(手动解除)
删除态 任务执行完毕或被删除,不再参与调度,等待空闲任务清理内存 1. 任务函数执行完毕2. 调用vTaskDelete()

2. 阻塞态 vs 挂起态(最易混淆)

维度 阻塞态 挂起态
触发方式 等待 “事件”(自动触发) 手动调用 API(强制挂起)
退出机制 事件完成 / 超时后自动退出 必须手动调用恢复 API才能退出
资源占用 占用任务控制块(TCB),但不占用 CPU 占用 TCB,完全不参与调度
典型场景 延时 100ms、等待串口数据 临时暂停某个任务(如故障处理)

3. 就绪态 vs 运行态

  • 就绪态是 “具备执行资格但没轮到”,运行态是 “正在执行”;
  • 就绪态任务会按优先级排队,调度器始终从就绪列表中选优先级最高的任务转为运行态。

4.总结:

  • FreeRTOS 任务核心状态为运行态、就绪态、阻塞态、挂起态,删除态是最终状态;
  • 状态切换的核心规则:运行态→阻塞态靠延时 / 等待同步对象,阻塞态→就绪态靠事件完成,就绪态→运行态靠调度器(优先级最高);
  • 阻塞态是 “等待事件自动恢复”,挂起态是 “手动挂起、手动恢复”,这是两者最核心的区别;
  • 单核 MCU 中运行态永远只有 1 个,就绪态任务按优先级排队等待执行

二、一个Tick时间多长:

FreeRTOS 的 Tick 时长由FreeRTOSConfig.h配置文件中的核心宏 configTICK_RATE_HZ 决定,计算公式如下:

Tick时长(毫秒)= 1000 / configTICK_RATE_HZ

configTICK_RATE_HZ 表示每秒的 Tick 中断次数(Tick 频率),开发者可以根据项目需求调整这个值:

configTICK_RATE_HZ Tick 时长 适用场景
1000 1ms 大多数常规场景(兼顾精度和开销)
500 2ms 对精度要求不高的场景
100 10ms 低功耗场景(减少 Tick 中断开销)
2000 0.5ms 高精度实时性场景(开销略高)

1ms 是行业内的 “黄金平衡点”:如果 Tick 时长太短(比如 0.1ms),Tick 中断会过于频繁,增加 CPU 负担;如果太长(比如 10ms),任务延时、时间片调度的精度会下降

三、任务切换方式:

(1)Tick中断:

Tick 中断仅检查 “时间片是否用完”:当1ms Tick 中断到来时,调度器会先判断 “当前任务是否已经用完 1ms 时间片”:

默认情况下,时间片的长度等于一个 Tick 的时长,但这不是 “绝对相等” 的铁律 —— 时间片长度以 Tick 为单位,且默认配置为 1 个 Tick,仅在特殊修改内核的场景下才会设为多个 Tick(几乎不用)。

举例:当 Tick 为 1ms,某个任务在执行到 0.2ms 时遇到 Tick 中断,调度器因它未执行满 1ms 时间片而不切换,等下一次 Tick 中断到来时,检查到这个任务总共执行了 1.2ms,不小于一个时间片(这里为1ms),则会切换任务

总结:Tick 中断仅在 “当前任务用完完整时间片 + 有同优先级就绪任务” 时,才会触发同优先级任务切换。新任务会从切换时刻开始,从0开始计时,直到累计执行时间不小于1个时间片。

(2)任务执行完毕;

(3)任务调用vTaskDelay()/vTaskSuspend()主动放弃 CPU;

(4)任务等待队列 / 信号量 / 事件组等同步对象;

(5)高优先级任务被唤醒(比如中断中调用xSemaphoreGiveFromISR())。

高优先级任务创建后会立即抢占 CPU(打断正在执行的同优先级任务),只要有更高优先级的任务处于 “就绪态”,CPU 就会执行这个高优先级任务,直到高优先级任务退出 “就绪态”(比如阻塞、挂起、结束)

四、几种常见情况的时间片拆解:

1.只有几个同优先级的任务切换时

如果为三个优先级相同的任务,从Task1,Task2,Task3依次进行时间片流转,直到再次运行Task1时任务提前结束,不到一个时间片的时间,便切换到下一个任务

2.有优先级高的任务抢占时

注:这里Task1和Task2优先级相同

图片解释:

(1)此时Task1和Task2为优先级相同的任务,Task3为优先级比Task1和Task2高的任务。

(2)在某次执行Task1时创建或者让优先级更高的Task3进入就绪态,此时立刻执行Task3任务。

(3)因为Task3为高优先级任务,当Task3任务用完完整时间片时,仍然继续执行Task3

(4)直到Task3退出 “就绪态”(比如阻塞、挂起、结束),切换任务,因为Task1执行过了,所以从Task2开始执行。

(5)Task2执行遇到第一个Tick中断时,Task任务并未执行满一个时间片,不切换任务,到第二个Tick中断到来时,当前任务用完完整时间片,且有同优先级就绪任务,所以切换到Task1

3.有延时的情况:

注:这里Task1和Task2优先级相同

(1)Task2在执行过程中,被vTaskDelay()/vTaskDelayUntil()函数延时两个Tick,Task2被放在DelayedTaskList链表,并从新任务调度切换为下一个任务,这里只有Task1

(2)经过两次Tick中断时,Task2回到就绪态,从新任务调度,此时只剩下Task2

五、任务切换过程:

Tick中断函数处理流程:

a. 时间基准:计数器cnt++;

b. 任务调度执行:

  1. 扫描DelayedTaskList链表,将满足恢复条件的任务移回ReadyList;
  2. 按优先级从高到低(n从大到小)遍历ReadyList[n]:
    • 定位首个非空任务链表
    • 将pxCurrentTCB指向该链表的下个任务
    • 启动该任务执行

以上图片均为本人结合网上资源进行绘画,如有错误欢迎大家批评指正!

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐