自顶向下学习RTOS第0讲-RTOS起源
1 RTOS的核心
嵌入式RTOS(实时操作系统),市面上有很多款不同的RTOS,如:
- FreeRTOS
- UCOSII
- RT-thread
- Huawei LiteOS
等等...
初次听说这么多名字不同的RTOS,容易让人眼花缭乱,难以把握它们的本质,本篇希望能从“第一性原理”出发,先提炼出RTOS的本质,然后从本质出发,分析对比不同RTOS的实现方式,进而达到完全掌握RTOS的目的,正所谓“万变不离其宗”。
归根结底,RTOS的核心:
1. 多任务
2. 任务间的通信
(不含其它协议栈如lwip/Fatfs/usb等协议栈)
RTOS正是围绕这两点进行构建的。
2 关于RTOS的几个问题
问题1:为什么要有多任务?
做个类比:人也是个多任务系统,在眼睛观察的同时,耳朵、鼻子、手、脚等可能同时在工作;同样,作为复杂一点的嵌入式系统,需要同时处理多个任务,比如手机可以边放音乐、边浏览网页、微信边在后台收消息,这都离不开多任务操作系统支持。
问题2:嵌入式多任务不用RTOS行不行?
简单的嵌入式常用前后台系统:
前后台系统:大循环 + 中断
中断在前台: 大循环在后台:
中断处理函数中触发对应的条件标记
while (1) {
条件1_IRQ(void) if (条件1)
{ {
条件1 = True; task1();
} }
条件2_IRQ(void) if (条件2)
{ {
条件2 = True; task2();
} }
条件3_IRQ(void) if (条件3)
{ {
条件3 = True; task3();
} }
... ...
}
前后台系统的优点:
简洁,明了,几乎不需要RAM/ROM的额外开销,一些简单的嵌入式系统这么做很方便。
当场景变复杂,任务比较多,前后台系统弊端也较明显,如下:
- 如果上述代码条件1、条件2、条件3都成立,只能等着task1完成后,执行task2,然后执行task3,这样就相当于批处理系统,缺点是作业周转时间长,如果task1很耗时,后面的任务只有等待,所有任务相当于同一优先级,一些关键任务实时性无法保证。
- 如果任务中有延时,cpu只能空转死等,无法切给其他任务用,cpu利用率低。
- 当任务多且任务之间依赖较多,前后台系统会很复杂,原因是:前后台系统是强迫人像机器一样顺序思考,写出顺序结构的代码,而采用RTOS则是多任务的,比较适合人类思维。
引入RTOS后:
第一个问题由时间片或者任务优先级解决;
第二个问题由系统延时解决;
第三个问题RTOS通过"虚拟"CPU,让所有任务认为自己“独占CPU”。
(实际上只有一个CPU,采用分时复用,宏观上并行,微观上还是串行,任务好像在"独占CPU")
后续章节将会解释解决方案。
3 RTOS的多任务
多任务的切换其实就是不同任务运行环境的切换。所以为了保证任务切换出去后,经过一段时间再切换回来能够继续往下执行,需要在切换之前保存当前任务的运行环境。
问题是要保存哪些东西?简单来说:保存可能被改变的东西
粗略列一下:
- cpu的寄存器(如PC,状态寄存器,数据寄存器等)---- 需要保存,因为新任务也要用
- 栈寄存器(BP与SP等)---- 需要保存,因为堆栈中保存着局部变量,属于任务私有
- 代码段,数据段等 ---- 不需要保存,因为它们不会被新任务覆盖
有时间可以分析一下C语言的函数调用,其底层其实就是栈帧切换过程,RTOS多任务切换与函数调用过程很类似:都需要保存cpu的寄存器,差别是:单任务系统函数调用只有一个调用栈(所以不需要保存),而RTOS多任务系统每个任务都需要保存独立栈空间。
RTOS多任务也采用单个栈行吗?
答案是不行,考虑多个任务如A, B, C,切换顺序可能是A->B->C->A->C->B 等,不满足栈的先入后出特性。
4 RTOS的任务间通信
多任务之间要进行通信,可以采用如下方式:
- 全局变量
- 信号量
- 消息邮箱
- 消息队列
这些通信方式都是必须的吗?它们之间差别是什么?见后续章节。