文章目录
- 前置说明
- RTThread框架
- 线程管理及调度器
- 线程间通信管理
- 时钟管理
- 内存分布
- 内存管理
-
- 动态内存堆管理
- 静态内存池管理
- RTT启动过程
- 自动初始化机制
- 内核对象管理架构
- 内核配置&剪裁
前置说明
一般单片机一般只有一个核心,做多线程实际上是 分时复用 CPU
,是并发的。线程通常是指操作系统上的概念,而本文介绍的实时操作系统 RTThread
的线程个人感觉应该称之为 任务 更合理,但为了和官方说法一致,以下仍叫线程。
主要参考: RTThread 内核基础
RTThread框架
与其他 RTOS
不同,RTThread
不仅仅是一个实时内核,它在努力构造一套开发生态。
其完整版框架如下:
当然这里我们仅学习 RTThread 内核
部分,同时本文大部分内容点到浅尝辄止,主要是对RTThread
有一个大体的印象。
线程管理及调度器
RTT
的本质是 基于优先级的全抢占式多线程调度系统,在该实时系统中,线程是最小的调度单位。
即在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的,包括线程调度器自身。
注意:RT-Thread 中,实际上线程并不存在运行状态,就绪状态和运行状态是等同的。
RTT
最多支持 256
个线程优先级,0
优先级代表最高优先级,最低优先级留给空闲线程使用。
同时它也支持创建多个具有相同优先级的线程,相同优先级的线程间采用 时间片轮转调度算法 进行调度,使每个线程运行相应时间。
线程间通信管理
- 线程同步
RT-Thread
采用 信号量、互斥量 与 事件集 实现线程间同步。线程通过对信号量、互斥量的获取与释放进行同步。 - 线程通讯
RT-Thread
支持 邮箱 和 消息队列 等通信机制。邮箱和消息队列的发送动作可安全用于中断服务例程中。通信机制支持线程按优先级等待或按先进先出方式获取。
时钟管理
任何操作系统都需要提供一个时钟节拍,以供系统处理所有和时间有关的事件,如线程的延时、线程的时间片轮转调度以及定时器超时等。
时钟节拍是特定的 周期性中断,这个中断可以看做是 系统心跳,中断之间的时间间隔取决于不同的应用,时钟节拍率越快,系统的额外开销就越大,从系统启动开始计数的时钟节拍数称为系统时间。
RT-Thread
的时钟管理以时钟节拍为基础,时钟节拍是 RT-Thread
操作系统中最小的时钟单位。
RT-Thread
的定时器提供两类定时器机制:
- 单次触发定时器
这类定时器在启动后只会触发一次定时器事件,然后定时器自动停止。 - 周期触发定时器
这类定时器会周期性的触发定时器事件,直到用户手动的停止定时器否则将永远持续执行下去。
通常使用定时器定时 回调函数(即超时函数),完成定时服务。用户根据自己对定时处理的实时性要求选择合适类型的定时器。
内存分布
首先看一下 RT-Thread Studio
中 编译完生成的 elf
文件:
-
text
代码段,用来存放 代码 及一些 只读常量,一般是只读的区域。 -
data
数据段,用来存放全局初始化变量,以及全局或局部 静态变量。 -
bss
BSS 段,用来存放所有 未初始化的数据,用0
来初始化。 -
dec
是decimal
即十进制的缩写,是text
,data
和bss
的算术和。 -
hex
是hexadecimal
即十六进制的缩写。 -
filename
编译生成的目标文件名,本例中即为:rtthread.elf
。
一般 MCU
包含的存储空间有:片内 Flash
与片内 RAM
。
RAM 相当于内存,Flash 相当于硬盘。
-
FLASH(烧录程序所占的Flash大小) =
text
+data
-
RAM (运行时占用的RAM大小) =
data
+bss
内存管理
动态内存堆管理
动态内存堆是根据我们开发需求分配任意大小的内存块。既然是堆,那么就要我们自己申请及释放。当不需要再使用这些内存块时,需要释放回堆中供其他应用分配使用,否则会造成 内存泄漏。
RTT为我们提供了三种动态内存堆的调度算法:
- 小内存管理算法
- slab算法
- memheap 管理算法
静态内存池管理
内存堆管理器非常灵活和方便,但是会产生以下两个问题:
- 分配效率不高,在每次分配时,都要空闲内存块查找。
- 容易产生内存碎片。
为了提高内存分配的效率,并且避免内存碎片,RT-Thread
提供了另外一种内存管理方法:内存池(Memory Pool)。
注意:内存池是 静态变量,其存放位置为 data
段。
当静态内存池具有可用内存时,系统对内存块分配的时间将是恒定的。
当静态内存池为空时,系统将申请内存块的线程挂起或阻塞掉 (即线程等待一段时间后仍未获得内存块就放弃申请并返回,或者立刻返回。
等待的时间取决于申请内存块时设置的等待时间参数),当其他线程释放内存块到内存池时,如果有挂起的待分配内存块的线程存在的话,则系统会将这个线程唤醒。
ps:内存池的线程挂起功能非常适合需要通过内存资源进行同步的场景。
RTT启动过程
自动初始化机制
在函数定义处通过宏定义的方式进行申明,就会在系统启动过程中被执行。
使用该方法,main()
函数会非常简洁。
内核对象管理架构
RT-Thread
采用内核对象管理系统来访问 / 管理所有内核对象,内核对象包含了内核中绝大部分设施,这些内核对象可以是静态分配的静态对象,也可以是从系统内存堆中分配的动态对象。
通过这种内核对象的设计方式,RT-Thread
做到了不依赖于具体的内存分配方式,系统的灵活性得到极大的提高。对象容器中包含了每类内核对象的信息,包括对象类型,大小等。对象容器给每类内核对象分配了一个链表,所有的内核对象都被链接到该链表上。
推荐:rt-thread的内核对象管理系统分析
内核配置&剪裁
RT-Thread
的一个重要特性是高度可裁剪性,支持对内核进行精细调整,对组件进行灵活拆卸。
配置主要是通过修改工程目录下的 rtconfig.h
文件来进行,用户可以通过打开 / 关闭该文件中的 宏定义 来对代码进行条件编译,最终达到系统配置和裁剪的目的。
另外,env
– menuconfig
是个好东西。
注意:在实际应用中,系统配置文件 rtconfig.h
是由配置工具自动生成的,无需手动更改。