Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
2024-RSOC
【2024-RSOC】RT-Thread线程以及启动机制
发布于 2024-07-23 23:50:49 浏览:770
订阅该版
[tocm] # 内核启动以及线程的概念 运行环境:Vscode+env 开发板:星火一号 ## 一、目录结构 先分析一下工程的目录结构 ![image-20240723190958235](https://raw.githubusercontent.com/CYFS3/Typroa/main/202407232323657.png) | pycache | 存放Python 编译器生成的字节码文件 | | :----------: | :-------------------------------: | | .vscode | 存放vscode配置文件 | | applications | 应用层 | | board | 板级初始化 | | build | 编译的中间文件 | | figures | 存放图像 | | libraries | 存放主控的库函数 | | rt-thread | rt-thread源码 | ## 二、env借助Kconfig剪裁配置 C语言项目的裁剪配置本质上通过条件编译和宏的展开来实现的,RT-Thread借助Kconfig这套机制更方便的实现了这一功能。 Kconfig文件在源码中呈现树形结构,需要**在工程的根目录下存在一份顶层Kconfig文件**,顶层Kconfig文件在文件中通过source语句显示地调用各子目录下的Kconfig文件。Env在根目录下执行menuconfig命令后会递归解析各级Kconfig文件,然后提供如下配置界面,完成相应的配置后并保存,根目录下会存在一份.config文件保存当前选择的配置项,并将.config文件转为RT-Thread的系统配置文件rtconfig.h。 ![构建配置系统](https://www.rt-thread.org/document/site/development-tools/build-config-system/figures/buildconfig2.png) 想了解Kconfig的语法可以查看[rt-thread的文档中心](https://www.rt-thread.org/document/site/#/development-tools/build-config-system/Kconfig)。 ## 三、env使用Scons构建编译 SCons 是一个软件构建工具,类似于 Make,但使用 Python 脚本作为其配置语言。`SConscript` 文件用于定义特定目录下的构建指令,而 `SConstruct` 文件则是顶层构建脚本。 如果我们想在工程中新建一个文件夹存放自己的文件。就可以写一个`SConscript`脚本文件。 ~~~python from building import * import os cwd = GetCurrentDir() CPPPATH = [cwd] src = ['hello.c'] #补上自己的源文件 group = DefineGroup('Hello_Test', src, depend = [''], CPPPATH = CPPPATH) Return('group') ~~~ 这时候我们重新在env里编译`scons`,生成vscode的json,就可以看到我们在`main`函数里面可以正常调用我们所创建的文件夹中的hello.c中的函数。 ## 四、rt-thread启动流程 不知道大家有没有这样的疑惑,一开始拿到rt-thread工程的时候,反正main函数跟普通的裸机开发也没有很大的区别,那么他是怎么实现调度以及终端的初识化呢? 为了在进入 main() 之前完成 RT-Thread 系统功能初始化,以gcc编译器为例子,我们执行了`entry`,最后才会创建main的线程并启动。这边可以看启动文件的汇编文件。 以星号一号的板子为例,启动文件的路径.\libraries\STM32F4xx_HAL\CMSIS\Device\ST\STM32F4xx\Source\ Templates\gcc\startup_stm32f407xx.s ![image-20240723214203291](https://raw.githubusercontent.com/CYFS3/Typroa/main/202407232323659.png) 可以看到先执行的函数是`entry`. ![启动流程](https://www.rt-thread.org/document/site/rt-thread-version/rt-thread-standard/programming-manual/basic/figures/rtt_startup.png) 不同编译器先执行的文件不同,可以查看上图。 不管是什么编译器,最终还是先调用启动函数` rtthread_startup()` ,最后进入用户入口函数` main()`。 现在我们来看一下`rtthread_startup()`干了什么事情。 路径rt-thread\src\components.c ![image-20240723214643021](https://raw.githubusercontent.com/CYFS3/Typroa/main/202407232323660.png) ![image-20240723215243678](https://raw.githubusercontent.com/CYFS3/Typroa/main/202407232323661.png) 具体启动的过程大家可以进行断点调试自行体会。 启动调度器,之后系统就会选在优先级最高的线程进行执行。 ### 1.rt_hw_local_irq_disable 汇编操作,关闭全局中断,避免收到其他因素干扰。 路径:`rt-thread\libcpu\arm\cortex-m3\context_gcc.S` ![image-20240723223258147](https://raw.githubusercontent.com/CYFS3/Typroa/main/202407232323662.png) ### 2.rt_hw_board_init 板极初始化 ![image-20240723223735780](https://raw.githubusercontent.com/CYFS3/Typroa/main/202407232323663.png) 自动初始化机制,`INIT_BOARD_EXPORT(fn)`:非常早期的初始化。 将函数插入到特殊的段中,最后由`rt_components_board_init`寻找并执行函数。 路径:`.\rt-thread\src\components.c` ![image-20240723223808129](https://raw.githubusercontent.com/CYFS3/Typroa/main/202407232323664.png) ### 3.rt_show_version 显示rt-thread版本 路径:`.\rt-thread\src\kservice.c` ![image-20240723224211409](https://raw.githubusercontent.com/CYFS3/Typroa/main/202407232323665.png) ### 4.rt_system_timer_init 系统定时器初始化 路径:`.\rt-thread\src\timer.c` ![image-20240723224355205](https://raw.githubusercontent.com/CYFS3/Typroa/main/202407232323666.png) ### 5.rt_system_scheduler_init 调度器初始化 路径:`.\rt-thread\src\scheduler_up.c` ![image-20240723224620807](https://raw.githubusercontent.com/CYFS3/Typroa/main/202407232323667.png) ### 6.rt_application_init 创建一个main线程 路径:`.\rt-thread\src\components.c` ![image-20240723224713720](https://raw.githubusercontent.com/CYFS3/Typroa/main/202407232323668.png) ### 7.rt_system_timer_thread_init 定时器线程初始化 路径:`.\rt-thread\src\timer.c` ![image-20240723224832833](https://raw.githubusercontent.com/CYFS3/Typroa/main/202407232323669.png) ### 8.rt_system_scheduler_start 路径:`.\rt-thread\src\scheduler_up.c` ![image-20240723222832531](https://raw.githubusercontent.com/CYFS3/Typroa/main/202407232323670.png) ## 五、rt-thread线程 ### 1.线程的定义 * 进程是一个独立的运行实体,包含自己的代码、数据、堆栈和其他资源。 * 每个进程都有自己的地址空间,相互隔离,保证安全性和稳定性。 rt-thrad标准版本只有线程的概念,没有进程的概念。 rt-thrad是抢占式调度的。 ### 2.线程控制块 路径:`.\rt-thread\include\rtdef.h` ![image-20240723230728383](https://raw.githubusercontent.com/CYFS3/Typroa/main/202407232323671.png) #### a.线程栈 RT-Thread 线程具有独立的栈,当进行线程切换时,会将当前线程的上下文存在栈中,当线程要恢复运行时,再从栈中读取上下文信息,进行恢复。 根据芯片的架构保存相应的寄存器。线程切换的过程就是保存现场(保存相对应的寄存器),线程的恢复就是还远相对应的寄存器。 路径:`.\rt-thread\libcpu\arm\cortex-m33\cpuport.c` ![image-20240723231347162](https://raw.githubusercontent.com/CYFS3/Typroa/main/202407232323672.png) 可以看到在创建线程的时候就是初始化了相对应的寄存器。 #### b.线程的状态 | 状态 | 描述 | | -------- | ------------------------------------------------------------ | | 初始状态 | 当线程刚开始创建还没开始运行时就处于初始状态;在初始状态下,线程不参与调度。此状态在 RT-Thread 中的宏定义为 RT_THREAD_INIT | | 就绪状态 | 在就绪状态下,线程按照优先级排队,等待被执行;一旦当前线程运行完毕让出处理器,操作系统会马上寻找最高优先级的就绪态线程运行。此状态在 RT-Thread 中的宏定义为 RT_THREAD_READY | | 运行状态 | 线程当前正在运行。在单核系统中,只有 rt_thread_self() 函数返回的线程处于运行状态;在多核系统中,可能就不止这一个线程处于运行状态。此状态在 RT-Thread 中的宏定义为 RT_THREAD_RUNNING | | 挂起状态 | 也称阻塞态。它可能因为资源不可用而挂起等待,或线程主动延时一段时间而挂起。在挂起状态下,线程不参与调度。此状态在 RT-Thread 中的宏定义为 RT_THREAD_SUSPEND | | 关闭状态 | 当线程运行结束时将处于关闭状态。关闭状态的线程不参与线程的调度。此状态在 RT-Thread 中的宏定义为 RT_THREAD_CLOSE | #### c.线程的优先级 RT-Thread 线程的优先级是表示线程被调度的优先程度。每个线程都具有优先级,线程越重要,赋予的优先级就应越高,线程被调度的可能才会越大。 **注意:rt-thrad线程的优先级是越小越高,与FreeRTOS相反** #### d.时间片 时间片轮转:时间片只对优先级一致的线程起作用,当线程的时间片用完事,就会让出cpu,使**同优先级的线程运行** #### e.线程的入口函数 线程控制块中的 entry 是线程的入口函数,它是线程实现预期功能的函数。 ~~~c void thread_entry(void* paramenter) { while (1) { /* 等待事件的发生 */ /* 对事件进行服务、进行处理 */ } } ~~~ #### f.线程错误码 ![image-20240723232112552](https://raw.githubusercontent.com/CYFS3/Typroa/main/202407232323673.png) #### g.线程的切换 RT-Thread 提供一系列的操作系统调用接口,使得线程的状态在这五个状态之间来回切换。几种状态间的转换关系如下图所示: ![线程状态转换图](https://www.rt-thread.org/document/site/rt-thread-version/rt-thread-standard/programming-manual/thread/figures/04thread_sta.png) ### 3.线程的创建 rt-thrad创建线程的方式有两种 * 静态创建 ~~~c static char thread1_addr[256]; static struct rt_thread thread1; void thread1_entry(void * paramenter) { while(1) { rt_kprintf("this is thread1\r\n"); rt_thread_mdelay(500); } } int main(void) { rt_err_t ret= rt_thread_init(&thread1,"thread1",thread1_entry,RT_NULL,thread1_addr,256,10,10); if(ret == RT_EOK) { rt_thread_startup(&thread1); } while (1) { rt_thread_mdelay(500); } } ~~~ * 动态创建 ~~~c void thread2_entry(void * paramenter) { while(1) { rt_kprintf("this is thread2\r\n"); rt_thread_mdelay(500); } } int main(void) { rt_thread_t thread2; thread2 = rt_thread_create("thread2",thread2_entry,RT_NULL,512,10,10); if(thread2 != RT_NULL) { rt_thread_startup(thread2); } while (1) { rt_thread_mdelay(500); } } ~~~ 具体线程的创建以及使用可以参考[官网文档](https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/thread/thread)
2
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
CYFS
这家伙很懒,什么也没写!
文章
6
回答
3
被采纳
0
关注TA
发私信
相关文章
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
WIZnet_W5500
ota在线升级
cubemx
PWM
flash
freemodbus
BSP
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
SFUD
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
出出啊
1518
个答案
343
次被采纳
小小李sunny
1444
个答案
290
次被采纳
张世争
813
个答案
177
次被采纳
crystal266
547
个答案
161
次被采纳
whj467467222
1222
个答案
149
次被采纳
本月文章贡献
出出啊
1
篇文章
1
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
3
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部