RT-Thread内核源码学习(1)–线程的定义与线程切换实现

RT-Thread内核源码学习(1)–线程的定义与线程切换实现
1 RT-Thread、uCOS家族(I/II/III)、FreeRTOS对比
常用的RTOS有国外的FreeRTOS、μC/OS、RTX和国内的RT-Thread、Huawei LiteOS和AliOS-Things等,其中尤以国外开源且免费的FreeRTOS的市场占有率最高。如今国产的RT-Thread经过10来年的发展,声势迅猛,在国产RTOS中占据鳌头。
下面,我从网上查到关于这方面的资料。
1.1 RT-Thread
RT-Thread是一款来自中国的开源嵌入式实时操作系统,由国内一些专业开发人员从2006年开始开发、维护,除了类似FreeRTOS和UCOS的实时操作系统内核外,也包括一系列应用组件和驱动框架,如TCP/IP协议栈,虚拟文件系统,POSIX接口,图形用户界面,FreeModbus主从协议栈,CAN框架,动态模块等,因为系统稳定,功能丰富的特性被广泛用于新能源,电网,风机等高可靠性行业和设备上,已经被验证是一款高可靠的实时操作系统。
RT-Thread实时操作系统遵循GPLv2+许可证,实时操作系统内核及所有开源组件可以免费在商业产品中使用,不需要公布应用源码,没有任何潜在商业风险。
RT-Thread实时操作系统核心是一个高效的硬实时核心,它具备非常优异的实时性、稳定性、可剪裁性,当进行最小配置时,内核体积可以到 3k ROM 占用、1k RAM 占用。
任务/线程调度
  在RT-Thread中线程是最小的调度单位,线程调度算法是基于优先级的全抢占式多线程调度算法,支持256个线程优先级(也能通过配置文件更改为最大支持32个或8个线程优先级),0优先级代表最高优先级,255优先级留给空闲线程使用;支持创建相同优先级线程,相同优先级的线程采用可设置时间片的轮转调度算法;调度器寻找下一个最高优先级就绪线程的时间是恒定的(时间复杂度是1,即O(1))。系统不限制线程数量的多少,只和硬件平台的具体内存相关。
任务同步机制
  系统支持semaphore(信号量)、mutex(互斥锁)作为线程间同步机制。mutex采用优先级继存方式以解决优先级翻转问题。semaphore的释放动作可安全用于中断服务例程中。同步机制支持线程按优先级等待或按先进先出方式获取信号量或互斥锁。
任务间通信机制
  系统支持event(事件)、mbox(邮箱)和MessageQueue(消息队列)等通信机制。event支持多事件“或触发”及“与触发”,适合于线程等待多个事件情况。mbox中一封邮件的长度固定为4字节,效率较MessageQueue更为高效。通信设施中的发送动作可安全用于中断服务例程中。通信机制支持线程按优先级等待或按先进先出方式获取。
时间管理
  系统使用时钟节拍来完成同优先级任务的时间片轮转调度;线程对内核对象的时间敏感性是通过系统定时器来实现的;定时器支持软定时器及硬定时器(软定时器的处理在系统线程的上下文中,硬定时器的处理在中断的上下文中);定时器支持一次性超时及周期性超时。
内存管理
  系统支持静态内存池管理及动态内存堆管理。从静态内存池中获取内存块时间恒定,当内存池为空时,可把申请内存块的线程阻塞(或立刻返回,或等待一段时间后仍未获得内存块返回。这取决于内存块申请时设置的等待时间),当其他线程释内存块到内存池时,将把相应阻塞线程唤醒。动态堆内存管理对于不同的系统资源情况,提供了面向小内存系统的小内存管理算法及大内存系统的SLAB内存管理算法。
设备管理
  系统实现了按名称访问的设备管理子系统,可按照统一的API界面访问硬件设备。在设备驱动接口上,根据嵌入式系统的特点,对不同的设备可以挂接相应的事件,当设备事件触发时,通知给上层的应用程序。
