RT-Thread简介及启动流程分析

阅读引言: 最近在学习RT-Thread的内部机制,觉得这个启动流程和一些底层原理还是挺重要的, 所以写下此文。

目录

1, RT-Thread简介

2,RT-Thread任务的几种状态

3, 学习资源推荐

4, 启动流程分析开始


1, RT-Thread简介

RT-Thread是一个来自中国的开源、中英文双语的实时操作系统(RTOS),它适用于各种资源受限的嵌入式系统。自2006年由熊谱翔(Bernard Xiong)创建以来,RT-Thread已经发展成为一个功能丰富、高度可伸缩、完全开源的实时操作系统。

以下是RT-Thread的一些主要特性:

  1. 实时性能:RT-Thread提供了高实时性能,能够满足嵌入式系统对实时性的需求。

  2. 可伸缩性:系统设计为可伸缩,可以运行在从几百字节内存的简单嵌入式设备到拥有大量内存的复杂系统。

  3. 组件丰富:RT-Thread拥有丰富的组件库,包括文件系统、TCP/IP网络协议栈、设备驱动框架等。

  4. 易用性:RT-Thread提供了简洁明了的API设计,易于学习和使用。

  5. 开源社区:拥有活跃的开源社区,众多开发者和公司贡献代码和组件。

  6. 跨平台:支持多种处理器架构,如ARM Cortex-M、Cortex-R、Cortex-A系列,以及MIPS、x86、XTensa等。

  7. 工具链支持:与多种编译器和集成开发环境(IDE)兼容,如Keil、IAR、GCC等。

  8. 软件包生态:RT-Thread Studio提供了丰富的软件包管理,方便开发者快速集成和使用。

  9. 文档和教程:提供详细的文档和教程,帮助开发者快速上手。

  10. 商业友好:RT-Thread的BSD许可协议非常友好,允许商业和个人用户免费使用和修改。

RT-Thread适用于各种嵌入式应用场景,包括智能家居、智能穿戴、智能安防、工业自动化、车载设备等。它的设计目标是为嵌入式开发提供稳定、高效、易用的解决方案。

2,RT-Thread任务的几种状态

状态特点
初始状态创建完任务还未开启任务调度器, 此时任务就属于此种状态
就绪状态启动调度器后, 任务属于此种状态
挂起(阻塞)任务因为等待某种资源而阻塞
运行运行
关闭线程退出

  • 初始状态

当线程刚开始创建还没开始运行时就处于初始状态:
 使用rt_thread_init()创建,但是未调用rt_thread_startup使它就绪
 使用rt_thread_create()创建,但是未调用rt_thread_startup使它就绪

  • 就绪状态

这个线程完全准备好了,随时可以运行:只是还轮不到它:这时它就处于就绪态(Ready)。
在下面几种情况下,线程都处于就绪状态:
 我们创建线程后,使用rt_thread_startup()函数使它进入就绪态。
 它在运行过程中,被更高优先级的线程抢占了,这时它处于就绪状态。
 它在运行过程中,轮到同优先级的线程运行了,这时它处于就绪状态。
 它因为等待某些资源而没有运行,别的线程或者中断函数把它唤醒了,这时它处于就绪状态。

  • 运行状态

当处于就绪状态的线程运行时,它就处于运行状态。

  • 挂起状态

在日常生活的例子中,母亲在电脑前跟同事沟通时,如果同事一直没回复,那么母亲的工作就被卡住了、被堵住了、处于挂起状态。
重点在于:母亲在等待

在RTOS中创建多个任务后, 如果别的这些任务中优先级最高的任务(线程)在循环执行过程中不调用休眠相关的函数让出CPU的资源, 那么比它优先级低的任务根本呢不会执行。


在实际产品中,我们不会让一个线程一直运行,而是使用"事件驱动"的方法让它运行:
 线程要等待某个事件,事件发生后它才能运行
 在等待事件过程中,它不消耗CPU资源
 在等待事件的过程中,这个线程就处于挂起状态, 就是阻塞状态


在挂起状态的线程,它可以等待两种类型的事件:
 时间相关的事件
 可以等待一段时间:我等2分钟
 也可以一直等待,直到某个绝对时间:我等到下午3点
 同步事件:这事件由别的线程,或者是中断程序产生
 例子1:线程A等待线程B给它发送数据
 例子2:线程A等待用户按下按键
 同步事件的来源有很多(这些概念在后面会细讲):
 信号量(semaphores)
 互斥量(mutexe)
 事件集(event)
