只读分享解决方案文档原理方案设计📅 2026-10-05🔖 Rev 1.4✅ 长期有效
🔧解决方案&应用市场分析 -> 原理方案设计 -> 嵌入式驱动&系统开发 -> AliOS_Things_系统开发指南

AliOS_Things_系统开发指南

生成时间 2026-10-04 11:48:54 长期有效
同系列 · 原理方案设计

AliOS Things 系统开发指南

编制日期 2026-10-05

面向 Cortex-M / RISC-V / C-Sky 的 AliOS Things 系统级开发规范 · 覆盖 Rhino 内核、CSI 硬件抽象、组件化配置、分级驱动加载、VFS 驱动、系统组件、调试、低功耗与 OTA · 系统总文档,驱动开发为其子章节

9 级
驱动加载等级
CORE → BUS → EARLY → VFS → LEVEL0~3 → POST
Rhino
实时内核
小体积硬实时,k_config.h 可裁剪
yaml
组件描述文件
package.yaml 取代手写 Makefile
0
设备树
无 DTS,板级资源全部静态配置
≥ 25%
栈水位余量
上线前压力测试实测值下限

一句话结论:AliOS Things 的系统设计,本质是用「Rhino 内核 + CSI 硬件抽象 + yaml 组件依赖 + 分级驱动加载」把「无序的外设初始化」变成「可声明的顺序」。 它比 FreeRTOS 多了组件依赖解析与 VFS 统一设备接入,比 Linux 少了设备树与虚拟内存——代价是板级资源全部写死在代码里,改一颗料的引脚就要动 BSP。 线上故障最集中的三处:同一加载级别内的初始化顺序随机、中断上下文调用了阻塞 API、栈与堆被低估。管住这三处,系统稳定性解决八成。

这份文档解决什么

本文是 AliOS Things 系统级开发总文档:从环境搭建、内核原理、启动与加载机制、CSI 抽象与 BSP 移植、组件化配置、设备驱动,到系统组件、调试、性能与低功耗、可靠性、测试、OTA 与发布规范,构成一条完整链路。 驱动开发作为第 8 章子章节嵌入;专项器件(SPI NOR / SPI NAND / SD NAND 等)的具体时序与命令细节,仍以对应器件的设计指南为准,本文只讲「在这一层该如何把驱动挂进系统」。

路线 1 · 移植新板 / 新芯片
先立骨架再写驱动
第 2 章环境与目录 → 第 5 章 CSI 接口族 → 第 6 章移植四要素(arch / chip / board / solution)→ 第 4 章分级加载排顺序 → 第 6.8 节验收清单。
重点:移植四要素(6.1)、静态资源配置(6.5)、分区表(6.6)
路线 2 · 排查启动与死机
起不来 / 跑着跑着重启
第 4 章启动链路与加载顺序 → 第 11 章崩溃捕获与栈回溯 → 第 11.8 节决策树 → 第 13 章看门狗与异常处理。
重点:九级加载表(4.3)、同级别随机陷阱(4.4)、addr2line(11.3)
路线 3 · 做产品级功能
联网 / OTA / 低功耗
第 7 章组件与裁剪 → 第 9 章 CLI / KV / SAL / LinkSDK / OTA → 第 12 章低功耗调优 → 第 15 章 A/B 分区与回滚。
重点:条件依赖(7.3)、分区表(15.1)、防砖设计(15.4)
路线 4 · 选型对比
AliOS / Linux / RT-Thread 怎么选
第 1.6 节四系统横向对照 → 附录 F 完整对照表 → 第 17 章 FAQ。
重点:MMU 与驱动隔离(1.6)、无 DTS 的代价(6.5)

使用约定

  • API 与目录以 AliOS Things 3.x(3.1 / 3.3) 为主线,兼顾 2.x;两代差异明显处均单独标注。目标平台以 Cortex-M3/M4/M33 为主,RISC-V 与 C-Sky(平头哥)差异另行提示。
  • 代码示例只保留系统层逻辑;具体寄存器操作统一写成 /* chip 层提供 */ 注释。
  • 标 红线 为强制约束,标 警惕 为高频踩坑点,标 建议 为经验推荐值。
  • 文中出现的默认数值(栈大小、优先级、阈值、目录名)以 SDK 模板与官方仓库为参考;不同版本目录名差异较大(如 2.x 顶层 board/ 在 3.3 收进 hardware/),落地前以工程内实际目录为准。
  • 芯片参数、时钟、外设行为一律以官方 datasheet 与 AliOS Things 官方文档 / 源码为准。

1 · 文档概述与系统架构

这一章解决三件事:这份文档覆盖到哪一层、AliOS Things 长什么样、以及它和 Linux / RT-Thread / FreeRTOS 的边界在哪。读完再决定后面该精读哪几章。

1.1 编写目的、适用范围与读者

本指南面向基于 AliOS Things 做系统级开发的工程团队:把一颗裸的 MCU 变成可量产、可升级、可运维的物联网设备。它要回答的不是「某个寄存器怎么写」,而是「一堆驱动和组件如何按正确顺序长成一个系统」。

维度本指南覆盖本指南不覆盖
目标读者系统 / BSP 工程师、驱动工程师、应用与云端对接工程师、测试与发布负责人只写业务逻辑、不接触系统的上层应用开发
硬件范围无 MMU 的 MCU:Cortex-M0/M3/M4/M33、RISC-V、C-Sky、Xtensa;linuxhost 仿真带 MMU 的 Cortex-A 应用处理器(走 Linux 分支)
软件范围Rhino 内核、CSI / HAL、BSP 移植、组件化构建、VFS 驱动、系统组件、调试、OTA、发布单个器件的电气与命令时序(见各器件设计指南)
文档定位系统总文档;驱动开发是其第 8 章子章节云端平台侧配置与业务规则(见阿里云 IoT 平台文档)

1.2 版本基线、内核与支持平台

AliOS Things 有两条实际在用的代际:2.x(Rhino 内核 + 顶层 board/ 与 platform/mcu/ + 组件 .mk 描述)与 3.x(3.1 / 3.3,统一 VFS 接入、yaml 构建、内核对象统一管理、轻应用框架)。本指南以 3.x 为主、2.x 兼容,两代差异较大处显式标注。

