type
status
date
slug
summary
tags
category
icon
password

RTOS基本概念

概念(维基百科)

实时操作系统(Real-time operating system, RTOS),又称即时操作系统,它会按照排序执行、管理系统资源,并为开发应用程序提供一致的基础。
实时操作系统与一般的操作系统相比,最大的特色就是“实时性”,如果有一个任务需要执行,实时操作系统会马上(在较短时间内)执行该任务,不会有较长的延时。这种特性保证了各个任务的及时执行。

为什么需要用RTOS

之前,当我们使用裸机开发时,处理较为简单的多任务,使用传统的轮询、前后台、状态机等方式即可实现需求,但当我们遇见较为复杂的多任务时,往往完成一个任务需要较长时间,并且难以将多任务碎片化再同时进行。这时RTOS便提供了一个现成、高效的解决方案。

基本原理

RTOS通过一套基于优先级、时间触发的机制,实现了将多个繁琐任务在各自设定好的时间间隔中重复、轮流调度。当时间间隔较短时,就好似所以任务是在同时进行一样。

常用技术

任务调度
同步原语
通信机制
任务通知
具体的实现细节见技术详解。

常见RTOS类型

  1. VxWorks - 由Wind River Systems开发,广泛用于航空航天、军事和工业应用中。
  1. FreeRTOS - 一个开源的RTOS,现由亚马逊网络服务(AWS)支持,广泛用于嵌入式设备。
  1. uc/OS - 由Micrium公司开发,现属于西门子,适用于微控制器级别的应用。
  1. QNX - 由黑莓公司拥有,广泛应用于汽车和工业自动化领域。
  1. RTLinux - 基于Linux的硬实时操作系统,适合需要Linux兼容性的实时应用。
  1. Windows CE(Compact Edition) - 微软开发的嵌入式操作系统,虽然微软已停止对其主流支持,但在一些特定领域仍有使用。
  1. ThreadX - 由Microsoft Azure Sphere支持,用于物联网(IoT)和嵌入式应用。
  1. VRTX - 一个高性能的RTOS,主要应用于需要高可靠性的关键任务系统中。
  1. NuttX - 一个开源的实时操作系统,适用于嵌入式系统,如无人机和物联网设备。
  1. mbed OS - 专为物联网设备设计,由ARM公司支持。
  1. RT-Thread - 国内流行的开源RTOS,广泛应用于各种嵌入式产品中。
  1. 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
    • 任务被自己或者其他任务挂起,等待再次开启

任务状态关系图

notion image

任务优先级

每个任务在建立时都必须设定一个任务优先级,就像中断一样,高优先级的任务可以打断低优先级的任务,而具体到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,但反之是可以的。

事件组

事件组用于满足多个任务就绪,并通知多个任务执行的情况。可以选择任务启动的条件逻辑(标志位与/或),被唤醒任务也可以选择不清除任务标志位。