RTOS 核心概念与选型:从裸机思维跳出来

说实话,很多从裸机转过来的朋友,第一次接触 RTOS 都会有点懵。我当年也是——明明一个 while 循环跑得好好的,为什么要搞个「任务」出来?

别急,咱们一步步拆开看。

什么是 RTOS?

RTOS 全称 Real-Time Operating System,实时操作系统。它跟 Linux、Windows 那种「通用操作系统」最大的区别在于:它保证在确定的时间内响应外部事件。

举个例子。你写一个无人机飞控,传感器数据来了,必须在 1ms 内处理完。裸机里你得手动算好每个中断的优先级、每个函数的执行时间,稍不留神就炸机。RTOS 帮你管这件事——它知道哪个任务紧急,先跑哪个。

核心要点:RTOS 不是「跑得快」,而是「时间确定」。微秒级的抖动可以接受,但绝不能出现「这次 1ms,下次 100ms」的情况。

任务与调度:谁先跑谁后跑?

任务(Task)是 RTOS 里最基本的执行单元。每个任务就是一个无限循环的函数,有自己的栈空间、优先级和状态。

调度器(Scheduler)是 RTOS 的大脑。它决定:当前该让哪个任务上 CPU。

抢占式调度

这是最常用的方式。高优先级任务就绪后,立刻打断正在运行的低优先级任务。我做过一个电机控制项目,电流环任务优先级最高,必须在 50μs 内响应。抢占式调度保证它随时能抢到 CPU。

我的习惯:抢占式调度下,优先级别设太多。5-8 级足够,多了反而容易出优先级反转的问题。

协作式调度

任务自己主动让出 CPU。说白了就是「你跑完了叫我」。这种方式代码简单,没有抢占带来的竞态问题。但缺点也很明显——如果一个任务死循环,整个系统就挂了。

我一般只在超低功耗的传感器节点上用协作式。平时任务跑完就休眠,醒来继续。

调度方式 优点 缺点 适用场景
抢占式 响应快,实时性强 需要处理临界区、优先级反转 工业控制、无人机、汽车电子
协作式 无竞态,代码简单 任务必须主动让出 CPU 低功耗传感器、简单状态机

任务状态与切换

每个任务在生命周期里有 4 种状态:

  • 运行态:正在使用 CPU
  • 就绪态:随时可以跑,但 CPU 被别的任务占着
  • 阻塞态:在等某个事件(信号量、队列、延时)
  • 挂起态:被其他任务或自己暂停了

任务切换(Context Switch)就是保存当前任务的寄存器、栈指针,加载下一个任务的上下文。这个过程越快越好——FreeRTOS 能做到几百个时钟周期内完成。

我曾经踩过的坑:任务栈分配太小,导致栈溢出,系统随机崩溃。排查了两天才发现。建议每个任务至少给 256 字节,复杂任务 512 起步。调试时打开栈溢出检测钩子。

任务状态转换图 运行态 就绪态 阻塞态 挂起态 被抢占 等待事件 调度 事件到达 挂起 恢复 说明:只有就绪态的任务才能被调度器选中进入运行态 阻塞态通常由延时、等待信号量/队列/事件触发

同步与通信机制

任务之间怎么传数据?怎么协调先后顺序?RTOS 提供了三种经典工具。

信号量(Semaphore)

说白了就是一个计数器。二值信号量像一把锁——只有 0 和 1。计数信号量可以管理多个资源。

我常用二值信号量做「中断通知」。比如串口收到一帧数据,中断里释放信号量,任务里等信号量来了再处理。这样中断服务程序只做最轻量的事。

注意:中断里释放信号量用 give-from-isr 版本,别用普通 give。否则可能死锁。

队列(Queue)

队列是任务间传递数据的首选。一个任务往里写,另一个任务往外读。队列自带拷贝机制——你发的是数据的副本,不是指针。这样避免了共享内存的竞态问题。

// FreeRTOS 队列示例
QueueHandle_t xQueue;

// 创建队列,每个元素 4 字节,深度 10
xQueue = xQueueCreate(10, sizeof(uint32_t));

// 任务 A 发送
uint32_t data = 100;
xQueueSend(xQueue, &data, portMAX_DELAY);

// 任务 B 接收
uint32_t recv;
xQueueReceive(xQueue, &recv, portMAX_DELAY);

事件标志组(Event Group)

一个任务要等多个条件同时满足,怎么办?事件标志组就是干这个的。每个 bit 代表一个事件,可以「与」逻辑等待(所有位都置位)或「或」逻辑等待(任意位置位)。

我记得有个项目需要等「传感器就绪」和「网络连接成功」两个条件都满足才能开始采集。用事件标志组,一行代码搞定。

机制 传递数据 典型用途 中断安全
信号量 否(仅计数) 资源管理、中断通知 是(需用 ISR 版本)
队列 是(拷贝数据) 任务间数据传递 是(需用 ISR 版本)
事件标志组 否(仅位标志) 多条件同步 是(需用 ISR 版本)

常见 RTOS 对比

市面上 RTOS 很多,我挑三个最常用的说说。

FreeRTOS

开源、轻量、资料多。适合资源受限的 MCU,比如 STM32、ESP32。我大部分项目都用它。缺点是没有原生设备驱动框架,得自己封装。

RT-Thread

国产之光。除了内核,还带了设备框架、文件系统、网络协议栈。适合功能复杂的物联网设备。我做过一个带 Wi-Fi 和 LCD 显示的温控器,RT-Thread 开箱即用。

uC/OS

老牌商业 RTOS,代码质量极高,文档详尽。但授权费不便宜。军工、医疗等对认证有要求的领域用得比较多。

特性 FreeRTOS RT-Thread uC/OS-III
授权 MIT 开源 Apache 2.0 商业授权
最小 ROM ~4KB ~6KB ~8KB
最小 RAM ~200B ~500B ~1KB
设备框架 无 有 无
生态 极广 国内活跃 成熟稳定

如何根据项目选型?

这个问题没有标准答案。我一般按这个思路来:

  1. 看资源:Flash 小于 32KB、RAM 小于 4KB?老老实实裸机或 FreeRTOS 精简版。
  2. 看功能:需要文件系统、网络、USB 协议栈?RT-Thread 省心很多。
  3. 看认证:医疗、航空?uC/OS 有安全认证,省得自己从头做。
  4. 看团队:团队熟悉哪个就用哪个。换 RTOS 的学习成本比想象中高。

我的建议:新手从 FreeRTOS 入手。资料多、坑少、社区活跃。跑通一个点灯任务,比看十篇理论文章都有用。