基线项说明对开发的影响
内核Rhino RTOS:小体积硬实时,支持静态与动态对象分配、多内存区域、work queue内核特性靠 k_config.h 裁剪,改配置不改源码
构建2.x:组件各自 .mk;3.x:组件各自 package.yaml,构建系统解析依赖图增删功能 = 增删 yaml 依赖,不手写 Makefile
设备接入3.3 起统一走 VFS,应用一律按标准文件方式访问 /dev 下节点驱动必须注册成 VFS 节点才能被标准方式访问
驱动框架对上给 AOS API,对下给原子驱动原语,复杂逻辑封装在驱动子系统内驱动开发者写的是「原语 + 注册」,不是裸读写
调试栈回溯解析率提升、新增 printk、cpuusage 与诊断命令增强崩溃定位不必每次都接仿真器
官方硬件HaaS100、HaaS200、HaaS EDU K1 等评估板新项目优先复用已适配板级,再改 board 层
基线选择建议

新项目一律从 3.3 之后的基线起:yaml 构建、统一 VFS、轻应用三件事能显著降低后期维护成本。已有 2.x 老项目不要中途升代际——目录结构(顶层 board/ 收进 hardware/)与构建描述(.mk 换 package.yaml)都会变,属于重写级改造。若必须升,走第 16 章的迁移发布流程。

1.3 术语与缩写

术语全称 / 含义出现场景
AOSAliOS Things 的简称,也是 aos_ API 前缀的来源所有 aos_* 接口、aos make 命令
RhinoAliOS Things 自研的实时内核任务、IPC、内存、定时器
krhinoRhino 内核原生 API 前缀,返回 kstat_t需要极致控制或读内核源码时
CSIChip System Interface,芯片系统接口(硬件抽象层接口族)GPIO / UART / SPI / I2C / ADC / DMA 等
HALHardware Abstraction Layer,更高一层的模块级适配(WiFi、Flash)网络与存储子系统适配
VFSVirtual File System,虚拟文件系统,把设备统一成 /dev 下节点驱动注册、open/read/write/ioctl
KVKey-Value 持久化键值存储保存配网信息、序列号、运行参数
YloopAliOS 的事件循环框架,主任务默认实例应用入口、事件驱动编程
SALSocket Abstract Layer,套接字抽象层多模网络统一 socket
uData传感器管理框架,统一传感器数据模型加速度计、温湿度等器件接入
ID2IoT Device ID,设备身份认证与密钥体系安全连接、安全存储、安全 OTA
LinkSDK阿里云物联网平台设备端 SDKMQTT 上云、物模型、设备影子
OTAOver-The-Air 固件升级,含差分与 A/B 回滚量产设备远程维护
多 bin / 轻应用应用与系统分离编译,JS / MicroPython 脚本引擎小镜像增量升级、脚本化业务逻辑
分级加载驱动按 9 个等级在链接段内自动排序初始化第 4 章核心机制
package.yaml组件的声明式描述文件(名称、版本、依赖、源文件、宏)第 7 章核心机制

1.4 五层架构总览

AliOS Things 采用分层架构 + 组件架构双视角:分层决定「谁能调用谁」,组件化决定「编译时怎么拼装」。两者叠加的效果是——上层只依赖 API 契约,下层只保证原语正确,中间靠构建系统把需要的组件拼进来。

AliOS Things 系统分层架构(组件化分层,自上而下依赖)⑤ 应用与解决方案层 solutions / application示例工程helloworld业务应用状态机 / 协议轻应用JS / MicroPython云端接入LinkSDK量产配置产品裁剪④ 通用组件层 components(增值中间件)OTA 差分升级ota / uagent传感器框架uData图形uDisplay / LVGL网络管理netmgr日志ulog / cli③ AOS API 与系统服务层(应用唯一应当直接依赖的一层)aos_* APIaos_task / aos_semPOSIX 层pthread / openVFS 虚拟文件系统/dev/xxxKV 键值存储aos_kvYloop 事件框架aos_post_event② 内核层 kernel(Rhino RTOS,本指南第 3 章主体)任务与调度krhino_taskIPCsem / mutex / queue内存管理堆 / 块 / 静态定时器krhino_timer对象管理统一内核对象① 硬件抽象与板级层 CSI / HAL / boardCSI 接口族 csi_gpio/uart/spiHAL 适配 hal_wifi/flasharch 移植 PendSV / SysTick板级配置 引脚 / 时钟 / 分区
图 1 · AliOS Things 五层架构:应用 / 组件 / AOS API 与系统服务 / 内核 / 硬件抽象
  • ⑤ 应用与解决方案层:示例工程、业务应用、轻应用脚本、云端接入。一个 solution 组件可以依赖多个 board 组件,构建时由 board_name 指定本次用哪块板。
  • ④ 通用组件层:OTA、uData、uDisplay、netmgr、ulog、cli 等增值中间件。它们与业务平级,靠组件依赖被拉进固件,靠加载等级或显式 init 挂进系统。
  • ③ AOS API 与系统服务层:应用唯一应当直接依赖的一层。VFS 把设备变成文件,KV 提供持久化,Yloop 提供事件循环。
  • ② 内核层 Rhino:任务、调度、IPC、内存、定时器、对象管理。3.3 起对任务 / 互斥量 / 信号量做一致性对象管理,可维护性与可扩展性明显提升。
  • ① 硬件抽象与板级层:CSI 接口族屏蔽寄存器差异,arch 层屏蔽 CPU 差异,board 层承载引脚、时钟与分区表。

1.5 AOS API 的三层封装

一个容易踩的坑是:同一件事在 AliOS Things 里有三套 API。混用不会报错,但会让代码在换内核或升版本时碎掉。