在等待一个同步事件时,可以加上超时时间。比如等待队里数据,超时时间设为10ms:
 10ms之内有数据到来:成功返回
 10ms到了,还是没有数据:超时返回

  • 关闭状态

当线程运行结束时,将处于关闭状态:
 可由运行状态正常退出,进入关闭状态
 或者通过线程删除函数进入关闭状态
 rt_err_t rt_thread_detach(),用来删除使用rt_thread_init()创建的线程
 rt_err_t rt_thread_delete(),用来删除使用rt_thread_create()创建的线程
在进入关闭状态时,线程所占据的资源(比如栈)不会立即释放,需等到空闲进程运行时才能清理。在这两个删除任务的函数内部实现是将任务控制块从不同的状态链表中进行搬移, 实际在回收任务控制块和任务栈的任务是在操作系统的空闲任务中做的。

3, 学习资源推荐

 FreeRTOS

RT-Thread

没有系统学习过RTOS的兄弟建议去学第一个, 正点原子推出的FreeRTOS教程, 讲得很仔细, 很底层。

4, 启动流程分析开始

追一下代码: 这里简单补充一下一般mcu的启动流程, 一般在固化程序运行完成之后就到了启动代码, 不太明白为什么mcu启动的第一个处理的函数(符号)是复位处理的可以去看我之前写的这篇文章: http://t.csdnimg.cn/DUkKK

Reset_Handler: 

; Reset handler
Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]   @导出Reset_Handler符号, 别的源文件可见
                IMPORT  __main                             @导入编译器生成的__main引导函数
                IMPORT  SystemInit                         @导入SystemInit函数符号
                LDR     R0, =SystemInit                    @初始化时钟等
                BLX     R0               
                LDR     R0, =__main
                BX      R0
                ENDP

mcu上电之后执行完固化的引导程序之后, 会根据拨码开关的指示来存储器中取出指令执行, 执行的指令的开始是启动代码, 启动代码中的第一个函数符号是复位处理, 在复位处理中调用到了SystemInit函数, 接下来到了SystemInit函数了。

SystemInit:

/**
  * @brief  Setup the microcontroller system
  *         Initialize the Embedded Flash Interface, the PLL and update the 
  *         SystemCoreClock variable.
  * @note   This function should be used only after reset.
  * @param  None
  * @retval None
  */
void SystemInit (void)
{
  /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
  /* Set HSION bit */
  RCC->CR |= 0x00000001U;

  /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#if !defined(STM32F105xC) && !defined(STM32F107xC)
  RCC->CFGR &= 0xF8FF0000U;
#else
  RCC->CFGR &= 0xF0FF0000U;
#endif /* STM32F105xC */   
  
  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= 0xFEF6FFFFU;

  /* Reset HSEBYP bit */
  RCC->CR &= 0xFFFBFFFFU;

  /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
  RCC->CFGR &= 0xFF80FFFFU;

#if defined(STM32F105xC) || defined(STM32F107xC)
  /* Reset PLL2ON and PLL3ON bits */
  RCC->CR &= 0xEBFFFFFFU;

  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x00FF0000U;

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000U;
#elif defined(STM32F100xB) || defined(STM32F100xE)
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000U;

  /* Reset CFGR2 register */
  RCC->CFGR2 = 0x00000000U;      
#else
  /* Disable all interrupts and clear pending bits  */
  RCC->CIR = 0x009F0000U;
#endif /* STM32F105xC */
    
#if defined(STM32F100xE) || defined(STM32F101xE) || defined(STM32F101xG) || defined(STM32F103xE) || defined(STM32F103xG)
  #ifdef DATA_IN_ExtSRAM
    SystemInit_ExtMemCtl(); 
  #endif /* DATA_IN_ExtSRAM */
#endif 

#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif 
}

建议各位先看一些大型工程代码的时候使用SourceInsight。总结一下Systeminit函数做了啥, 初始化Flash, 系统时钟。

RTOS的启动, 需要在特定的编译环境下才能成功, 举个例子

在绝大多数的编程语言中, 数字、字母、下划线是构成字符串的要求, 这种写法防到标准C语言语法中肯定就报错了, 只是在嵌入式C语言中, 可以根据不同的平台, 不同的应用场景将编译器修改配套, 这样就不会报错了。

