--- tags: [source-summary] type: source source: "尚硅谷嵌入式技术之FreeRTOS实时操作系统 V1.0.3 — FreeRTOS基础知识" author: "尚硅谷研究院" date: 2026-07-17 created: 2026-07-17 --- # 任务调度与状态管理 > **用生活理解**:CPU 像一个只能同时接待一位顾客的收银员。多任务系统让收银员在多个顾客之间快速切换,让每个顾客都觉得自己被"同时"服务着。调度器就是排号系统——谁优先级高谁先结账,同优先级按顺序来。 ## 多任务处理 ### 基本概念 **内核**是操作系统的核心组件。多任务操作系统通过任务之间的快速切换,在单核处理器上制造出"并发执行"的假象。 实际上,单核 CPU 在同一时刻只能运行一个任务。任务切换的速度非常快(毫秒级),让人感觉多个任务在同时运行。 ![多任务逻辑并发 vs 实际执行](./assets/multitasking_concurrent_vs_actual.png) 多任务系统的好处: - 将复杂应用拆分为一组更小、更易管理的任务 - 便于团队分工和代码复用 - 时序和排序细节从应用代码中剥离,由 OS 负责 ## 任务调度 ### 工作原理 **调度器**是内核中负责决定"下一时刻运行哪个任务"的组件。**调度策略**是决定算法。 FreeRTOS 默认使用**固定优先级的抢占式调度策略**,对同等优先级任务执行**时间片轮询**。 ### 调度模式 | 模式 | 说明 | 适用场景 | | -------------- | ---------------------------------------------- | ---------------------------------- | | **抢占式调度** | 高优先级任务随时可以抢占正在执行的低优先级任务 | 需要确保高优先级任务实时响应的场景 | | **时间片轮询** | 同优先级任务轮流执行固定长度的时间片 | 多任务需要公平分配 CPU 的场景 | ![抢占式调度](./assets/scheduling_preemption.png) 高优先级任务在等待资源(延时、信号量等)时会主动让出 CPU,调度器选择其他就绪的高优先级任务执行,不会让低优先级任务"饿死"。 ![时间片轮询调度](./assets/scheduling_timeslice.png) ### 协程式调度 除了抢占式调度,FreeRTOS 也支持协程式调度(configUSE_PREEMPTION = 0 时启用)。在协程式调度下,任务不会被强制打断,只有当任务主动调用 `taskYIELD()` 或阻塞等待时,调度器才会切换任务。协程式调度实时性较弱,一般不推荐使用。 ## 任务状态 ### 四种任务状态 FreeRTOS 中任务共存在 4 种状态: | 状态 | 含义 | 进入方式 | 退出方式 | | ----------------------- | ------------------ | ----------------------------- | ------------------ | | **运行态**(Running) | 任务正在执行 | 调度器选中 | 被抢占、阻塞、挂起 | | **就绪态**(Ready) | 任务可执行但未运行 | 创建、解除阻塞、解挂 | 被调度器选中 | | **阻塞态**(Blocked) | 等待延时或外部事件 | vTaskDelay、等待队列/信号量等 | 延时到、事件发生 | | **挂起态**(Suspended) | 类似暂停 | vTaskSuspend() | vTaskResume() 解挂 | ### 状态转换 只有就绪态可以转变为运行态。其他状态的任务想要运行,必须先转变为就绪态。 ![任务状态转换图](./assets/task_state_diagram.png) ## 滴答计数器 ### 工作原理 FreeRTOS 实时内核通过**滴答计数变量**(Tick Count)测量时间。SysTick 定时器中断以固定频率递增滴答数,每次中断称为一个 **RTOS 滴答**。 每次滴答中断时,内核检查是否有任务需要解除阻塞或唤醒。如果需要,就在 ISR 中进行任务切换。 `configTICK_RATE_HZ` 配置项定义每秒的滴答数(通常设为 1000,即 1ms 一次滴答)。 ## 上下文切换 ### 基本概念 **上下文**是任务执行时使用的所有处理器资源(寄存器、堆栈等)的集合。 **上下文切换**就是:暂停当前任务时保存其上下文到栈中,切换到下一个任务时从栈中恢复其上下文。这样任务恢复执行时,"以为自己从未被打断过"。 ![上下文切换——保存](./assets/context_switch_save.png) ![上下文切换——恢复](./assets/context_switch_restore.png) ### 上下文切换的三种触发时机 1. **RTOS 滴答中断**:处理就绪列表,判断是否抢占或时间片轮转 2. **任务执行完毕**:任务函数返回时自动切换 3. **主动让出**:调用 `taskYIELD()` 主动触发 ### PendSV 机制 实际的上下文切换在 **PendSV** 中断服务函数中完成。PendSV 被设置为**最低中断优先级**,这样上下文切换不会影响其他关键 ISR。 触发方式:将 ICSR 寄存器的 bit28(PendSV 挂起位)置 1。 ## 空闲任务 ### 基本概念 调度器启动时**自动创建空闲任务**,以最低优先级创建,确保至少有一个任务可运行。 空闲任务的三个作用: | 作用 | 说明 | | ---------------------- | --------------------------------------------------- | | **释放被删任务的内存** | 任务调用 vTaskDelete 后,其资源在空闲任务中统一回收 | | **CPU 空闲计数** | 统计 CPU 空闲率,辅助性能分析 | | **低功耗模式** | 配合 tickless 模式,在空闲时进入低功耗状态 | ## 就绪列表 ### 就绪列表结构 就绪列表是 `pxReadyTasksLists[x]` 数组,其中 x 代表任务优先级(0 ~ configMAX_PRIORITIES-1)。 在 32 位硬件上,一个 32 位的位图变量标记哪些优先级对应的就绪列表中有任务: ![就绪列表位图](./assets/ready_list_bitmap.png) 多个相同优先级的任务连接在同一个就绪列表上: ![同优先级就绪列表](./assets/ready_list_same_priority.png) 调度器始终在所有就绪列表中选择**最高优先级**的任务来执行。 ## 核心函数速查表 | 函数 | 参数 | 返回值 | 功能简述 | | ----------------------- | ------------ | ------ | ---------------- | | `vTaskStartScheduler()` | void | void | 启动任务调度器 | | `vTaskSuspend()` | TaskHandle_t | void | 挂起指定任务 | | `vTaskResume()` | TaskHandle_t | void | 恢复被挂起的任务 | | `taskYIELD()` | void | void | 主动请求任务切换 | ## 常见问题与避坑 - **避免死循环占用 CPU**:任务循环中必须包含阻塞操作(vTaskDelay、等待队列等),否则同优先级或低优先级任务无法运行。 - **优先级反转**:高优先级任务等待低优先级任务持有的资源时,可通过互斥信号量的优先级继承机制缓解。 - **不要中断中调用阻塞 API**:ISR 中调用会阻塞的 FreeRTOS API 可能导致系统崩溃,应使用带 `FromISR` 后缀的函数。