AOS API 三层封装:应用只看到 aos_ / POSIX,内核原语是 krhino_应用 / 组件代码调用 aos_task_new() / aos_sem_new() / pthread_create() / open()AOS API 层(osal_aos 组件)aos_ 前缀:跨内核的官方稳定接口,语义贴近 POSIX,版本间保持兼容POSIX 层:pthread / open-read-write,便于复用既有 Linux 代码Rhino 内核原语层krhino_task_create / krhino_sem_create / krhino_buf_queue_create返回 kstat_t 错误码(RHINO_SUCCESS / RHINO_NULL_PTR / RHINO_NO_MEM)硬件:Cortex-M / RISC-V / C-Sky / Xtensa / linuxhost
图 2 · AOS API 三层封装:应用 → aos_ / POSIX → krhino_ 内核原语 → 硬件
选型规则(红线)

业务与组件代码只用 aos_ 前缀或 POSIX 层;只有三类代码允许碰 krhino_:内核自身、BSP 里追求极致开销的路径、以及读内核源码定位问题的调试代码。 原因很实在:aos_ 是版本间承诺兼容的官方接口,krhino_ 是内核实现细节,代际升级时签名与语义都可能调整。

1.6 与 Linux / RT-Thread / FreeRTOS 的差异

把 AliOS Things 放进坐标系里看,它的独特性主要在三处:无设备树、分级加载、yaml 组件依赖。其余部分与主流 RTOS 大同小异。

项目AliOS ThingsLinux 内核RT-ThreadFreeRTOS
内核形态Rhino 实时内核,硬实时、小体积宏内核,需 MMU(Cortex-A)类 UNIX 分层 RTOS极简调度内核
硬件描述无 DTS,板级静态配置设备树 DTS / DTSI无 DTS(少数 BSP 用)无,全部写在代码里
驱动模型CSI + VFS 统一接入 + 分级加载总线模型 + probe 匹配I/O 设备模型 rt_device无统一模型,各写各的
工程管理package.yaml 声明依赖Kconfig + MakefileSCons + Kconfig手写 Makefile / CMake
构建工具aos-cube / aos make、HaaS Studiomake / bitbakescons / RT-Thread StudioIDE 或 CMake
文件系统LittleFS / FatFS / KV,轻量EXT4 / UBIFS / SquashFSDFS + elm-FatFS / LittleFS需外挂组件
云与生态LinkSDK / OTA / 阿里云原生整合需自行移植 SDK软件包中心,云接入自选需自行移植 SDK
驱动故障域驱动多在系统态;部分平台支持隔离驱动全在内核态,崩即 panic全在内核态全在内核态
选型一句话

要做连云 + 量产 OTA 的 MCU 设备,AliOS Things 的组件与云服务整合能省掉大量自研;要带屏、协议栈复杂、依赖丰富驱动生态的 MCU 项目,RT-Thread 的软件包更全;要极致精简或已有成熟代码栈,FreeRTOS 心智负担最小;上了 Cortex-A、要跑复杂业务,直接 Linux。

1.7 阅读路线图与本系列文档的关系

  1. 先看第 1、2 章:确认基线版本、把环境跑通、知道代码该放哪个目录。哪怕只是照抄 helloworld 示例,也要自己编译烧录一遍。
  2. 再看第 3、4 章:内核对象与启动 / 加载顺序。这两章决定了后面所有「为什么会卡住」类问题的定位速度。
  3. 做移植读第 5、6 章:CSI 接口族与移植四要素;做功能读第 7、9 章:组件依赖与系统组件。
  4. 写驱动读第 8 章:VFS 注册、open/ioctl 链路、并发与低功耗约束。专项器件的时序细节回到对应器件设计指南。
  5. 联调与上线读第 11、12、13 章:调试手段、资源与功耗、可靠性;交付读第 14、15、16 章:测试、OTA、发布。
文档边界

本系列中《RT-Thread 系统开发指南》《RT-Thread 驱动开发指南》《FreeRTOS 系统开发指南》与本指南同属 RTOS 系列,章节结构对齐,便于横向对照;涉及具体存储器件(SPI NOR / SPI NAND / SD NAND / eMMC)的电路与命令细节,请查阅对应器件系列文档。

2 · 开发环境与工程结构

这一章的目标很具体:把示例工程编译、烧录、跑起来,并且知道每一行代码该放在哪个目录。环境没跑通之前,后面所有章节都只是纸上谈兵。

2.1 宿主机环境与依赖

推荐 Ubuntu 20.04 / 22.04(64 位)。AliOS Things 的构建体系基于 Python 脚本(aos-cube)与构建框架混合,对宿主机版本比较敏感。

# 1) 基础依赖
sudo apt-get update
sudo apt-get install -y build-essential git python3 python3-pip python3-setuptools \
                        libssl-dev libffi-dev curl minicom

# 2) 构建工具 aos-cube(AliOS Things 命令行工具集)
pip3 install aos-cube           # 若系统 Python 版本冲突,可先建 venv
aos --version                   # 验证安装,例如 aos-cube 版本号 Rev 1.4.x

# 3) Cortex-M 工具链(以 ARM 官方 GCC 为例)
sudo apt-get install -y gcc-arm-none-eabi binutils-arm-none-eabi
arm-none-eabi-gcc --version

# 4) 其它架构按需安装
#    C-Sky(平头哥):csky-abiv2-elf-*     (芯片厂商提供)
#    RISC-V        :riscv64-unknown-elf-* 或 riscv32-elf-*
#    Xtensa        :xtensa-lx106-elf / xtensa-esp32-elf(厂商提供)

# 5) linuxhost 目标(PC 上跑仿真,验证业务逻辑很方便)
sudo apt-get install -y g++-multilib       # 64 位机器上编 32 位目标需要
环境三坑

① Python 版本:aos-cube 对 Python 版本敏感,系统自带 python3 与 pip 混用常出问题,建议用 python3 -m venv 隔离; ② 缺 32 位运行库:编译 linuxhost 时会报无法执行 32 位指令,装 g++-multilib; ③ 路径含中文或空格:部分脚本处理不好,源码目录必须是纯 ASCII 路径。

2.2 SDK 获取与分支管理规范

git clone https://github.com/alibaba/AliOS-Things.git
cd AliOS-Things
git checkout rel_3.3.0          # 或项目指定的基线 tag
git submodule update --init --recursive   # 部分版本含子模块