在复位处理中, 当SystemInit执行完之后, 会接着调用编译器提供的__main引导函数, 找到函数的main函数入口, 从而去执行用户代码。在这里有一点特殊的是, __main引导找到的函数是 $Sub$$main, 所有接下来我们该去分析一下 $Sub$$main做了什么了。

int rtthread_startup(void)
{
    rt_hw_interrupt_disable();

    /* board level initialization
     * NOTE: please initialize heap inside board initialization.
     */
    rt_hw_board_init();

    /* show RT-Thread version */
    rt_show_version();

    /* timer system initialization */
    rt_system_timer_init();

    /* scheduler system initialization */
    rt_system_scheduler_init();

#ifdef RT_USING_SIGNALS
    /* signal system initialization */
    rt_system_signal_init();
#endif

    /* create init_thread */
    rt_application_init();

    /* timer thread initialization */
    rt_system_timer_thread_init();

    /* idle thread initialization */
    rt_thread_idle_init();

    /* start scheduler */
    rt_system_scheduler_start();

    /* never reach here */
    return 0;
}

可以看到在这个函数中还是做了蛮多事情的, 但是主要的就两个函数。

看一下这两个源文件的实现

可以看到比较核心的是创建了一个主线程, 要不先去看看这个主线程干了啥?

可以看到, 在初始化一些系统组件之后, 调用到了我们的main函数。

具体任务切换是如何切的就要看cpu架构了, 本质通过一些链表, 定时器, 找到需要运行的任务, 将正在运行的任务和需要运行的任务进行一次上下文切换。

到此, RT-Thread启动流程就大致的分析完成了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/717446.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[element-ui]el-select多选选择器选中其中一个选项,不可删除

背景: 产品真的很多奇奇怪怪的需求,一边吐槽一边实现。 前提:选择器作为表格的筛选项,提供三个选项值。 要求:默认选中其中一个值,这个值不可删除。 如图: 小声吐槽:搞这些有什么…

Mobaxterm 配置 ssh 隧道

背景介绍: 在使用 ssh远程 连接服务器时,由于许多服务器并没有公网ip,或者不能从内部直接访问,经常使用 跳板机端口转发 的形式访问服务器。 但是在实际使用中,我们经常会有些网络和数据交换操作,需要用到…

【刷题】LeetCode刷题汇总

