851 字
4 分钟
嵌入式 RTOS 内核分析:FreeRTOS 的任务调度与内存管理
嵌入式 RTOS 内核分析:FreeRTOS 核心机制
什么时候需要 RTOS
很多嵌入式项目从裸机(bare metal)开始。当你的代码中出现这种结构时,就该认真考虑 RTOS 了:
// 裸机时代的"伪多任务"——痛苦的开始while (1) { if (flag_10ms) { do_sensor_read(); flag_10ms = 0; } if (flag_100ms) { do_comm_protocol(); flag_100ms = 0; } if (uart_data_ready) { process_uart(); } // 某个任务运行时间太长,其他任务全部延迟...}RTOS 解决的核心问题:在资源受限的 MCU 上,让多个不同优先级的任务看起来像是在同时运行。
FreeRTOS 调度器核心
Tick 中断
整个调度器的心跳来自 SysTick 定时器中断。默认情况下每 1ms 触发一次(configTICK_RATE_HZ = 1000):
void xPortSysTickHandler(void) { portDISABLE_INTERRUPTS();
// 更新系统 tick 计数 xTaskIncrementTick();
// 如果需要任务切换 if (xTaskIncrementTick() != pdFALSE) { // 触发 PendSV——ARM Cortex-M 的上下文切换机制 portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; }
portENABLE_INTERRUPTS();}PendSV:ARM 架构的优雅设计
为什么不在 SysTick ISR 中直接切换?因为 ARM 的中断优先级机制:PendSV 被配置为最低优先级,确保它只在所有高优先级 ISR 处理完毕后执行,避免在中断处理中途切换任务。
// PendSV handler——实际的任务上下文切换__attribute__((naked)) void xPortPendSVHandler(void) { __asm volatile ( "mrs r0, psp\n" // 获取当前任务栈指针 "stmdb r0!, {r4-r11}\n" // 保存 callee-saved 寄存器 "str r0, [%[pxCurrentTCB]]\n" // 保存任务栈顶 // ... 选择下一个任务 ... "ldr r0, [%[pxCurrentTCB]]\n" "ldmia r0!, {r4-r11}\n" // 恢复新任务的寄存器 "msr psp, r0\n" "bx lr\n" :: [pxCurrentTCB] "r" (&pxCurrentTCB) );}队列:FreeRTOS 最核心的 IPC 机制
队列是 FreeRTOS 内部实现最精妙的数据结构之一:
- 环形缓冲区实现,无动态内存分配
- 支持多任务同时等待(读/写),通过链表管理等待任务
- 带超时的阻塞机制——这是实时性的关键保证
关键数据结构:
typedef struct QueueDefinition { int8_t *pcHead; // 队列存储区起始 int8_t *pcWriteTo; // 下一个写入位置 int8_t *pcReadFrom; // 下一个读取位置
List_t xTasksWaitingToSend; // 等待发送的任务链表 List_t xTasksWaitingToReceive; // 等待接收的任务链表
volatile UBaseType_t uxMessagesWaiting; // 当前消息数 UBaseType_t uxLength; // 队列容量 UBaseType_t uxItemSize; // 每个元素的大小} Queue_t;内存管理策略对比
FreeRTOS 提供了 5 种内存分配方案(heap_1.c 至 heap_5.c):
| 方案 | 特点 | 适用场景 |
|---|---|---|
| heap_1 | 仅分配不释放,无碎片 | 任务数固定的简单系统 |
| heap_2 | 支持释放,使用 best-fit | 需谨慎,有碎片风险 |
| heap_3 | 封装标准 malloc/free | 需要线程安全的 malloc |
| heap_4 | 支持相邻空闲块合并 | 推荐:大多数应用的默认选择 |
| heap_5 | heap_4 + 多堆区 | 多块非连续 RAM 的 SoC |
优先级反转与互斥信号量
这是 RTOS 实战中必踩的坑:
高优先级 Task A 等待信号量 → 被中优先级 Task B 抢占 →低优先级 Task C 持有信号量但无法执行 → 死锁!FreeRTOS 的互斥信号量内置优先级继承机制:当高优任务等待时,持有信号量的低优任务自动继承高优先级,确保它能尽快释放信号量。
裸机到 RTOS 的转变不只是加个调度器,而是思维方式的转变:从”我控制一切流程”到”我设计任务的协作规则”。这个转变比代码更难。
嵌入式 RTOS 内核分析:FreeRTOS 的任务调度与内存管理
https://mizuki.mysqil.com/posts/freertos-kernel-analysis/