# 团队内建议:基线用 tag,开发用分支
git checkout -b feature/<board>-port rel_3.3.0
  • 基线一律用 tag(如 rel_3.3.0),禁止直接把 master 当产品基线——master 上的组件版本号会漂移(常见 master、dev_aos),导致构建不可复现。
  • 组件版本号必须与仓库 branch / tag 一致:package.yaml 里的 version 不是随便写的,构建系统依赖它解析依赖;对不上会直接报依赖解析失败。
  • 私有组件建议独立仓库 + 版本 tag,通过依赖引入,避免把 SDK 仓库改得无法升级。

2.3 交叉工具链配置

架构工具链前缀典型目标芯片配置要点
ARM Cortex-Marm-none-eabiSTM32、NXP、GD32、HaaS 系列最常见;-mcpu 与 FPU 选项要与实际内核匹配
C-Skycsky-abiv2-elf平头哥 CK 系列厂商提供,需在 chip 组件 package.yaml 写 toolchain_prefix
RISC-Vriscv64-unknown-elf / riscv32-elfGD32V、BL 系列、K210ABI(ilp32 / lp64)与 ISA 扩展要一致
Xtensaxtensa-lx106-elfESP8266 / ESP32厂商专用工具链,烧录需配套 esptool
Linux hostgcc(32 位)linuxhost 仿真目标纯业务逻辑调试,装 g++-multilib

工具链前缀通常在芯片组件的 package.yaml 里通过 toolchain_prefix 指定,构建系统据此拼出编译器名。若编译时报「找不到编译器」,先确认芯片组件里的前缀与实际安装的工具链名一致。

2.4 工具链全景

开发流水线:编码 → 构建 → 烧录 → 运行观测 → 调试① 编码HaaS Studio / VSCodeAliOS Studio 插件② 构建aos make app@boardaos-cube 解析 yaml③ 烧录aos upload厂商工具 / J-Link④ 观测串口终端 + CLIulog / printk⑤ 调试GDB / coredumpSmartTrace / SystemView中间产物:out/ 下按 app@board 分目录 → binary 内 .elf / .bin / .map + config.mkelf 用于 GDB 与 addr2line;bin 用于烧录;map 用于查 ROM/RAM 占用;config.mk 记录本次全部选项宿主机依赖(Ubuntu 20.04 / 22.04 推荐)- python3 + pip:安装 aos-cube- git:SDK 源码与子模块- gcc-arm-none-eabi:Cortex-M 工具链- csky-elf / riscv-elf:按芯片选装- build-essential、libssl-dev、python3-setuptools- g++-multilib:linuxhost 目标需要Windows / macOS 可选路径- HaaS Studio:一键编译烧录- 串口工具:串口助手 / minicom / screen- 驱动:J-Link、CH340、CP210x- 路径不要含中文与空格- 建议 WSL2 跑构建,避免工具链差异- 烧录工具依赖 Python,版本要匹配
图 3 · 开发与调试工具链全景:编码 / 构建 / 烧录 / 观测 / 调试五段流水线
工具定位典型用法
aos-cube命令行构建工具集:make / upload / monitor / convertaos make app@board、aos upload、aos monitor
AliOS Studio / HaaS StudioVSCode 插件:图形化编译、烧录、串口、调试新建工程、一键编译下载、串口终端
串口终端日志与 CLI 交互波特率以板级 UART 定义为准(常见 115200)
J-Link / OpenOCD断点、单步、内存查看配合 GDB 做 HardFault 现场分析
SmartTrace / SystemView系统行为 Trace:任务切换、中断、事件分析调度抖动与卡顿
coredump 解析脚本崩溃现场离线解析配合 elf 与 addr2line 还原调用栈

2.5 SDK 目录结构详解

AliOS Things 的目录按系统架构分层划分:components 放内核之上的所有功能组件,hardware 放板级支持包与 CPU 适配代码,kernel 放内核,solutions 放完整应用工程。2.x 与 3.x 目录名差异较大,落地前以工程实际目录为准。

SDK 目录结构(3.x 形态;2.x 的板级多在顶层,芯片层在 platform/mcu 下)components/内核之上的全部功能组件,每个目录一个 package.yaml→ 增改功能的主战场hardware/board(板级)+ chip(芯片)+ arch(CPU 架构)三类组件→ 移植新板新芯片落在这里kernel/Rhino 内核与内核对象管理、调度、IPC、内存→ 通常不改,靠 k_config.h 裁剪solutions/完整应用工程(solution 类型组件)→ 产品工程入口build / tools/构建框架、CLI、打包、烧录、coredump 解析脚本→ 一般不动documentation / test/说明文档与 UT 测试用例→ 交付与回归用编译产物:out/ 下按目标分目录,binary 内生成 .elf / .bin / .map,config.mk 记录全部选项
图 4 · SDK 目录结构与各目录职责(3.x 形态)
目录(3.x)对应 2.x内容什么时候动它
components/components/、framework/、utility/每个子目录一个组件 + package.yaml增删功能、新增自定义组件
hardware/board/、platform/board + chip + arch 三类组件移植新板 / 新芯片
kernel/kernel/、core/Rhino 内核与内核对象管理基本不动,改 k_config.h
solutions/application/、example/、projects/完整应用工程(solution 类型)产品工程入口
build/、tools/build/、tools/构建框架、CLI、打包烧录脚本一般不动
documentation/、test/test/文档与 UT 用例交付与回归
目录直觉

记住一句话:要加功能去 components,要换硬件去 hardware,要写产品去 solutions。内核目录尽量别碰——AliOS Things 的设计目标就是内核零修改,任何需要改内核源码才能实现的需求,八成是设计方式不对。

2.6 编译、清理与单组件编译

# 配置(交互式选择 app / board;首次会自动下载 kconfig 工具)
aos make menuconfig
aos make helloworld_demo@haas100 -c config     # 指定 app@board 并重新配置

# 编译:app@board 是最核心的一条命令
aos make helloworld_demo@haas100
aos make helloworld_demo@haas100 JOBS=8        # 并行加速