目录 一、刷题题号1:两数之和 二、解法总结1. 嵌套循环2. 双指针 一、刷题 记录LeetCode力扣刷题 题号1:两数之和 双循环(暴力解法): class Solution {public int[] twoSum(int[] nums, int target) {int[] listne…

仪表运放输入端抗RFI滤波器设计注意事项

1 概述 有个潜在问题却往往被忽视,即仪表放大器中存在的射频整流问题。当存在强射频干扰时,集成电路的内部结点可能对干扰进行整流,然后以直流输出失调误差表现出来; 2 共模和差模输入滤波器 该滤波器针对CM(R1-C1和R2-C2)&#…

快去复习吧+++常用算法及参考算法 递推法++穷举法++排序(冒泡、选择)++查找(顺序、折半)++字符串处理++方程求根++无穷级数求和

接上:常用算法及参考算法 (1)累加 (2)累乘 (3)素数 (4)最大公约数 (5)最值问题 (6)迭代法 常用算法及参考算法 7. 递推法…

vite配置之获取.env.[mode]下的数据

需求 vite.config.ts获取配置文件下面的数据.vue,.ts,.tsxsrc文件夹下面获取配置文件下面的数据 一、src/* .vue,.ts,.tsx 文件夹下面使用环境变量 之前webpack或者用的vue-cli我们在获取配置文件数据的时候通过process.env,但是在vite里面不能通过这种方式 vit…

论文《Dual-Contrastive for Federated Social Recommendation》阅读

论文《Dual-Contrastive for Federated Social Recommendation》阅读 论文概况MotivationMethodologyClient Local ComputingCenter Server Aggregation 总结 今天简单总结一下一篇关于联邦推荐方面的论文《Dual-Contrastive for Federated Social Recommendation》&#xff0c…

【Esp32连接微信小程序蓝牙】附Arduino源码《 返回10007 相同特征id冲突问题》

前言 最近接了一个外包,发现了esp32连接小程序会有很多bug,所以接下来会慢慢更新解决方案,还是需要多接触项目才能进步呀兄弟们! 附上uuid的生成链接: // See the following for generating UUIDs: // https://www.uu…

Minillama3->训练tokenizer

GitHub - charent/ChatLM-mini-Chinese: 中文对话0.2B小模型(ChatLM-Chinese-0.2B),开源所有数据集来源、数据清洗、tokenizer训练、模型预训练、SFT指令微调、RLHF优化等流程的全部代码。支持下游任务sft微调,给出三元组信息抽取微调示例。中文对话0.2B小模型(ChatLM-Chi…

Peewee,一个既小巧又强大的 Python 库-轻松实现数据库的增删改查

目录 01初识 Peewee 为什么选择 Peewee? 02安装与配置 安装 Peewee 配置 Peewee 03定义模型 定义简单模型 定义复杂模型 04基本操作 创建记录 查询记录 更新记录 删除记录 05高级操作 复杂查询 事务处理 使用信号 模型迁移 06实战案例 简单博客系统 任务管…

C语言最终文章-二叉树

文章目录 前言二叉树的性质二叉树的存储方式顺序存储堆及其应用TopK问题堆排序 链式存储二叉树的练习1.二叉树查找值为x的节点2.判断是否为完全二叉树LC226.翻转二叉树[LC572. 另一棵树的子树](https://leetcode.cn/problems/subtree-of-another-tree/description/)两道选择题 …

python操作注册表没有权限(error:5拒绝访问)

在IDE中运行 1. Openkey( , , accesswinreg.KEY_ALL_ACCESS) 2. 管理员方式运行Vscode或PyCharm 如果要打包成应用呢? 怎么处理权限问题?

Python 循环语句

在Python当中,循环语句用于重复执行特定的代码块,知道某个条件不再满足为止。Python中常用的循环有两种:for 循环 和 while 循环,下面我会分别详细解释它们的用法和特点 for 循环 for循环用于遍历可迭代对象(iterable)&#xff0…

522. 最长特殊序列 II

题目 给定字符串列表 strs ,返回其中最长的特殊序列的长度。如果最长特殊序列不存在,返回 -1。 特殊序列定义如下:该序列为某字符串独有的子序列(即不能是其他字符串的子序列)。 字符串 s 的子序列可以通过删去字符…

学习笔记——网络管理与运维——SNMP(基本配置)

四、SNMP基本配置 1、SNMP配置举例 整个华为数通学习笔记系列中,本人是以网络视频与网络文章的方式自学的,并按自己理解的方式总结了学习笔记,某些笔记段落中可能有部分文字或图片与网络中有雷同,并非抄袭。完处于学习态度&#x…

PaddleOCR学习——PP-OCR系列

相关知识前置: PP-LCNet PP-LCNetV3 PP-LCNetV3系列模型是PP-LCNet系列模型的延续,覆盖了更大的精度范围,能够适应不同下游任务的需要。PP-LCNetV3系列模型从多个方面进行了优化,提出了可学习仿射变换模块,对重参数…

人脸识别系统---年龄预测

一、预测年龄 1.加载预训练的人脸检测模型 face_cascade cv2.CascadeClassifier(haarcascade_frontalface_default.xml)2.加载预训练的性别和年龄识别模型 gender_net cv2.dnn.readNetFromCaffe(deploy_gender.prototxt, gender_net.caffemodel) age_net cv2.dnn.readNet…

Qwen-VL图文多模态大模型LoRA微调指南

大模型相关目录 大模型,包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步,扬帆起航。 大模型应用向开发路径:AI代理工作流大模型应用开发实用开源项目汇总大模…

数据可视化实验二:回归分析、判别分析与聚类分析

目录 一、使用回归分析方法分析某病毒是否与温度呈线性关系 1.1 代码实现 1.2 线性回归结果 1.3 相关系数验证 二、使用判别分析方法预测某病毒在一定的温度下是否可以存活,分别使用三种判别方法,包括Fish判别、贝叶斯判别、LDA 2.1 数据集展示&am…

软件改为开机自启动

1.按键 win R,输入“shell:startup”命令, 然后就可以打开启动目录了,如下: 2.然后,把要开机启动的程序的图标拖进去即可。 参考:开机启动项如何设置