做嵌入式开发,选对 RTOS 等于成功一半。FreeRTOS 是装机量最大的老牌 RTOS,Zephyr 是 Linux 基金会力推的新一代可扩展 RTOS。两者架构哲学完全不同:FreeRTOS 极简内核 + 手动移植,Zephyr 微内核 + 设备树 + Kconfig 像 Linux 一样构建。
一、为什么需要 RTOS
裸机(Super Loop)开发在简单场景够用:
while (1) { read_sensor(); process(); send_data(); delay(100); }但遇到以下情况就会翻车:
多个任务需要"同时"响应(按键 + 通信 + 控制)
实时性要求(中断必须在 1ms 内处理)
复杂状态机(协议栈、文件系统等)
RTOS 提供任务调度、通信原语(队列/信号量)、定时器,让多任务并发变得可控。
二、FreeRTOS 架构
FreeRTOS 是极简抢占式内核,核心代码只有几个 C 文件:
FreeRTOS/ 核心 ├── tasks.c 任务管理、调度器 ├── queue.c 队列、信号量、互斥量 ├── list.c 就绪列表等数据结构 ├── timers.c 软件定时器 ├── heap_x.c 5 种内存管理方案可选 └── port.c 与 MCU 架构相关的移植层任务调度:优先级抢占 + 时间片轮转
任务数:理论无上限(受内存约束)
内存:任务栈由用户分配,内核对象从 heap 分配
移植:每个 MCU 架构写一个
port.c(汇编上下文切换)
FreeRTOS 的哲学是"最小侵入"——你只拿你需要的内核,其余自己搭。
三、Zephyr 架构
Zephyr 是微内核 + 统一构建系统,借鉴了 Linux 的设计:
Zephyr/ ├── kernel/ 微内核:调度、线程、同步 ├── drivers/ 统一驱动模型(按设备树实例化) ├── boards/ 板级支持包(DTS 描述) ├── include/ API └── samples/ 示例构建系统:CMake + Kconfig + 设备树(DTS) ├── prj.conf Kconfig 开关(像 make menuconfig) ├── *.overlay 设备树覆盖(追加硬件描述) └── CMakeLists.txt 编译定义任务(Zephyr 叫 thread)调度:同样优先级抢占 + 时间片
驱动模型:所有外设通过设备树描述,运行时用
device_get_binding()获取可裁剪:Kconfig 逐项开关,最终镜像只包含用到的代码
跨平台:一份应用代码,改设备树即可换 MCU(ARM/RISC-V/Xtensa)
Zephyr 的哲学是"像 Linux 一样标准化"——统一的设备模型和构建体验。
四、五维深度对比
4.1 内核架构
| 维度 | FreeRTOS | Zephyr |
|---|---|---|
| 内核形态 | 单体内核,手动裁剪 | 微内核 + 构建期链接 |
| 代码量 | ~1 万行核心 | 内核小,但生态大 |
| 任务概念 | Task | Thread(更丰富状态) |
| 构建系统 | 无,自己接 Make/CMake | 统一 CMake + Kconfig + DTS |
| 硬件描述 | 代码里写死 | 设备树(dts)声明式 |
4.2 调度策略
两者都支持优先级抢占 + 时间片轮转。Zephyr 额外支持:
协作式线程(cooperative,最高优先级,不可被抢占)
元数据调度(META-IRQ 线程,中断下半部)
// Zephyr:协作线程优先级用负数 K_THREAD_DEFINE(my_coop, STACK, thread_fn, NULL, NULL, NULL, K_PRIO_COOP(1), 0, 0); // 协作,最高优先级 K_THREAD_DEFINE(my_preem, STACK, thread_fn, NULL, NULL, NULL, K_PRIO_PREEMPT(5), 0, 0); // 抢占,优先级 54.3 内存占用(STM32F407,最小配置实测)
| 指标 | FreeRTOS | Zephyr |
|---|---|---|
| ROM (Flash) | ~6 KB(仅内核) | ~12 KB(含最小驱动) |
| RAM | ~2 KB(含调度结构) | ~4 KB |
| 最小栈 | 用户自定 | 默认 512-1024 B |
Zephyr 略大,但换来统一驱动和构建系统。对资源极度紧张(<32KB Flash)的 MCU,FreeRTOS 仍更友好。
4.4 驱动模型
这是两者最大的差别:
FreeRTOS 驱动: // 直接操作寄存器,自己写 GPIO 驱动 HAL_GPIO_WritePin(GPIOA, PIN_5, 1); Zephyr 驱动: // 设备树声明 → 运行时绑定 const struct device *led = device_get_binding("led0"); gpio_pin_set(led, PIN, 1);Zephyr 的驱动是"一次编写,多板复用";FreeRTOS 通常每个项目自己适配外设。
4.5 生态与许可
| 维度 | FreeRTOS | Zephyr |
|---|---|---|
| 许可 | MIT(AWS 维护) | Apache-2.0 |
| 厂商支持 | 几乎所有 MCU 厂商 | 主流厂商 + Linux 基金会 |
| 组件库 | 较少(社区贡献) | 丰富(USB/BLE/FS/Net 官方维护) |
| 学习曲线 | 平缓 | 较陡(要学 DTS/Kconfig) |
| LTS | AWS 提供长期支持版 | 每半年发版 + LTS |
五、特性矩阵
六、实战:STM32 上双 RTOS 跑 LED
6.1 FreeRTOS(基于 HAL + CubeMX 生成骨架)
#include "FreeRTOS.h" #include "task.h" void led_task(void *arg) { while (1) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); vTaskDelay(pdMS_TO_TICKS(500)); // 阻塞延时,释放 CPU } } int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); xTaskCreate(led_task, "led", 128, NULL, 2, NULL); vTaskStartScheduler(); // 启动调度器 while (1); }6.2 Zephyr(设备树 + 应用代码分离)
app.overlay(板级设备树追加): / { leds { compatible = "gpio-leds"; led0: led_0 { gpios = <&gpioa 5 GPIO_ACTIVE_HIGH>; }; }; }; prj.conf: CONFIG_GPIO=y CONFIG_MAIN_STACK_SIZE=1024 src/main.c: #include <zephyr/kernel.h> #include <zephyr/drivers/gpio.h> #define LED0_NODE DT_ALIAS(led0) void main(void) { const struct device *led = DEVICE_DT_GET(LED0_NODE); gpio_pin_configure(led, DT_GPIO_PIN(LED0_NODE, gpios), GPIO_OUTPUT_ACTIVE); while (1) { gpio_pin_toggle(led, DT_GPIO_PIN(LED0_NODE, gpios)); k_msleep(500); // 阻塞延时 } }Zephyr 把"硬件描述"从代码里抽出来放进设备树,应用代码只关心逻辑——换板子改 overlay 即可,不动 C 代码。
七、选型决策树
八、适用场景总结
九、总结
FreeRTOS 和 Zephyr 不是"谁更好",而是"谁更合适":
FreeRTOS:极简、灵活、生态无敌,适合资源紧、快速交付、已有积累
Zephyr:标准化、驱动丰富、跨平台,适合商业产品、网络化、长期维护
新人从 FreeRTOS 入门,产品从 Zephyr 起手,是多数团队的最优解。
下一篇预告:选好 RTOS 后怎么连云?下期我们回到「Edge AI 全栈实战 ②」,用 FreeRTOS/ESP32 把传感器数据经 MQTT 推给 Flink 做实时流处理。
往期回顾:
ESP32 开发环境搭建指南:从零配置到编译烧录的 12 个坑
树莓派 5 + TFLite 边缘部署实战:YOLOv8 目标检测跑出 30fps 的完整方案
- Edge AI 全栈1:ESP32 传感器采集到云端大模型推理的完整链路