# 清理
aos make clean                                 # 清理当前目标产物
aos make distclean                             # 彻底清理(含配置)

# 烧录与串口
aos upload helloworld_demo@haas100             # 交互式选择串口号后烧录
aos monitor helloworld_demo@haas100            # 打开串口终端

# 生成组件模板(把已有源码目录快速变成组件)
aos convert <dir>                              # 在 dir 下生成 package.yaml 模板
编译命令三个高频问题

① app@board 名字写错:app 名来自 solutions 下的工程名,board 名来自 board 组件的 name,拼错会直接报找不到目标; ② 首次编译自动下载工具链 / kconfig:离线环境需提前准备,否则卡在下载环节; ③ 配置未生效:改了 package.yaml 或 def_config 后要 -c config 重新生成配置头,否则宏不生效。

关于单组件编译:AliOS 的构建以 app@board 为整体单位,没有独立的「只编一个组件」开关。实践做法是——建一个最小 solution 组件,只依赖要验证的那个组件,用它做编译与链接验证。这比在全量工程里排除依赖快得多,还能顺带验证组件的依赖声明是否自洽。

2.7 编译产物说明

产物路径 / 名称用途
可执行文件binary 目录下的 .elfGDB 调试、addr2line 符号解析
烧录镜像binary 目录下的 .bin量产烧录与 OTA 源镜像
链接映射binary 目录下的 .map查 ROM/RAM 占用、定位大符号
编译选项快照目标目录下的 config.mk复现构建、排查宏定义差异
自动配置头工程内的 aos_config.h所有 def_config 宏的最终落地
建议

把每次量产版本的 elf、map、config.mk 一并归档。线上崩溃时,只有与现场固件完全一致的 elf 才能正确解析栈回溯——版本对不上的符号表解析出来的行号全是错的。

2.8 烧录与串口调试环境

  1. 确认串口号与波特率:Linux 下 /dev/ttyUSB0,Windows 下 COMx;波特率以板级 UART 配置为准。
  2. 烧录:aos upload,交互式选择端口;特殊芯片依赖厂商工具(如 esptool),配置写在 upload 描述文件里。
  3. 打开终端:aos monitor 或串口助手,确认能看到启动日志与 CLI 提示符。
  4. 验证:输入 help 看命令列表,再敲内核与内存类命令确认组件已起来。
  5. 固化配置:把串口号、波特率、烧录偏移写进项目 README,避免团队内反复问。
红线:烧录偏移必须核对

不同芯片的 bootloader 偏移与分区表位置各不相同。照抄别的板子的烧录地址是变砖的最快路径——尤其涉及 OTA 分区时,地址错会导致校验失败而反复重启。地址以目标板的链接脚本与分区表为准(见 6.6 与 15.1)。

3 · Rhino 内核基础

Rhino 是 AliOS Things 自研的实时内核:小体积、硬实时、可裁剪。这一章不追求罗列每个 API,而是把任务、IPC、中断、内存、调度、定时器六类机制的设计意图与约束讲清楚——绝大多数系统级 Bug 都能追溯到这六处之一。

3.1 V2 与 V3 的内核形态差异

方面2.x3.x工程影响
内核Rhino,功能以宏内核方式提供Rhino 继续演进,新增内核对象统一管理任务 / 互斥量 / 信号量用一致方式管理,可维护性提升
设备接入多种访问方式并存统一 VFS 接入应用一律按文件方式访问设备,驱动必须注册节点
驱动框架驱动各自实现对上 AOS API、对下原子驱动原语驱动写「原语 + 注册」,子系统承担复杂逻辑
构建组件 .mk组件 package.yaml依赖关系显式化,增删功能改 yaml
调试基础手段栈回溯、printk、cpuusage 增强崩溃定位效率显著提升
应用形态C 应用为主新增 JS / MicroPython 轻应用业务逻辑可脚本化,支持小镜像增量升级
关于「微内核」的说法要谨慎

业界常把 3.x 描述为向微内核 / 多 bin 方向演进(组件化、服务化、应用与系统分离编译)。但要清醒:在没有 MPU / TrustZone 的 Cortex-M 上,所谓隔离是软约束——一个野指针照样能写穿整个地址空间。 工程上真正能拿到收益的是故障域收敛(驱动崩溃不至于拖垮调度)与增量升级(应用单独编镜像),而不是内存级隔离。是否需要用户态驱动,取决于芯片有没有 MPU,而不是取决于版本号。

3.2 内核对象与统一对象管理

3.3 起,任务、互斥量、信号量等内核资源采用一致的管理方式:统一的创建 / 销毁路径、统一的错误码体系、可被诊断命令统一遍历。这带来两个直接好处:

  • 诊断命令能列出全部对象:任务列表、内存信息、CPU 占用等命令依赖的就是这套对象管理;对象不进容器,命令行里就看不到它。
  • 静态与动态创建路径对齐:静态对象(自带控制块与缓冲)与动态对象(堆上分配)语义一致,只是内存来源不同,便于后期把动态改静态以抗碎片。

错误码:内核原语返回 kstat_t(如 RHINO_SUCCESS、RHINO_NULL_PTR、RHINO_NO_MEM),而 aos_ 层通常转成 int(0 成功 / 负值失败)。混用两层时不要拿 kstat_t 当 errno 用。

3.3 任务状态机与优先级

任务(task)是 Rhino 的调度实体。理解状态机的价值在于:看到某个任务不动了,能立刻判断它是被阻塞、被挂起,还是压根没被调度。

Rhino 任务状态机与迁移条件(就绪 / 运行 / 阻塞 / 挂起 / 删除)就绪 READY等待被调度运行 RUNNING占用 CPU阻塞 BLOCKED等信号量 / 队列 / 延时挂起 SUSPEND被显式挂起删除 DELETED已退出,资源待回收调度选中被抢占等待资源资源可用suspendresumedelete空闲任务(idle):优先级最低,系统无事可做时运行,是低功耗与 CPU 占用统计的锚点任务退出:exit / delete 之后进入删除态,静态创建对象的栈与控制块需自行回收
图 5 · Rhino 任务状态机:就绪 / 运行 / 阻塞 / 挂起 / 删除
/* AOS 层创建任务的典型形态 */
static aos_task_t g_sensor_task;
static uint8_t    g_sensor_stack[2048];      /* 静态栈:抗碎片,推荐 */

