type
status
date
slug
summary
tags
category
icon
password
RTOS基本概念
概念(维基百科)
实时操作系统(Real-time operating system, RTOS),又称即时操作系统,它会按照排序执行、管理系统资源,并为开发应用程序提供一致的基础。
实时操作系统与一般的操作系统相比,最大的特色就是“实时性”,如果有一个任务需要执行,实时操作系统会马上(在较短时间内)执行该任务,不会有较长的延时。这种特性保证了各个任务的及时执行。
为什么需要用RTOS
之前,当我们使用裸机开发时,处理较为简单的多任务,使用传统的轮询、前后台、状态机等方式即可实现需求,但当我们遇见较为复杂的多任务时,往往完成一个任务需要较长时间,并且难以将多任务碎片化再同时进行。这时RTOS便提供了一个现成、高效的解决方案。
基本原理
RTOS通过一套基于优先级、时间触发的机制,实现了将多个繁琐任务在各自设定好的时间间隔中重复、轮流调度。当时间间隔较短时,就好似所以任务是在同时进行一样。
常用技术
任务调度
同步原语
通信机制
任务通知
具体的实现细节见技术详解。
常见RTOS类型
- VxWorks - 由Wind River Systems开发,广泛用于航空航天、军事和工业应用中。
- FreeRTOS - 一个开源的RTOS,现由亚马逊网络服务(AWS)支持,广泛用于嵌入式设备。
- uc/OS - 由Micrium公司开发,现属于西门子,适用于微控制器级别的应用。
- QNX - 由黑莓公司拥有,广泛应用于汽车和工业自动化领域。
- RTLinux - 基于Linux的硬实时操作系统,适合需要Linux兼容性的实时应用。
- Windows CE(Compact Edition) - 微软开发的嵌入式操作系统,虽然微软已停止对其主流支持,但在一些特定领域仍有使用。
- ThreadX - 由Microsoft Azure Sphere支持,用于物联网(IoT)和嵌入式应用。
- VRTX - 一个高性能的RTOS,主要应用于需要高可靠性的关键任务系统中。
- NuttX - 一个开源的实时操作系统,适用于嵌入式系统,如无人机和物联网设备。
- mbed OS - 专为物联网设备设计,由ARM公司支持。
- RT-Thread - 国内流行的开源RTOS,广泛应用于各种嵌入式产品中。
- Huawei LiteOS - 华为推出的轻量级物联网操作系统
技术详解
栈与堆
学习过计算机组成原理的朋友们应该对栈和堆不陌生。那大家应该知道,我们平时运行程序时,运行的局部变量都是保存在栈当中的,而堆则是内存空间中一片我们可以通过函数手动划分的空间。
而内存管理对于RTOS尤其重要,因为为了保持各个任务的独立性,即使我们的不同任务在调取同一个函数,函数中的变量也不能对应到同一片内存地址。并且往往我们开辟多任务时,函数会多层嵌套调用,为了保证程序在任务间跳转时,之后还能够恢复现场,RTOS会将当时得寄存器、运行地址等等保存到栈当中,多层嵌套下来,又会占用不少内存。因此,在创建任务时,我们需要指定任务所需要的内存。
FreeRTOS提供了现成的内存管理方法,有5个,通常选择heap_4
heap_1
简单内存分配,无法释放内存
heap_2
允许动态释放内存,但较为简单,会有内存碎片化问题,有效内存无法使用
heap_3
使用标准库的malloc()和free()来管理内存。
heap_4
基于heap_2,增加了内存合并功能
heap_5
heap_4的改进办,支持多块非连续内存区域分配,但复杂度高,有更高的管理开销
任务调度器
任务状态
在RTOS中,每个任务只有四种可能状态
- ready
- 任务准备好了,随时可以运行,即进入running状态
- runnin
- 任务正在运行
- blocked
- 任务正在等待某个标志或事件发生,事件发生后进入ready状态
- suspended
- 任务被自己或者其他任务挂起,等待再次开启
任务状态关系图

