STM32 进阶封神之路(三十九) FreeRTOS 临界区、挂起 / 删除、钩子函数、调度底层原理|从应用到内核深度解析

0 阅读5分钟

STM32 进阶封神之路(三十九)

FreeRTOS 临界区、挂起 / 删除、钩子函数、调度底层原理|从应用到内核深度解析

前言

在上一篇中,我们系统讲解了 FreeRTOS 基础架构、任务创建、优先级抢占、延时机制等核心入门内容。

本篇继续深入 FreeRTOS 中层与内核级知识点,包括:临界区、任务挂起与删除、三大钩子函数、空闲任务、系统调度底层、Systick 时钟、可剥夺内核完整运行流程。

内容更贴近工程实践、更接近内核本质,适合希望真正掌握 FreeRTOS 运行机制、能够独立排查系统问题的开发者。


一、临界区(Critical Section)彻底解析

1.1 什么是临界区?

临界区是指:一段执行过程中不希望被任何任务或中断打断的代码段

典型场景:

  • printf 打印输出
  • SPI、IIC、DHT11 等时序操作
  • W25Q64 读写操作
  • 结构体数据赋值、多变量同步更新

1.2 临界区标准 API

c

运行

taskENTER_CRITICAL();   // 进入临界区
taskEXIT_CRITICAL();    // 退出临界区

使用规则:

  • 必须成对使用
  • 执行时间越短越好
  • 不能在临界区内调用 vTaskDelay 等阻塞函数

1.3 临界区实验现象

无临界区保护:

  • 两个任务同时 printf
  • 输出内容乱码、拼接、错位

有临界区保护:

  • 每个 printf 完整输出
  • 不会被其他任务打断
  • 线程安全

1.4 中断能否打断临界区?

取决于系统配置:

c

运行

configMAX_SYSCALL_INTERRUPT_PRIORITY
  • 高优先级中断可以打断
  • 低优先级中断无法打断

二、任务挂起与恢复(Suspend / Resume)

2.1 挂起任务

c

运行

vTaskSuspend(xHandle_LED1);

效果:

  • 任务暂停运行
  • 不再参与调度
  • 任务现场保持
  • 可随时恢复

2.2 恢复任务

c

运行

vTaskResume(xHandle_LED1);

效果:

  • 任务退出挂起态
  • 进入就绪态
  • 等待调度运行

2.3 挂起 vs 删除

  • 挂起:暂停执行,可恢复
  • 删除:彻底销毁,无法恢复

适用场景:

  • 暂时不需要运行的任务
  • 低功耗管理
  • 按键启停控制

三、任务删除(vTaskDelete)

c

运行

vTaskDelete(xHandle_LED1);
xHandle_LED1 = NULL;

注意事项:

  • 删除后必须清空句柄
  • 任务堆栈、TCB 内存由空闲任务回收
  • 已删除任务无法再使用

四、FreeRTOS 三大钩子函数(Hook)

钩子函数是内核提供给用户的扩展接口,用于监控系统运行状态。

4.1 堆栈溢出钩子(最重要)

c

运行

void vApplicationStackOverflowHook(...)
{
    while(1); // 死循环便于调试定位
}

作用:任务堆栈越限时触发。

4.2 空闲任务钩子

c

运行

void vApplicationIdleHook(void)
{
    // 低功耗处理、系统监测、运行统计
}

4.3 内存申请失败钩子

c

运行

void vApplicationMallocFailedHook(void)
{
    while(1);
}

五、空闲任务(Idle Task)

5.1 何时创建?

调用 vTaskStartScheduler () 时,系统自动创建。

5.2 特点

  • 优先级 0(最低)
  • 负责回收删除任务的内存
  • 支持进入低功耗模式

5.3 作用

  • 系统无任务运行时执行
  • 清理系统资源
  • 支持 Tickless 低功耗模式

六、FreeRTOS 内核调度底层原理(超深入)

6.1 任务控制块 TCB(Task Control Block)

TCB 是任务的 “身份证”,包含:

  • 栈指针
  • 优先级
  • 任务名
  • 状态
  • 各种事件链表

内核通过 TCB 管理、调度所有任务。

6.2 就绪列表(Ready List)

按优先级分组的双向链表。调度器每次从最高优先级链表取出任务运行。

6.3 任务切换核心:PendSV 中断

FreeRTOS 任务切换并非直接切换,而是:

  1. 触发 PendSV 中断
  2. 在 PendSV 中保存任务现场
  3. 切换到最高优先级任务

这是 Cortex-M 架构 RTOS 的标准实现。

6.4 Systick 系统时钟

  • 提供系统时钟节拍(Tick)
  • 默认 1ms 一次
  • 驱动延时、时间片、软件定时器

c

运行

configTICK_RATE_HZ 1000    // 1ms

七、可剥夺内核完整运行流程(图文逻辑)

  1. 启动调度器
  2. 运行最高优先级任务
  3. 任务调用 vTaskDelay → 进入阻塞
  4. 切换到下一个最高优先级任务
  5. 高优先级任务就绪 → 立即抢占低优先级
  6. 循环执行

这就是 FreeRTOS 实时性的根本保障。


八、任务编写规范(工程必备)

  • 任务必须是死循环
  • 必须包含 vTaskDelay 或阻塞调用
  • 禁止使用裸机延时
  • 共享资源必须使用临界区或互斥锁
  • 堆栈不能设置过小
  • 高优先级任务执行时间必须短
  • 禁止在临界区内延时

九、FreeRTOS 移植注意要点

  • 选择正确的 port 文件(CM3 / CM4)
  • 关闭裸机 Systick 中断
  • 正确配置系统时钟频率
  • 设置堆大小 configTOTAL_HEAP_SIZE
  • 打开所需功能宏
  • 实现堆栈溢出钩子
  • 禁止使用裸机延时函数

十、面试高频答案总结(直接背诵)

  • FreeRTOS 是可剥夺实时内核
  • 永远运行优先级最高的任务
  • 任务四种状态:就绪、运行、阻塞、挂起
  • vTaskDelay 释放 CPU,delay_ms 不释放
  • 临界区保护不希望被打断的代码
  • 空闲任务负责回收内存
  • PendSV 中断负责任务切换
  • Systick 提供系统时钟
  • 堆栈溢出钩子用于调试
  • 高优先级任务可以抢占低优先级任务

结尾

这两篇文章完整覆盖了 FreeRTOS 从入门到内核原理的核心内容,从基础概念到 API 使用,从工程规范到面试要点,全部用最详细、最通俗、最易理解的方式讲解。

能够完整看完并理解以上内容,你已经具备熟练使用 FreeRTOS 进行实际项目开发的能力,能够独立完成多任务架构设计、任务调度、系统调试与问题排查。