static void sensor_task_entry(void *arg)
{
    while (1) {
        sample_once();                        /* 采样 */
        aos_msleep(100);                      /* 让出 CPU,不要用忙等 */
    }
}

int sensor_task_start(void)
{
    int ret = aos_task_new_ext(&g_sensor_task, "sensor", sensor_task_entry,
                               NULL, sizeof(g_sensor_stack), SENSOR_PRIO);
    /* 返回值必须检查:栈不足 / 优先级非法 / 内存不足都会失败 */
    return ret;
}
优先级三条规则

① 数值越小优先级越高(0 为最高),与 FreeRTOS / RT-Thread 一致,但与某些 OS 相反,跨项目移植时务必确认; ② 优先级数量由 k_config.h 决定,不是无限的——优先级级数越多,就绪表开销越大; ③ 空闲任务必须存在且优先级最低,它是低功耗与 CPU 占用统计的锚点,不要用业务任务把它饿死。

优先级分档方法论(建议)

档位优先级区间建议放什么约束
中断下半部 / 硬实时最高档(数值最小)协议时序、采样触发、马达控制执行时间必须可预期,禁止阻塞
通信与协议栈中高档网络收发、串口协议解析允许短阻塞,禁止长时间占用
业务处理中档状态机、云端消息、业务计算按业务重要性细分 1~2 档
存储与日志中低档Flash 写入、日志落盘允许较长阻塞
监控与空闲最低档看门狗喂狗、空闲钩子、统计绝不能抢占业务

3.4 IPC 原语与选型

Rhino 提供的内核原语相当丰富:缓冲队列、环形缓冲、定时器、信号量、互斥量、队列、事件等。选择的原则不是「哪个功能多」,而是「哪个语义最贴合」——用信号量保护临界区是经典的优先级反转来源。

六类 IPC 原语的能力矩阵与选型原则原语传递数据所有权优先级继承典型场景信号量 sem计数无无资源计数、同步通知互斥量 mutex无有有保护临界资源事件 event标志位无无多条件等待与组合触发队列 queue指针 / 消息无无任务间传消息缓冲队列 buf_queue带长度的数据块无无变长数据,如串口流工作队列 workqueue回调函数无无中断下半部、延迟执行选型原则:只做同步用 sem;保护共享资源必须用 mutex(带优先级继承);要传数据用 queue / buf_queue中断里只能发信号(非阻塞版本),绝不能等
图 6 · 六类 IPC 原语能力矩阵与选型原则
原语AOS 层接口(示意)内核原语关键点
信号量aos_sem_new / aos_sem_signal / aos_sem_waitkrhino_sem_create / give / take可指定超时;中断里只 signal
互斥量aos_mutex_new / lock / unlockkrhino_mutex_create / lock / unlock带优先级继承;必须由持有者释放
事件aos_event_new / get / setkrhino_event_create / get / set支持「与 / 或」组合等待
队列aos_queue_new / send / recvkrhino_queue_create / send / recv定长消息,多生产者多消费者
缓冲队列对应带长度的数据队列krhino_buf_queue_create变长数据块,适合流式数据
工作队列aos_workqueue / aos_workkrhino_work_init / run中断下半部与延迟执行
红线:拿信号量当互斥量用

信号量没有所有权概念:A 任务拿、B 任务放也能通过,一旦发生优先级反转,系统没有任何机制兜底。 保护共享资源一律用互斥量(带优先级继承);信号量只用于「同步通知」与「资源计数」。

3.5 中断模型与中断上下文约束

中断是实时性的来源,也是稳定性事故的高发区。Rhino 的模型与主流 RTOS 一致:上半部极短、下半部延后。

中断上半部 / 下半部划分:ISR 只做清中断与投递,重活交给任务或工作队列上半部:ISR(中断上下文)- 关调度、不可阻塞- 使用中断栈(Cortex-M 走主栈)- 只做:读状态 / 清中断 / 取数据- 投递:sem give、queue send、work 提交- 禁止:malloc、延时、等锁、批量打印- 执行时间目标:越短越好下半部方式 A:工作队列 / 延迟处理- 在内核工作线程上下文执行- 仍不可长时间阻塞- 适合:短小的后半段处理下半部方式 B:专用处理任务- 独立任务 + 队列 / 信号量- 可以阻塞、可以拿锁、可以慢- 适合:协议解析、Flash 写入、日志落盘- 优先级按业务实时性设定投递通知
图 7 · 中断上半部 / 下半部划分:ISR 只做清中断与投递

中断上下文禁用清单(红线)

  • 任何可能阻塞的调用:带超时的 sem_wait / mutex_lock / queue_recv、延时接口。
  • 拿互斥量:中断没有任务上下文,持有者语义不成立。
  • 大批量日志打印:串口打印是毫秒级操作,会把中断拖成灾难。
  • 动态内存分配:不可重入风险 + 时长不确定。
  • 浮点运算(无 FPU 上下文保存时):会破坏任务侧浮点现场。
中断侧正确姿势

ISR 里做三件事:读状态、清中断、把数据投递出去(sem give / queue send 的非阻塞版本 / 提交 work)。其余一律交给下半部任务。 需要量化指标时,建议约束:单条 ISR 执行不超过数十微秒、中断服务总占用 CPU 不超过 10%(具体阈值按产品实时性定)。

3.6 内存管理与碎片

Rhino 为大多数内核对象提供静态与动态两种分配;小内存块分配器既支持定长块也支持变长块,并支持多个内存区域。这套设计的目的是让固件能塞进资源极其有限的设备,代价是碎片与容量必须自己管。