RT-Thread的架构
RT-Thread与其他RTOS相比,主要的区别在于:除了实时内核外,还具备丰富的中间组件,其架构如图1所示。
在这里插入图片描述
RT-Thread的架构包括内核层、组件与服务层和RT-Thread软件包。
内核层:RT-Thread内核是核心,包括内核系统中对象的实现(如多线程及其调度、信号量、邮箱、消息队列、内存管理、定时器等);libcpu/BSP(芯片移植相关文件/板级支持包)与硬件密切相关,由外设驱动和CPU移植构成。
组件与服务层:组件是基于 RT-Thread 内核之上的上层软件,例如虚拟文件系统、FinSH 命令行界面、网络框架、设备框架、动态模块等。采用模块化设计,做到组件内部高内聚,组件之间低耦合。
RT-Thread 软件包:运行于 RT-Thread 物联网操作系统平台上,面向不同应用领域的通用软件组件,由描述信息、源代码或库文件组成。RT-Thread 提供了开放的软件包平台,这里存放了官方提供或开发者提供的软件包,该平台为开发者提供了众多可重用软件包的选择,这也是 RT-Thread 生态的重要组成部分。软件包生态对于一个操作系统的选择至关重要,因为这些软件包具有很强的可重用性,模块化程度很高,极大的方便应用开发者在最短时间内,打造出自己想要的系统。RT-Thread 已经支持的软件包数量已经达到 60多个。具体软件包分为:
物联网相关的软件包:Paho MQTT、WebClient、mongoose、WebTerminal 等。
脚本语言相关的软件包:目前支持 JerryScript、MicroPython。
多媒体相关的软件包:Openmv、mupdf。
工具类软件包:CmBacktrace、EasyFlash、EasyLogger、SystemView。
系统相关的软件包:RTGUI、Persimmon UI、lwext4、partition、SQLite 等。
外设库与驱动类软件包:RealTek RTL8710BN SDK。
其他。
1.2 FreeRTOS
FreeRTOS是专为小型嵌入式系统设计的可扩展的实时内核。
亮点包括:
微小的封装形式。
免费的RTOS调度程序。
免费嵌入式软件源代码。
免版税。
抢占式,协作式和混合配置选项,可选时间分片。
SafeRTOS衍生产品对代码完整性提供了高度的信心。
包括一个为低功耗应用设计的tickless模式。
可以使用动态或静态分配的RAM来创建RTOS对象(任务,队列,信号量,软件定时器,互斥体和事件组)。
官方支持>30个嵌入式系统架构(以ARM7和ARM Cortex-M3为一体架构)。
FreeRTOS-MPU支持ARM Cortex-M3内存保护单元(MPU)。
设计小巧,简单易用。通常,RTOS内核二进制映像将在4K到9K字节的区域内。
可移植性非常好的源代码结构,主要用C。
支持实时任务和协同程序。
直接到任务通知,队列,二进制信号量,计数信号量,递归信号量和互斥体,用于任务之间的通信和同步,或实时任务和中断之间。
创新事件组(或事件标志)实施。
具有优先级继承的互斥体。
高效的软件定时器。
强大的执行跟踪功能。
堆栈溢出检测选项。
免费监控的论坛支持或可选的商业支持和许可。
对可创建的实时任务数量没有软件限制。
对任务优先级分配没有限制 - 可以为多个实时任务分配相同的优先级。
许多支持的架构的免费开发工具。
从标准的Windows主机开发。
1.3 uCOS家族(I/II/III)
μC/ OS-II和μC/ OS-III是抢占式,高度便携式和可扩展的实时内核。这些内核旨在便于在大量CPU架构上使用,这些内核是μC/ OS实时操作系统的关键组件。
关键价值(Key Values):
可移植性。 提供前所未有的易用性,μC/OS内核提供完整的源代码和深入的文档。 μC/ OS内核运行在大量处理器架构上,端口可供下载。
可扩展性。 μC/ OS内核允许无限制的任务和内核对象。内核的内存占用可以缩小,仅包含应用程序所需的功能,通常为6-24 KB的代码空间和1KB的数据空间。
可靠性。 μC/ OS内核包括减少开发时间的调试功能。内核提供广泛的范围检查,包括检查API调用中传递的指针,来自ISR的任务级服务,允许范围内的参数以及有效的指定选项。
高效性。 Micrium的内核还包括有价值的运行时统计信息,使您的应用程序的内部可视化。 识别性能瓶颈,并在开发周期的早期优化电源使用。
μC/ OS内核的特性包括以下亮点(Highlights):
抢占式多任务实时内核,可选择循环调度。
提供完整,干净,一致的源代码,具有深入的文档。
高可扩展性:无限数量的任务,优先级和内核对象。
同时等待多个内核对象。
直接向任务发送信号/消息。
资源高效:6K至24K字节代码空间,1K +字节数据空间)。
非常低的中断禁用时间。
广泛的性能测量指标(可配置)。
可用于关注安全型应用。
下表显示了µC/OS 多年来的演变, 比较了每个版本中可用的功能。
表1 各版本uC/OS功能
在这里插入图片描述
1.4 RTX
Keil RTX是为ARM和Cortex-M设备设计的免版税,确定性的实时操作系统。它允许您创建同时执行多个功能的程序,并帮助创建更好的结构和更容易维护的应用程序。
特征:
具有源代码的免版权,确定性RTOS。
灵活的调度:循环,抢占和协作。
具有低中断延迟的高速实时操作。
为资源有限的系统提供小封装。
无限数量的任务每个具有254个优先级。、
无限数量的邮箱,信号量,互斥量和计时器。
支持多线程和线程安全操作。
内核感知调试支持MDK-ARM。
使用μVision配置向导的基于对话框的设置。
2 创建线程
在RT-Thread中,线程是实体任务的载体,其描述一个任务执行运行环境,也描述这个任务所处优先等级。同时,线程是RT-Thread中最基本调度单位。
2.1 RT-Thread 线程组成
在RT-Thread中,线程由三部分组成:线程栈、线程函数和线程控制块。
线程栈在形式上是一段连续内存空间,RT-Thread设计者通过定义一个数组或申请一段动态内存作为线程的栈。在RT-Thread内核源码中,其定义如图2:
在这里插入图片描述
图2 RT-Thread OS对线程栈的定义
在该段源码中,线程栈其实就是一个预先定义好的全局数据,数据类型为 rt_uint8_t,大小我们设置为512。
在RT-Thread中,线程被理解为一个独立的、无限循环且不能返回的函数,系统为了顺利的调动线程,需要为每个线程都额外定义一个线程控制块,这个线程控制块就相当于线程的身份证,里面存有线程的所有信息,比如线程的栈指针,线程名称,线程的形参等。
在RT-Thread源码中,线程控制块类型声明如图3所示:
在这里插入图片描述
图3 线程控制块类型申明
在源码中,线程控制块结构体只有一些基本成员,RT-Thread规定:如果要定义线程控制块变量就使用struct rt_thread xxx的形式,定义线程控制块指针就使用rt_thread_t xxx的形式。
2.2 线程创建函数的实现
线程栈,线程的函数实体,线程的控制块最终需要联系起来才能由系统进行统一调度。那么这个联系的工作就由线程初始化函数rt_thread_init()来实现,源码如图4所示:
在这里插入图片描述
图4 rt-thread_init()函数
其中,该函数形参中,thread是线程控制块指针;entry 是线程函数名, 表示线程的入口;parameter是线程形参,用于传递线程参数; stack_start用于指向线程栈的起始地址;stack_size表示线程栈的大小,单位为字节。图4中,源码第10行rt_list_init(&(thread->tlist))表示初始化线程链表节点,RT-Thread将线程插入到各种链表中,就是通过这个节点来实现。源码第19~21行表示初始化线程栈,当线程第一次运行的时候,加载到CPU寄存器参数就放在线程栈里面。
下面就是在main函数中创建两个flag相关的线程,源码如图5所示。
在这里插入图片描述
图5创建两个flag相关的线程实例
以上主要对RT-Thread的线程定义和如何创建线程进行了梳理,创建线程包括:定义线程栈,定义线程函数,定义线程控制块和实现线程创建函数等内容。
3 实现就绪列表
3.1 定义就绪列表
线程创建好后,需要将线程添加到就绪列表里面,表示线程已经就绪,系统随时可以调度。定义线程的就绪列表源码如图6所示。
在这里插入图片描述
图6 定义就绪列表
就绪列表实际上就是一个rt_list_t类型的数组,数组的下标对应了任务的优先级,从0~RT_THREAD_PRIORITY_MAX, 在RT-Thread中,0的优先级最高。一个空的就绪列表如图7所示。
在这里插入图片描述
图7 空的就绪列表
同一优先级的线程插入到就绪列表的同一条链表中,例如:
优先级 0 — Task1-> Task2-> Task3->Task4,表示优先级0有4个任务。
优先级 1 — task1-> 2ask2,表示优先级0有2个任务。
优先级 2 — 。。。。。。
优先级 3 — 。。。。。。
。。。。。。
3.2 将线程插入到就绪列表
在RT-Thread中,线程控制块包含一个tlist成员,数据类型为rt_list_t,将线程插入到就绪列表就是通过将tlist这个节点插入到就绪列表中实现。如图8中的标红部分就是其实现代码。
在这里插入图片描述
图8 将线程插入到就绪列表
该段代表表示在线程flag1和flag2创建好后,将线程flag1和flag2分别添加到就绪列表rt_thread_priority_table[0]和rt_thread_priority_table[1]里面。
4 实现调度器
调度器是操作系统的核心,顾名思义是实现任务的调度,即线程的切换,换句话说就是从就绪列表中找到优先级最高的线程,然后执行。
4.1 RT-Thread的线程优先级管理
在RT-Thread调度器的实现中,包含了一个共256个优先级队列的数组(如果系统最大支持32个优先级,那么这里将是一个包含了32个优先级队列的数组),每个数组元素中放置相同优先级链表的表头。这些相同优先级的列表形成一个双向环形链表,最低优先级线程链表一般只包含一个idle线程。线程优先级管理如图9所示。
在这里插入图片描述
图9 RT-Thread线程优先级管理
4.2 调度器相关函数实现代码
4.2.1调度器初始化
调度器在使用前必须先初始化,以初始化系统调度器的一些全局变量。RT-Thread对线程调度器初始化化的实现源码如图10所示。
在这里插入图片描述
图10 初始化调度器函数实现
其中,源码第34行,定义了一个局部变量,且用register修饰是为了防止被编译器优化,第38~41行表示初始化线程就绪列表,初始化结束后,整个就绪列表为空,如图7就是一个为空的就绪列表。
4.2.2启动调度器
在系统完成初始化,并切换到第一个线程,要启动线程调度器,该部分源码如图11所示。
在这里插入图片描述
图11 启动调度函数实现
在调用这个函数时,它会查找系统中优先级最高的就绪态线程,然后切换过去执行。另外在调用这个函数前,必须先做idle线程的初始化,即保证系统至少能够找到一个就绪状态的线程执行。此函数是永远不会返回的。
4.2.3线程调度
调用这个函数后,系统会计算一次系统中就绪态的线程,如果存在比当前线程更高优先级的线程时,系统将切换到高优先级的线程去。上层应用程序一般不需要调用这个函数。其具体的函数实现如图12所示。
在这里插入图片描述
如图12 线程调度函数实现
该函数采用两个线程轮流切换的机制实现调度。如果当前线程为线程1,则把下一个要运行的线程改为线程2;如果当前线程为线程2,则把下一个要运行的线程改为线程1。
4.3实现代码及实验现象
4.3.1 实现代码
本部分实现两个线程的切换,源码如图13所示。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
图13 线程切换实现源码
4.3.2 实验现象
打开Deil5,采用软件仿真模式调试代码,将要观察的变量添加到逻辑分析仪,并将变量设置为Bit模式,全速运行,查看波形图如图14所示。
在这里插入图片描述
图14 线程切换波形图

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值