RTOS
一、什么是RTOS?
RTOS(Real-Time Operating System,实时操作系统)是一种专门为“必须在规定时间内完成指定任务”而设计的操作系统。它像一个严格的时间管家,确保每个任务在截止时间前被执行,而不是“尽量快”地执行。
1.1 RTOS vs 普通操作系统
| 对比项 | 普通操作系统(如Windows、Linux) | RTOS(如FreeRTOS、VxWorks) |
|---|---|---|
| 核心目标 | 提高平均吞吐量,让多个程序公平使用CPU | 保证每个关键任务在截止时间前完成 |
| 响应时间 | 不确定,可能因为后台任务延迟几秒 | 可预测,通常在微秒级响应中断 |
| 调度策略 | 时间片轮转、优先级(但可能被高负载影响) | 基于优先级抢占,高优先级任务立即执行 |
| 典型应用 | 办公软件、网页浏览、游戏 | 汽车发动机控制、医疗设备、工业机器人 |
1.2 实时系统分类
| 类型 | 定义 | 举例 |
|---|---|---|
| 硬实时 | 错过截止时间会导致灾难性后果 | 安全气囊弹开、火箭姿态控制 |
| 软实时 | 错过截止时间仅影响体验,无致命后果 | 视频播放卡顿、鼠标移动延迟 |
二、RTOS系统架构
RTOS采用分层架构设计,从下至上依次为:
- 硬件层:微控制器(CPU、内存、外设)
- 硬件抽象层(HAL):CPU/外设接口封装
- RTOS内核:任务调度器、任务间通信、内存管理、定时器
- 组件层/中间件:文件系统(FATFS)、网络协议栈(LwIP)、设备驱动框架
- 应用层:用户任务(读取传感器、控制算法、发送数据等)
三、核心机制
3.1 任务状态迁移
任务在RTOS中有四种基本状态:
- 运行态:正在使用CPU
- 就绪态:准备就绪,等待CPU调度
- 阻塞态:等待事件(延时、信号量、队列)
- 挂起态:被人工暂停,需要手动恢复
状态迁移关系:
- 就绪态 → 运行态:调度器选择
- 运行态 → 就绪态:被抢占/时间片用完
- 运行态 → 阻塞态:等待事件
- 阻塞态 → 就绪态:事件到达
- 运行态 → 挂起态:调用vTaskSuspend
- 挂起态 → 就绪态:调用vTaskResume
3.2 调度策略对比
| 调度策略 | 工作原理 | 特点 | 适用场景 |
|---|---|---|---|
| 优先级抢占式调度 | 高优先级任务可打断低优先级任务 | 实时性强,保证紧急任务优先 | 大多数RTOS默认策略 |
| 时间片轮转 | 同优先级任务轮流运行固定时间片 | 公平性好,防止任务饥饿 | 同优先级任务较多的场景 |
| 协作式调度 | 任务主动让出CPU才切换 | 简单,无抢占开销 | 任务简单、可控性高的系统 |
3.3 上下文切换流程
- 任务A正在运行
- 触发PendSV异常(任务切换)
- 硬件自动保存部分寄存器到A的堆栈
- 软件手动保存剩余寄存器到A的堆栈
- 更新堆栈指针,指向任务B的堆栈
- 软件手动恢复任务B的寄存器
- 硬件自动恢复任务B的剩余寄存器
- 任务B开始运行
四、任务间通信(IPC)
4.1 IPC机制对比表
| 通信机制 | 核心作用 | 典型应用场景 | 类比 |
|---|---|---|---|
| 队列 | 任务间传递数据 | 传感器数据采集与处理 | 像一个管道,放消息和取消息 |
| 信号量 | 任务间传递信号 | ISR唤醒处理任务 | 像一个旗帜,表示”某事发生了” |
| 互斥量 | 保护共享资源 | 防止全局变量同时访问 | 像一个钥匙,拿钥匙才能进厕所 |
| 事件标志组 | 任务等待多个条件 | 等待GPS和网络都就绪 | 像一个任务清单 |
4.2 优先级反转与继承
优先级反转场景:
- 低优先级任务A获得共享资源锁
- 高优先级任务C就绪,请求同一资源被阻塞
- 中优先级任务B就绪,抢占A运行
- 结果:高优先级C被无关的中优先级B阻塞
解决方案:优先级继承
- 当C被A阻塞时,A临时继承C的高优先级
- A快速运行完并释放资源
- A恢复原优先级
- C获得资源继续运行
sequenceDiagram
participant H as 高优先级任务C
participant M as 中优先级任务B
participant L as 低优先级任务A
participant R as 共享资源
Note over L,R: 步骤1:A获得资源锁
L->>R: 获取锁(正在使用资源)
Note over H: 步骤2:C就绪,抢占A
H->>H: 开始运行
H->>R: 请求资源锁(被A持有)
R-->>H: 锁不可用,C进入阻塞
Note over M: 步骤3:B就绪,抢占A
M->>M: 开始运行(B优先级高于A)
Note over H: 此时C被B间接阻塞
优先级反转发生
Note over L: 步骤4:优先级继承
L->>L: 临时继承C的优先级
快速运行释放锁
L->>R: 释放锁
R-->>H: 锁可用,C就绪
H->>H: 抢占B,获取锁运行
4.3 死锁必要条件
死锁发生的四个必要条件:
| 条件 | 说明 |
|---|---|
| 互斥条件 | 资源一次只能被一个任务使用 |
| 持有并等待 | 任务持有资源同时等待其他资源 |
| 不可剥夺 | 资源只能由持有者主动释放 |
| 循环等待 | 存在任务-资源的环形链 |
避免方法:
- 固定加锁顺序
- 使用超时等待机制
- 避免在持有锁时申请其他锁
五、内存与中断管理
5.1 FreeRTOS内存管理算法
| 算法 | 特点 | 适用场景 |
|---|---|---|
| heap_1 | 只能申请不能释放 | 系统启动后不再释放内存 |
| heap_2 | 最佳匹配,可能产生碎片 | 频繁申请释放但大小固定 |
| heap_4 | 合并相邻空闲块,减少碎片 | 最常用,通用场景 |
5.2 ISR(Interrupt Service Routine 中断服务程序)设计原则
| 禁止行为 | 原因 | 正确做法 |
|---|---|---|
| 调用阻塞型API | 会导致系统响应延迟 | 只释放信号量或发消息 |
| 执行耗时操作 | 阻塞其他中断和任务 | 快进快出,唤醒任务处理 |
| 使用不可重入函数 | 可能导致数据错乱 | 使用线程安全的函数 |
六、主流RTOS选型
| RTOS名称 | 许可证 | 典型应用领域 | 核心优势 |
|---|---|---|---|
| FreeRTOS | MIT | STM32、ESP32等MCU | 开源免费、社区活跃,行业标准 |
| RT-Thread | Apache 2.0 | 物联网、消费电子 | 国产开源,组件丰富(GUI/网络) |
| Zephyr OS | Apache 2.0 | 新兴物联网 | Linux背书,模块化设计 |
| VxWorks | 商业 | 航天航空、军工 | 高可靠性,通过安全认证 |
| QNX | 商业 | 汽车电子、医疗 | 微内核架构,高度安全 |
七、面试高频考点速查表
| 考点 | 核心问题 | 关键回答要点 |
|---|---|---|
| RTOS vs 裸机 | 有什么区别? | 裸机是顺序循环,RTOS是多任务抢占 |
| 任务状态 | 有哪几种?如何迁移? | 运行/就绪/阻塞/挂起 |
| 上下文切换 | 切换时发生了什么? | 保存寄存器到堆栈,恢复新任务寄存器 |
| 调度算法 | RTOS用什么调度? | 优先级抢占 + 时间片轮转 |
| 优先级反转 | 是什么?如何解决? | 中优先级阻塞高优先级,优先级继承解决 |
| 死锁 | 四个必要条件? | 互斥/持有等待/不可剥夺/循环等待 |
| ISR注意事项 | ISR中不能做什么? | 不能阻塞、不能耗时操作 |
| 任务饥饿 | 系统卡死可能原因? | 高优先级任务一直运行不阻塞 |
八、项目经验
根据modem运行特点,我们内部做了一个专用的嵌入式RTOS,我们的业务逻辑都是在这个嵌入式操作系统上面运行(有些早期的产品是用Linux裁剪)。
Radio Setup的segment设定的build和execute时间优化的过于严格,导致少部分板子出现稳定性问题,概率性的crash。加入时间戳表格信息在校准的xml log中,超过设定时间的报fail并且不继续执行后续的流程不crash在tech侧,通过给部分bands的corner case重新优化build和execute的时间分配来解决。