MCU 上的典型内存布局:静态区、堆、任务栈、中断栈静态区 .data / .bss:全局量、静态任务栈、静态内核对象编译期确定,永不归还;适合必须长期存在且不允许分配失败的对象堆 Heap:aos_malloc / krhino_mm_alloc支持多块内存区域;小内存块分配器兼顾定长与变长;碎片是长期运行的头号风险建议:长期运行的对象静态化;只在初始化期做动态分配任务栈:每个任务独立栈空间含局部变量、函数调用、中断嵌套(部分架构)中断栈:中断上下文使用Cortex-M 通常复用主栈 MSP,占用在链接脚本里预留监控手段:内存信息命令看总量 / 已用 / 峰值;泄漏检测看未归还块上线门槛:堆峰值后剩余 ≥ 20%,任务栈水位余量 ≥ 25%
图 8 · 内存布局:静态区 / 堆 / 任务栈 / 中断栈与监控门槛
碎片的三条经验

① 长期运行的对象静态化:任务、互斥量、队列、网络缓冲等,能静态就静态,避免长期运行后堆碎成筛子; ② 动态分配集中在初始化期:运行期尽量不再 malloc / free,要用就做成定长块池; ③ 变长数据用缓冲队列而不是反复 malloc,让内存行为可预测。

泄漏与踩踏的检测手段

手段能发现什么使用时机
内存信息命令当前总量、已用、峰值、剩余联调期与压力测试中定期观察
内存泄漏检测未归还的分配点及其调用栈长时间拷机后执行,比对分配点
栈水位统计任务栈历史最大使用量压力测试后逐个任务核对
静态分配改造根本性规避碎片稳定性要求高的量产项目

3.7 调度器与抢占

Rhino 是基于优先级的抢占式调度器:任何时刻就绪的最高优先级任务在运行;同优先级任务可按时间片轮转(取决于配置)。调度点包括:任务阻塞 / 退出、信号量与队列操作、中断返回、时间片到期、显式让出。

  • 抢占代价:上下文切换要压栈弹栈,优先级级数与任务数越多开销越大。频率极高的任务要考虑合并或降频。
  • 临界区:关中断 / 关调度的时间直接决定最坏中断延迟。关中断区间内禁止任何函数链不确定的操作。
  • 优先级反转:低优先级任务持锁、中优先级任务抢占、高优先级任务等锁——靠互斥量的优先级继承缓解,但不能靠它根治错误的锁设计。

3.8 定时器与工作队列

机制上下文适合做什么不适合做什么
软件定时器回调定时器服务上下文周期性心跳、超时判定、状态轮询耗时操作、拿锁、大量 IO
任务延时任务自身上下文让出 CPU、周期性任务节拍精确时序(受调度抖动影响)
工作队列工作线程上下文中断下半部、延迟执行长阻塞操作(会堵住队列)
硬件定时器中断上下文精确定时、波形输出、输入捕获任何阻塞操作
定时器回调的三条约束

① 回调里不能阻塞,否则整个定时器服务被拖住;② 回调要短小可预期,长活投递给任务;③ 不要在回调里创建 / 删除内核对象,容易与服务自身的锁形成竞态。

4 · 启动流程与分级驱动加载

这一章是 AliOS Things 与其他 RTOS 拉开差距的地方。Linux 靠设备树描述硬件、靠 probe 顺序解决依赖;AliOS Things 靠链接段 + 九个初始化等级解决同样的问题。理解它,就能解释「为什么我的驱动有时好用有时不好用」。

4.1 上电到应用任务的全链路

上电到应用任务:七个阶段,每一段都有明确的归属目录① 复位向量Reset_Handler:初始化栈指针、数据段与 bss归属:arch 层② 芯片级初始化时钟树、Flash 等待周期、中断向量表归属:chip / mcu 层③ 板级初始化引脚复用、外设电源、串口、分区表注册归属:board 层④ 内核初始化Rhino 内核对象、调度器、空闲任务就位归属:kernel⑤ 系统服务初始化VFS、CLI、KV、SAL、Yloop、OTA、sensor归属:components⑥ 分级驱动加载CORE → BUS → EARLY → VFS → LEVEL0~3 → POST归属:drivers⑦ 应用入口主任务创建、Yloop 运行、业务线程启动归属:solutions
图 9 · 启动全链路七阶段与各自归属目录

七个阶段的分工非常明确:arch 层只管 CPU,chip 层只管芯片,board 层只管这块板子,components 只管软件服务。排查启动故障时,先看卡在哪一段,就基本知道该翻哪个目录的代码。

4.2 系统服务初始化内部顺序

内核初始化之后,系统会依次拉起一组核心服务。以 2.x / 3.x 共有的结构为例,典型顺序是:

/* 概念化顺序,具体以工程源码为准 */
vfs_init();          /* 虚拟文件系统:建立 inode 表与互斥保护 */
vfs_device_init();   /* 注册设备节点与操作方法 */
cli_service_init();  /* 命令行服务(未启用 CLI 时走无 CLI 的日志初始化) */
aos_kv_init();       /* 键值存储初始化:配网信息、序列号从这里读 */
sal_device_init();   /* 套接字抽象层(启用网络时) */
aos_loop_init();     /* Yloop 事件循环:主任务默认实例 */
ota_service_init();  /* OTA 服务(启用时) */
sensor_init();       /* 传感器框架(启用时) */
/* 之后进入分级驱动加载,再进入应用入口 */
顺序带来的两条推论

① 依赖 VFS 的驱动必须排在 VFS 初始化之后——这正是 VFS_DRIVER_ENTRY 这一级存在的意义; ② KV 初始化早于业务,意味着业务可以在启动早期读到配网信息;反过来,任何在 KV 之前就要读存储的代码都会失败。

4.3 九级驱动加载机制

驱动框架把设备驱动分成 9 个级别,每个级别对应一个声明宏与一个链接段。用同一宏声明的函数指针会在链接阶段被放进同一个段,系统启动时按段顺序依次调用。这是 AliOS Things 解决驱动依赖的核心手段。

