初识 Zephyr RTOS:从源码结构到设计哲学
一、Zephyr RTOS 是什么?
Zephyr 是 Linux 基金会旗下的开源实时操作系统(RTOS),2016 年由 Intel、NXP、Nordic 等厂商联合发起,目前已成为嵌入式领域最具活力的 RTOS 项目之一。
核心特征
多架构支持。 Zephyr 支持 ARM (Cortex-M/R/A)、x86、RISC-V、ARC、Xtensa、MIPS、SPARC 等十余种 CPU 架构,覆盖从资源受限的 MCU(如 Cortex-M0+,~8KB RAM)到带 MMU 的 MPU(如 Cortex-A)的广泛硬件谱系。
完整的平台体系。 Zephyr 不止是一个内核调度器。它内置了驱动模型、设备树(Devicetree)、Kconfig 配置系统、构建系统(CMake + west)、网络协议栈、蓝牙协议栈、文件系统、Shell、日志子系统、USB 协议栈、电源管理等一整套平台级组件。这是它和 FreeRTOS 等纯内核 RTOS 最本质的区别——后文会展开讨论。
模块化与可配置。 通过 Kconfig 菜单系统,你可以在编译前精细裁剪内核和子系统——不用的模块不参与编译,零代码体积开销。
上游优先。 Zephyr 强调板级代码和驱动尽量合入上游主线,减少厂商 fork,避免生态碎片化。
开源治理。 采用 Apache 2.0 许可证,由 Linux 基金会技术指导委员会管理,Intel、NXP、Nordic、ST、TI 等均为白金/银牌成员。
二、Zephyr 源码目录结构
下面是 Zephyr 主仓库的顶层目录结构和各目录的职责说明:
zephyr/
├── arch/ # 架构相关代码
├── boards/ # 板级支持
├── cmake/ # CMake 构建脚本
├── doc/ # 官方文档(RST/Sphinx)
├── drivers/ # 设备驱动
├── dts/ # 设备树源文件和绑定
├── include/ # 公共头文件
├── kernel/ # 内核核心实现
├── lib/ # 通用库
├── modules/ # 外部模块(HAL、MCUboot 等)
├── samples/ # 示例程序
├── scripts/ # 工具脚本
├── share/ # 共享资源
├── soc/ # SoC 级定义和初始化
├── subsys/ # 子系统
├── tests/ # 测试用例
├── CMakeLists.txt # 顶层 CMake
├── Kconfig # 顶层 Kconfig
└── west.yml # 多仓库管理清单
逐目录说明
arch/ —— 架构层
包含不同 CPU 架构的底层实现:
| 子目录 | 说明 |
|---|---|
arch/arm/core/ |
ARM 架构的启动代码、异常/中断向量、上下文切换、堆栈初始化 |
arch/arm/core/cortex_m/ |
Cortex-M 专项:NVIC 配置、MPU 支持、SysTick、浮点上下文 |
arch/x86/ |
x86 架构(Intel 处理器),包含 MMU、IOAPIC 等 |
arch/riscv/ |
RISC-V 架构,支持 RV32/RV64,MMU 和 CLIC 中断控制器 |
架构层的责任是:让内核和驱动不关心 CPU 差异。启动序列、线程切换的汇编、特权级别切换、MMU/MPU 配置、异常处理都在这里。
boards/ —— 板级支持
每个板子一个目录,包含:
- Kconfig 板级默认配置——这个板子的默认串口是 UART0,默认晶振频率是 32MHz……
- 板级设备树文件——描述板上有什么外设、它们接在哪个引脚、I2C/SPI 地址是什么
- 板级文档——板子照片、引脚图、跳线说明
例如 boards/nordic/nrf52840dk_nrf52840/ 就是 Nordic nRF52840-DK 的板级支持。
drivers/ —— 设备驱动
这是 Zephyr 最大的目录之一,按外设类型分层:
drivers/
├── gpio/ # GPIO 驱动
├── i2c/ # I2C 控制器驱动
├── spi/ # SPI 控制器驱动
├── uart/ # 串口驱动
├── sensor/ # 传感器驱动(加速度、温度、湿度等 100+ 种)
├── pwm/ # PWM 驱动
├── adc/ # ADC 驱动
├── dac/ # DAC 驱动
├── counter/ # 定时器/计数器驱动
├── watchdog/ # 看门狗驱动
├── flash/ # Flash 存储驱动
├── bluetooth/ # 蓝牙 HCI 驱动
├── usb/ # USB 控制器驱动
├── can/ # CAN 控制器驱动
├── ethernet/ # 以太网驱动
├── dma/ # DMA 控制器驱动
├── clock_control/ # 时钟控制器驱动
├── pinctrl/ # 引脚复用控制
├── regulator/ # 电源调节器驱动
└── ...
Zephyr 的驱动模型是统一的:所有 GPIO 驱动实现同一个 gpio_driver_api,上层代码通过 gpio_pin_set()、gpio_pin_get() 等统一 API 操作任何厂家的 GPIO。驱动是 Zephyr 平台价值的核心体现——它屏蔽了芯片差异,让上层业务逻辑可以跨硬件复用。
dts/ —— 设备树
包含两部分:
dts/bindings/:设备树绑定(YAML 格式),定义每种设备节点允许的属性和类型。这是编译期校验设备树正确性的依据。- SoC 和板级
.dts/.dtsi文件:描述具体芯片的外设基地址、中断号、引脚分配等硬件事实。
设备树是 Zephyr 硬件描述的核心机制。它让同样的驱动代码在不同板子上运行时,只需换一套设备树,无需修改驱动源码。
include/ —— 公共头文件
include/zephyr/ 下按功能分子目录:
include/zephyr/
├── kernel/ # 内核 API(线程、信号量、互斥锁、队列、消息队列…)
├── drivers/ # 驱动 API 头文件
├── sys/ # 系统工具(环形缓冲、原子操作、C++ 互操作…)
├── net/ # 网络 API
├── bluetooth/ # 蓝牙 API
├── usb/ # USB API
├── logging/ # 日志宏
├── shell/ # Shell 框架
├── posix/ # POSIX 子集(pthread、信号…)
├── dt-bindings/ # 设备树宏定义头文件
└── ...
kernel/ —— 内核核心
Zephyr 内核的实现代码,包括:
- 调度器(多优先级、时间片轮转、最早截止时间优先 EDF)
- 线程管理(创建、终止、挂起、恢复、优先级调整)
- 同步原语(信号量、互斥锁、条件变量、事件、屏障)
- 数据传递(消息队列、管道、栈、队列)
- 内存管理(内存池、堆分配器、内存域、虚拟地址空间)
- 中断管理(ISR 注册、延迟中断下半部 workqueue)
- 时钟和超时管理
- SMP 多核支持
subsys/ —— 子系统
这是 Zephyr 区别于纯内核 RTOS 的关键目录之一:
| 子系统 | 说明 |
|---|---|
subsys/logging/ |
日志系统(支持多后端:串口、RTT、网络、文件……) |
subsys/shell/ |
命令行 Shell(支持 UART、Telnet、USB CDC ACM 后端) |
subsys/fs/ |
文件系统(LittleFS、FATFS 等) |
subsys/net/ |
网络栈(含 IP/TCP/UDP/HTTP/MQTT/CoAP/TLS/DTLS) |
subsys/bluetooth/ |
蓝牙 Host 协议栈(GAP/GATT/L2CAP/Mesh) |
subsys/usb/ |
USB 协议栈(CDC/DFU/HID/MSC/Audio) |
subsys/storage/ |
持久化存储(Flash Map、NVS、Settings API) |
subsys/mgmt/ |
设备管理(MCUmgr OTA 固件升级) |
subsys/dfu/ |
设备固件升级引导 |
subsys/pm/ |
电源管理策略 |
subsys/tracing/ |
内核事件追踪 |
subsys/modbus/ |
Modbus 协议栈 |
subsys/canbus/ |
CAN 总线高层协议 |
soc/ —— SoC 级定义
SoC 层级夹在"架构"和"板级"之间:同一个架构(如 ARM Cortex-M4)可以有很多不同厂家(Nordic nRF52、ST STM32、NXP i.MX RT)的 SoC。此目录存放 SoC 特定的初始化代码、寄存器定义、Flash/RAM 布局等。
modules/ —— 外部模块
Zephyr 通过 west.yml 管理外部依赖(HAL 层、MCUboot、LittleFS、mbedTLS、LVGL 等),这些外部仓库被下载到 modules/ 下统一编译。每个模块有一个 zephyr/module.yml 或使用 zephyr/module.yml 描述如何集成。
samples/ —— 示例程序
按子系统分类的完整示例,如:
samples/basic/blinky/—— 经典的 LED 闪烁(最小示例)samples/basic/threads/—— 线程创建和调度samples/subsys/shell/shell_module/—— Shell 使用示例samples/net/sockets/echo/—— Socket 网络通信
这是学习 Zephyr 最好的入口。
tests/ —— 测试
Zephyr 的测试基础设施,每个驱动/子系统都有对应的测试用例,通过 twister 测试框架在 CI 中运行。
scripts/ —— 工具脚本
包含 west 命令的实现、twister 测试框架、menuconfig 配置工具、设备树工具链(edtlib、dtlib)、编译工具链适配脚本等。
cmake/ —— 构建系统
Zephyr 基于 CMake 的构建框架。此目录包含通用的 CMake 函数和宏,供应用项目和模块使用,例如:
cmake/kconfig.cmake—— Kconfig 到 C 宏的转换cmake/dts.cmake—— 设备树到 C 宏的处理cmake/toolchain.cmake—— 交叉编译工具链配置cmake/flash.cmake—— 烧录目标
三、Zephyr 应用项目与 Zephyr 源码的关系
典型应用项目结构
my_zephyr_app/
├── CMakeLists.txt # CMake 构建入口
├── prj.conf # Kconfig 项目配置
├── src/
│ ├── main.c # 主程序
│ └── ... # 其他源文件
├── boards/ # 板级配置(可选)
│ └── nrf52840dk_nrf52840.overlay # 设备树 Overlay
├── include/ # 头文件(可选)
├── lib/ # 自定义库(可选)
└── README.md
核心目录/文件说明
CMakeLists.txt
项目的构建入口。最关键的一行是:
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(my_app)
target_sources(app PRIVATE src/main.c)
find_package(Zephyr) 会加载 Zephyr 源码中的 CMake 框架,把 Zephyr 的 arch、kernel、drivers、subsys 等全部作为编译对象纳入同一个 CMake 构建图。你的应用和 Zephyr 系统一起编译,共享同一个构建上下文——这是理解 Zephyr 应用项目关系的关键。
prj.conf
应用层的 Kconfig 配置。你可以在这里启用/禁用内核和子系统功能:
CONFIG_GPIO=y
CONFIG_SERIAL=y
CONFIG_SHELL=y
CONFIG_LOG=y
CONFIG_BT=y
这些配置会与 Zephyr 源码中的 Kconfig 文件自动合并,最终只编译你需要的模块。
boards/<board>.overlay
设备树 Overlay 文件。它不会修改 Zephyr 源码中的板级设备树,而是在编译时叠加到基础设备树上,用于:
- 启用板子上某个外设(Zephyr 默认可能没启用)
- 修改引脚分配
- 配置外设参数(如 I2C 频率)
- 添加板载外设的传感器节点
&i2c0 {
status = "okay";
bme280@76 {
compatible = "bosch,bme280";
reg = <0x76>;
};
};
应用与源码的关系模型
可以用一张图来理解:
┌──────────────────────────────────────────┐
│ my_zephyr_app │
│ ┌─────────┐ ┌─────────┐ ┌──────────┐ │
│ │ main.c │ │ prj.conf│ │ .overlay │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
└───────┼─────────────┼─────────────┼───────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────┐
│ CMake + Kconfig + Devicetree │ ← 构建系统层(Zephyr 源码提供)
│ find_package(Zephyr) 把它们全部串起来 │
└──────────────────────────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────┐
│ Zephyr 源码树 │
│ ┌────────┐ ┌──────────┐ ┌────────────┐ │
│ │ kernel │ │ drivers │ │ subsys │ │
│ │ arch │ │ include │ │ modules │ │
│ └────────┘ └──────────┘ └────────────┘ │
└──────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────┐
│ 交叉编译工具链 │
│ arm-none-eabi-gcc / llvm │
└──────────────────────────────────────────┘
│
▼
┌──────────┐
│ ELF 固件 │
└──────────┘
关键结论:
- 应用和 Zephyr 不是分离编译再链接的两个独立产物。
find_package(Zephyr)把你的src/main.c和 Zephyr 的kernel/、drivers/等源代码放入同一次 CMake 构建调用中,最终产出单个 ELF + hex/bin。 - Zephyr 是底座,应用是上面的逻辑。 Zephyr 提供了内核、驱动、协议栈、构建系统;你的应用只需关注业务逻辑,通过
prj.conf裁剪和.overlay适配硬件。 - 应用项目不产生自己的 SDK。 应用只有一个
CMakeLists.txt+ 一些配置文件 + 应用源代码。它依赖的环境变量ZEPHYR_BASE指向 Zephyr 源码树,所有底层能力都由 Zephyr 源码提供。
四、“Zephyr 不能当 middleware”——从平台视角理解 Zephyr 的抽象边界
很多从 FreeRTOS 转过来的开发者会问:“能不能把 Zephyr 像 FreeRTOS 一样,当做一个 middleware 组件塞进我们的 SDK 里?”
答案是:可以,但通常不应该这样做。 这不是 Zephyr 的能力问题,而是两种不同的设计范式。
FreeRTOS 的边界:内核 + 少量组件
FreeRTOS 的核心交付物是:
- 一个任务调度器
- 一组同步原语(信号量、互斥锁、队列、事件组)
- 软件定时器
- 可选的内存管理方案(heap_1 ~ heap_5)
- 可选的 TCP/UDP 协议栈和 CLI
概括一下:FreeRTOS 主要覆盖线程调度和基本的 RTOS IPC,它几乎不碰驱动、外设、板级硬件描述、构建系统、配置框架这些东西。所以它能很自然地作为一个 “middleware” 组件嵌入到任何 SDK 里——它要的只是底层的 tick 时钟和一个启动入口,剩下的交给厂商的 HAL/SDK 就行。
Zephyr 的边界:完整的 OS 平台
Zephyr 的抽象边界完全不一样。它覆盖了:
| 层面 | FreeRTOS 覆盖 | Zephyr 覆盖 |
|---|---|---|
| 内核调度 | ✓ | ✓ |
| 同步原语和 IPC | ✓ | ✓ |
| 统一设备驱动模型 | ✗ | ✓(drivers/ + 标准驱动 API) |
| 硬件描述语言 | ✗ | ✓(Devicetree) |
| 配置系统 | ✗ | ✓(Kconfig) |
| 构建系统框架 | ✗ | ✓(CMake + west) |
| 多仓库依赖管理 | ✗ | ✓(west.yml) |
| 网络/蓝牙/USB 协议栈 | 可选+有限 | ✓(完整集成) |
| 文件系统 | ✗ | ✓ |
| Shell / 日志 | 可选 | ✓ |
| 电源管理 | ✗ | ✓ |
| 设备管理 / OTA | ✗ | ✓ |
Zephyr 的抽象边界已经做到了OS 平台级别。它的构建/配置/设备树/驱动模型/依赖管理是一个紧密耦合的整体,很难像 FreeRTOS 那样干净地切出一个独立的内核组件放进另一个构建体系。
不是你技术不行,是"两套平台叠加"的工程现实
假设你要把 Zephyr 塞进一个厂商 SDK——
那个 SDK 大概率已经有:
- 自己的构建系统(Make / IAR / 自定义脚本)
- 自己的外设驱动(HAL / LL / stdperiph)
- 自己的配置方式(头文件宏 / CubeMX 代码生成)
- 自己的板级管理方式
- 自己的链接脚本和启动流程
然后你再加上 Zephyr 的:
- CMake + Kconfig + Devicetree 构建和配置体系
- 统一驱动模型
- 板级和 SoC 级设备树定义
结果是什么?平台叠平台。 构建时要协调两套构建系统,初始化时要协调两套启动流程,驱动要决定用谁的(用 Zephyr 的就得放弃 SDK 的 HAL 封装,用 SDK 的就得放弃 Zephyr 的跨硬件可移植性),引脚配置要两头配置保持一致……
这就像在 iOS 上再跑一套 Android 框架——两套完整的平台系统同时存在,协调成本远远超过收益。
抽象层不是问题,合理的抽象边界才是
有一种常见的误解是:“Zephyr 的抽象层太厚了,所以不好当 middlelayer。” 其实恰恰相反:
合理的抽象层可以把不可避免会变的东西给隔离掉——比如芯片差异、外设 IP、板级差异。 这样上层业务逻辑才能稳定。Zephyr 的驱动模型、设备树、Kconfig 正是在做这件事——而且做得很好。你在 nRF52840 上写的 I2C 传感器驱动,换到 STM32 上只需改设备树,代码一行不用动。
真正的问题不是"抽象层太厚",而是抽象边界放在哪一层:
- FreeRTOS 的边界在"内核",它可以很容易地被嵌入到任何 SDK 中——代价是驱动和外设的统一需要另想办法(通常是厂商 SDK 各搞一套,上层的可移植性靠你自己的 HAL 层)。
- Zephyr 的边界在"OS 平台",它自己就是底座——好处是驱动、配置、构建、依赖统一搞定,代价是它不能简单地被人塞进另一个平台里当 middlelayer。
内核厂商为什么统一不了驱动 API?
一个自然的问题是:为什么内核厂商不把驱动 API 也统一了?这样 FreeRTOS 不就能像 Zephyr 一样有跨硬件的驱动可移植性了吗?
答案在于驱动 API 统一需要的东西超出了内核的能力范围:
- 设备模型:不是简单的函数指针表,而是包含设备生命周期(init→power→suspend→resume)、设备依赖关系(SPI 依赖 GPIO 引脚,传感器依赖 SPI/I2C 总线)、设备电源域归属
- 配置机制:一个 UART 驱动在没有 Kconfig 的情况下如何让用户选择波特率?靠
#define宏和#ifdef条件编译凑出来的地狱? - 硬件描述:一个 I2C 设备在第几个 I2C 控制器上、地址是多少、中断线接哪个 GPIO——这些信息放哪里?硬编码在 C 文件里?那换一个板子怎么办?
- 构建链接:不同芯片的驱动源文件不同,片选需要构建系统的支持,不是靠
if defined(STM32F4) include "stm32f4_i2c.c"就能搞定的 - 依赖管理:某些驱动依赖外部的 HAL 库、协议栈或者算法库,谁来管理版本兼容性?
这些不是"一个调度器 + 一组同步原语"能解决的范畴。它们需要的是一个操作系统平台的体系——设备模型 + 配置机制 + 硬件描述 + 构建链接 + 依赖管理,五者缺一不可。
所以结论是:真正能统一驱动的,要么是 Zephyr/RT-Thread 这样把自己定位成"平台"的系统,要么是某个强势厂商自己的全栈 SDK(但那会锁死生态)。纯 RTOS 内核天然覆盖不了驱动统一这件事。
总结:选平台还是选 middleware,本质是选谁做底座
| FreeRTOS 模式 | Zephyr 模式 | |
|---|---|---|
| 定位 | 内核 + 少量组件 | 完整的 OS 平台 |
| 抽象边界 | RTOS IPC 层 | 从硬件到子系统的全栈 |
| 集成方式 | 嵌入到厂商 SDK 中 | 它就是底座,厂商 HAL 适配到它之上 |
| 驱动模型 | 依赖厂商 SDK | 统一的跨平台驱动 API |
| 硬件描述 | 无标准(头文件/代码生成) | Devicetree 标准化描述 |
| 可移植性 | 需要你自己的抽象层 | 换板子改设备树即可 |
| 多仓库管理 | 无(手工管理版本) | west.yml 声明式管理 |
| 适合场景 | 已有成熟 SDK 平台,只需一个调度器 | 从零构建跨硬件产品线,需要完整 OS 平台 |
Zephyr 让你觉得"麻烦",恰恰是因为它在做平台。你要么接受它做底座,让上层业务逻辑享受跨硬件复用的红利;要么就别把它当 FreeRTOS 那样的 middleware——这两种用法背后的设计哲学不同,没有高下之分,只有适合不适合。
但如果你今天还在找"把 Zephyr 塞进 SDK 当 middleware 的方法"——那不是 Zephyr 的设计缺陷,而是你对它的定位预期和它的设计边界发生了错位。理解了这个,你才算真正理解了 Zephyr。
五、接下来
本文只讲了"是什么"和"为什么"。后续计划写:
- Zephyr 的编译与构建流程:CMake + Kconfig + Devicetree 三者的协作机制
- 在电脑上跑第一个 Zephyr 程序:环境搭建、编译、用 QEMU 仿真运行
- 建立自己的板子设备树:从零写一个板级 Devicetree,在自己板子上跑通电灯程序
欢迎关注。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)