任务优先级
每个任务在建立时都必须设定一个任务优先级,就像中断一样,高优先级的任务可以打断低优先级的任务,而具体到RTOS中,如果当前有任意高优先级的任务在运行时,低优先级任务是在等待的。
在创建任务时,任务会按各自的优先级被存入一个任务链表组,每个优先级都有一个由此优先级任务串成的链表。
时间片轮转
对于相同优先级的任务,如果没有更高优先级的任务在运行,则采取时分复用的方式运行,RTOS给每个任务依次划分了时间片,时间片从1毫秒到几十毫秒不等,也可能更长。
RTOS优先级的监测间隔为1个tick,一般为1ms或10ms,每个tick,RTOS都会自高优先级到低优先级检查任务链表,当检查到一个高优先级的链表中由准备好的任务时,便开始遍历这个链表上的任务,让它们在各自的时间片上运行。
空闲任务
RTOS自带一个优先级为0的任务,即优先级最低的任务,通常用于回收已经结束的任务的内存占用。为了让空闲任务可以被运行到,我们在编写代码时应当注意每个任务都不能总是占用运行时间,需要预留空闲时间,不然最终可能会导致程序内存不够而出现问题。
空闲任务可以被添加一个钩子函数,空闲任务每执行一次,就调用一次钩子函数,它可以用来运行一些后台函数,计算系统空闲时间或者进入省电状态。
任务创建函数
同步与互斥
在执行多任务时,我们常常会遇见同一个资源两个任务都想访问的情况,这种时候,必然只有一个任务可以访问,另一个任务无法访问,这叫互斥;因此另一个任务只有等到资源没有被使用时,接收到事件提示才能访问,这个过程就叫同步。
想要实现同步与互斥,只使用全局变量来做资源空闲标志是不行的,因为RTOS中的任务切换时以汇编语句为最小逻辑单元的,所以在判断标志位或者修改标志位的过程中,如果任务被切换了依然会出现两个任务同时访问的情况。
对此,RTOS提供了多种解决方式,如队列、事件组、信号量、任务通知、互斥量。
这些方法的有一个共性,当同时有多个任务执行操作时(读/写),会选择最高优先级的任务,如果优先级都相同,则选择被阻塞事件更长的任务。
各个解决方式的特点
方法 | 使用场景 |
队列 | 数据传递 |
信号量 | 状态通知,或者表示资源数量 |
互斥量 | 状态通知,但是解锁只能由当时上锁的任务实现,常用于串口 |
任务通知 | 用于通知指定的任务,效率高 |
事件组 | 有广播作用,可同时唤醒所有符合条件的任务,被唤醒任务可选择不清除事件 |
队列
队列即我们熟知的FIFO的数据结构。
在RTOS中,可使用队列来在任务和任务/中断之间传递数据,其依然遵从FIFO,写或者读某个队列的任务如果同时有多个,那么都会允许高优先级的任务读写。
用来写/读队列的函数,如果队列为满/空则都会使任务进入Blocked状态,直到队列可以被执行操作才唤醒任务,并且都可以设置最长等待时间。
信号量
用于任务通知,感觉可以认为是我们裸机开发时常用得全局标志位,只是加上了多任务下的blocked等待功能,可以用来当任务之间得计数器或者状态标志位。
其中,计数上限为1的被称为二进制信号量,常用于当作标志位。
互斥量
同样用于任务通知,不同于信号量,它的解锁只能由当时上锁的任务自身解开。常用于使用时不能被中断的外设,比如串口,或者使用全局/静态变量,不想被打断的变量修改。
在使用互斥量时,还又可能出现优先级反转的情况,如果优先级较低的任务锁了某个互斥量,而一个优先级高的任务希望得到互斥量,这时前者的优先级就会被暂时抬高到后者的等级。
然而,值得注意的是,学过数据库的朋友们应该了解到,这里存在一个死锁的问题,也就是说如果存在两个任务A、B,它们同时分别锁住了互斥量C、D,但A、B又希望得到对方的互斥量才能运行,这样就会无休止地等待下去,这就是死锁。
为了解决这个问题,RTOS提供了递归锁。
任务通知
任务通知,顾名思义就是起到通知任务的功能,但与前面提到的信号量不同,任务通知的发出方是知道通知的是谁的,并且任务通知的效率非常高,但无法广播,而且不能发送给中断ISR,但反之是可以的。
事件组
事件组用于满足多个任务就绪,并通知多个任务执行的情况。可以选择任务启动的条件逻辑(标志位与/或),被唤醒任务也可以选择不清除任务标志位。