驱动分级加载:宏决定链接段,段决定初始化顺序;同级内部顺序随机顺序声明宏典型驱动约束与说明1CORE_DRIVER_ENTRY中断控制器、时钟、tick 源最早,不能依赖任何其它驱动2BUS_DRIVER_ENTRYSPI / I2C / UART 等总线控制器本体总线必须先于挂在它上面的设备3EARLY_DRIVER_ENTRYGPIO、Flash 控制器、看门狗早于 VFS,便于早期日志与存储4VFS_DRIVER_ENTRY需要注册设备节点的驱动VFS 已初始化,可安全注册5LEVEL0_DRIVER_ENTRY普通外设一级:传感器、显示最常用的默认档6LEVEL1_DRIVER_ENTRY普通外设二级需要 LEVEL0 就绪后再来7LEVEL2_DRIVER_ENTRY普通外设三级依赖前面各级8LEVEL3_DRIVER_ENTRY普通外设四级更晚的依赖项9POST_DRIVER_ENTRY收尾:统计、巡检、业务钩子所有驱动就绪之后另:VFS_DRIVER_BG_ENTRY 为后台初始化声明,用于低优先级驱动的延后加载
图 10 · 九级驱动加载顺序、对应宏与典型驱动
/* 典型用法:用宏声明驱动初始化函数 */
static void my_spi_driver_entry(void)
{
    /* 1) 初始化控制器(CSI 已提供时直接调用 CSI 接口) */
    csi_spi_init(&g_spi, 0);
    /* 2) 注册到 VFS / 设备框架 */
    register_dev_node("/dev/spi0", &my_spi_ops, &g_spi);
}
BUS_DRIVER_ENTRY(my_spi_driver_entry)            /* 总线控制器:第 2 级 */

static void my_sensor_driver_entry(void)
{
    /* 依赖 SPI 总线,必须晚于 BUS 级 */
    sensor_dev_t *dev = sensor_create(...);
    sensor_register(dev);
}
LEVEL1_DRIVER_ENTRY(my_sensor_driver_entry)      /* 第 6 级 */

分级选择决策(建议)

你的驱动是什么推荐级别理由
中断控制器、时钟、tick 源CORE内核本身依赖,必须最早
SPI / I2C / UART 等总线控制器本体BUS挂在总线上的设备必然依赖它
GPIO、Flash 控制器、看门狗EARLY早期日志与存储需要
需要注册设备节点、提供 open/ioctlVFS必须在 VFS 初始化之后
传感器、显示屏等普通外设LEVEL0 ~ LEVEL2按相互依赖逐级后移
收尾、统计、巡检、业务级钩子POST 或 LEVEL3所有驱动就绪后再跑
耗时长但可延后的初始化VFS_DRIVER_BG(后台)不阻塞主启动链路

4.4 同级别内顺序随机(头号陷阱)

红线:同一宏声明的多个驱动,初始化顺序是随机的

驱动框架只保证级与级之间的顺序;同一级内部的调用顺序由链接器决定,不可依赖。 这解释了一种极其经典的现象:同样的代码,加了一个源文件、改了一个编译选项,某个驱动就时好时坏——因为它恰好和另一个同级别驱动产生了隐式依赖。

规避方法

  • 有依赖就分到不同级别:被依赖方放更低的级别号,不要试图在同一级里「碰巧先跑」。
  • 同级别驱动之间禁止隐式依赖:A 驱动的初始化函数不得假设 B 已完成。
  • 需要严格次序的一组初始化,合并成一个 entry:在一个初始化函数内按固定顺序调用,比分散到多个宏可靠得多。
  • 改用显式初始化:对强依赖的模块,宁可在业务启动阶段显式调用 init,也不要赌链接顺序。
  • 用后台声明延后:耗时且非关键的初始化用后台声明,避免拖慢启动并降低耦合。

4.5 内核态 / 用户态与多 bin

3.x 在架构上支持应用与系统分离编译(多 bin)与轻应用运行时,目标是三件事:

  • 增量升级:应用单独编成小镜像,OTA 时只下应用,省流量、降风险。
  • 故障域收敛:应用逻辑出错不至于直接拖死内核(在具备 MPU / TrustZone 的平台上效果更实)。
  • 脚本化业务:JS / MicroPython 轻应用让业务改动不必重新编译整个固件。
问题内核态驱动用户态驱动 / 多 bin
性能最佳:直接访问,无切换开销有跨态调用开销
故障影响驱动崩溃即系统崩溃故障可收敛,系统可继续
硬件依赖无特殊要求需要 MPU / TrustZone 才有实际隔离效果
升级粒度整包升级应用 / 模块单独升级
适用高频、强实时、与中断强耦合的驱动协议类、业务类、非实时外设
什么时候该拆到用户态

判断标准只有两条:是否需要硬实时响应与是否必须直接碰中断。只要有一条成立,就留在系统态;否则拆出去能换来更好的可维护性与升级灵活性。 反过来,为了「架构好看」把高频驱动拆到用户态,只会换来抖动与复杂度。

4.6 启动期故障排查

启动不起来时,先按「卡在哪一段」定位,再谈细节。绝大多数问题能在下表中找到对应项。

现象可能根因排查动作
完全无串口输出波特率 / 引脚不对;时钟未起;烧录地址错量串口 TX 波形;查板级 UART 引脚与时钟配置;核对烧录偏移
打印几行后停住卡在某个驱动的初始化里(忙等或失败重试)在各级 entry 加标记日志;用后台声明把可疑驱动延后验证
反复重启看门狗未喂 / 栈溢出 / 断言失败看复位原因寄存器;临时关看门狗验证;检查任务栈
某个设备节点不存在驱动未注册或注册晚于访问确认加载级别;确认访问发生在初始化之后
时好时坏同级别顺序随机导致的隐式依赖把有依赖的驱动拆到不同级别
启动很慢某驱动初始化耗时长;Flash 读取慢;日志过多用时间戳打点;把非关键初始化改后台声明
排查工具建议

在每一级驱动的入口加一行带时间戳的日志(哪怕只是级别编号),是定位启动问题性价比最高的手段。把这套日志做成默认打开的编译选项,量产前再关掉,比事后接仿真器快得多。

扫码打开本页
微信扫码 · 展会可扫

再分享给同事

同事可直接打开这份资料《AliOS_Things_系统开发指南》,在线阅读;完整章节请下载芯参谋。