RTOS 基础
本文性质声明:本机无开发板,全部内容未实测,为手册级框架。站内对照:os-scheduling(调度算法与实时性)、conc-threads-vs-processes(上下文切换成本)、conc-mutex(锁与数据竞争)、qt-thread(Qt 侧的任务模型)。
一、什么时候需要 RTOS:三个判据
裸机(超级循环 + 中断)能覆盖的场景比想象多。上 RTOS 的三个信号:
- 多个不同周期的活动要共存:10ms 采按键、100ms 刷屏、1s 报一次——裸机用软件定时器数组能撑,但活动超过五六个、还有优先级差(通信协议栈 > UI 刷新)时,超级循环的”顺序执行”模型开始伤害正确性。
- 有阻塞需求:等一个 DMA 完成、等一个信号量、等队列来数据——裸机只能忙等或状态机化,RTOS 的任务挂起/恢复把”等待”变成几乎免费。
- 代码规模/维护:协议栈、文件系统、多设备驱动并行开发——RTOS 的任务边界就是模块边界,比” everyone 改同一个 while(1)” 可协作。
别为”看起来专业”上 RTOS:一个 8KB Flash 的 MCU 跑一个 50KB 的 RTOS 干轮询就能干的活,是负优化。判据是”有没有真正的并发与阻塞需求”,不是项目大小。
二、任务与栈:分配的口径
xTaskCreate(vTaskA, "A", 128, NULL, 2, &hA); // 入口/名字/栈深(字)/参数/优先级/句柄
三个要点:
- 栈深单位是”字”不是字节:
128= 512 字节(32 位机)。栈估不准是 RTOS 崩溃第一因——printf/浮点/局部大数组都会悄悄吃掉栈。精确测量法:初始化填魔数(0xA5),运行峰值后看最低被践踏到哪(FreeRTOS 的uxTaskGetStackHighWaterMark()就是干这个的),按峰值 ×1.5 定。 - 优先级 = 抢占序:数值大优先级高(FreeRTOS 约定)。同优先级靠时间片轮转(开
configUSE_TIME_SLICING),不同优先级严格抢占。 - 任务数量不是免费的:每个任务的 TCB + 栈都是 RAM。静态分配(
xTaskCreateStatic,编译期定)比动态(堆)可预测——安全关键场景禁用堆(本站 c-restricted-environment 的”无堆策略”同款思想,emb-rtos-comm 会讲内存池)。
三、调度器:抢占、时间片与阻塞
抢占式调度的决策点:每次中断退出、任务主动让出(vTaskDelay/等信号量)时,调度器比较就绪任务里谁优先级最高,切过去。于是:
vTaskDelay(n)不是 sleep 而是”放弃 CPU n 个 tick”:期间同优先级任务能跑——高优先级任务里的vTaskDelay(1)会让出 CPU,这是”忙等转协作”的标准手法(裸机 delay 则死等)。- 时间片只在同优先级间轮转:高优先级任务就绪时立即抢占,时间片无关。
- 阻塞 = 显式等待:
xQueueReceive/xSemaphoreTake带超时参数——任务在等待期间不占 CPU(状态切到 Blocked),这是 RTOS 相对轮询的根本效率来源。 - 关中断/关调度的窗口:临界区(
taskENTER_CRITICAL())只关调度——别在临界区里调用会阻塞的 API(FreeRTOS 的断言会抓这个,但 release 关断言后就是死锁)。
调度延迟(中断到任务跑的间隔)= 中断处理 + 调度器决策 + 上下文切换——实测手段是示波器打 GPIO(中断里拉高、任务里拉低,量脉宽),这是 emb-lowpower 之外的”实时性体检”。
四、FreeRTOS 与 RT-Thread 对照
| FreeRTOS | RT-Thread | |
|---|---|---|
| 出身 | MIT,事实标准,AWS 维护 | 国产,Apache 2.0 |
| 体量 | 内核精简(几个文件) | 组件丰富(文件系统、网络栈、Shell、设备驱动框架、包管理器) |
| 哲学 | 只做内核,生态自己拼 | “一站式”——内核 + 组件 + 工具链(Env + 包管理) |
| 驱动模型 | 无(各用各的 HAL) | POSIX 风格设备驱动框架(rt_device_register/统一 open/read/ioctl) |
| 适合 | 资源紧、要深度定制、团队有经验 | 要快速出原型、组件开箱即用、团队不想拼生态 |
| 迁移成本 | 低(内核 API 小) | 中(跟着组件框架走) |
中性选型口径:8–64KB RAM 的小 MCU、只要任务/队列/信号量 → FreeRTOS;要文件系统 + 网络 + Shell + 图形全家桶、512KB+ Flash → RT-Thread 的组件省下的工时是真的。共同要求:两边都要吃透”任务/栈/优先级/临界区”这套内核概念——API 名字不同,模型同源(本站 conc 专栏讲的概念原样适用)。
五、排障索引
| 症状 | 入口 |
|---|---|
| 跑几分钟后 hardfault | 栈溢出(第二节的高水位测量);堆耗尽(动态创建没删) |
| 低优先级任务饿死 | 高优先级任务没阻塞点(while(1) 里没 delay) |
| 偶发数据错乱 | 临界区漏了;ISR 里调了非 FromISR API(FreeRTOS 的两套 API:xQueueSend vs xQueueSendFromISR) |
| 调度不及时 | 关调度窗口太长;优先级分组(emb-gpio-timer 的 NVIC)与任务优先级没对齐 |
| 移植新 MCU 痛苦 | 端口层(上下文切换汇编)问题——换官方 port,别手写 |
六、未实测清单
- 本文所有 RTOS 行为——无开发板,未测。
- 上下文切换成本——未测(本站 conc-coroutines 量过同族的协程切换 3.56ns 做量级参照)。
- FreeRTOS/RT-Thread 的具体 API 行为——以你的版本文档为准。
- 用户 Linux 开发机上的项目 RTOS——该机不可达,未涉及。