Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
2024-RSOC
【2024-RSOC】RT-Thread内核线程基础【Day2】
发布于 2024-07-23 21:42:24 浏览:523
订阅该版
[tocm] # 【2024-RSOC】RT-Thread内核线程基础【Day2】 ## RT-Thread内核启动流程 RT-Thread是一个开源的实时操作系统,其启动流程大致可以分为以下几个步骤: ![内核启动.png](https://oss-club.rt-thread.org/uploads/20240723/93f499263706d9f7d3c3ff7de762bef1.png.webp) - 硬件初始化:首先进行硬件的初始化,包括设置CPU寄存器,配置时钟和复位,以及初始化外设等。 - 内存初始化:接着对系统内存进行初始化,确保堆栈和数据段的正确布局。 - 中断初始化:配置中断控制器,设置中断向量表,使能全局中断。 - 系统时钟初始化:设定系统时钟源和频率,这是后续所有定时操作的基础。 - RTOS内核初始化:调用rt_kernel_init()函数来初始化RT-Thread内核,这包括初始化各种内核对象的数据结构,如信号量、互斥锁、事件组、消息队列等。 - 创建主线程:内核初始化后,会创建一个名为main的主线程,该线程的入口点是main_thread_entry()函数,用户的应用程序初始化代码通常在这里开始执行。 - 创建空闲线程:同时创建一个空闲线程,当没有其他更高优先级的线程可运行时,CPU会运行空闲线程,避免空转。 - 启动调度器:调用rt_scheduler_start()函数启动调度器,此时内核开始运行,根据线程的优先级和状态进行调度。 ## 线程基本数据结构 RT-Thread中的线程由`struct rt_thread`结构体表示,它包含了线程的基本信息和运行时的状态,例如线程名称、类型、优先级、栈指针、入口函数、参数等。这个结构体包含了线程管理所需的所有信息: ```c struct rt_thread { /* rt object */ char name[RT_NAME_MAX]; /**< 线程名 */ rt_uint8_t type; /**< 对象类型 */ rt_uint8_t flags; /**< 线程标志 */ #ifdef RT_USING_MODULE void *module_id; /**< 应用模块ID */ #endif rt_list_t list; /**< 对象链表 */ rt_list_t tlist; /**< 线程链表 */ /* 堆栈指针和入口 */ void *sp; /**< 堆栈指针 */ void *entry; /**< 入口函数 */ void *parameter; /**< 参数 */ void *stack_addr; /**< 堆栈地址 */ rt_uint32_t stack_size; /**< 堆栈大小 */ /* 错误代码 */ rt_err_t error; /**< 错误代码 */ rt_uint8_t stat; /**< 线程状态 */ #ifdef RT_USING_SMP rt_uint8_t bind_cpu; /**< 绑定CPU */ rt_uint8_t oncpu; /**< 当前运行的CPU */ rt_uint16_t scheduler_lock_nest; /**< 调度器锁嵌套计数 */ rt_uint16_t cpus_lock_nest; /**< CPU锁嵌套计数 */ rt_uint16_t critical_lock_nest; /**< 临界区锁嵌套计数 */ #endif /* 优先级 */ rt_uint8_t current_priority; /**< 当前优先级 */ #if RT_THREAD_PRIORITY_MAX > 32 rt_uint8_t number; rt_uint8_t high_mask; #endif rt_uint32_t number_mask; #ifdef RT_USING_EVENT /* 线程事件 */ rt_uint32_t event_set; rt_uint8_t event_info; #endif #ifdef RT_USING_SIGNALS rt_sigset_t sig_pending; /**< 挂起的信号 */ rt_sigset_t sig_mask; /**< 信号掩码 */ #ifndef RT_USING_SMP void *sig_ret; /**< 信号返回堆栈指针 */ #endif rt_sighandler_t *sig_vectors; /**< 信号处理向量 */ void *si_list; /**< 信号信息列表 */ #endif rt_ubase_t init_tick; /**< 初始化时刻的时钟节拍 */ rt_ubase_t remaining_tick; /**< 剩余时钟节拍 */ #ifdef RT_USING_CPU_USAGE rt_uint64_t duration_tick; /**< CPU使用时钟节拍 */ #endif #ifdef RT_USING_PTHREADS void *pthread_data; /**< pthread数据 */ #endif struct rt_timer thread_timer; /**< 线程定时器 */ void (*cleanup)(struct rt_thread *tid); /**< 线程退出时的清理函数 */ #ifdef RT_USING_LWP void *lwp; #endif rt_ubase_t user_data; /**< 用户数据 */ }; typedef struct rt_thread *rt_thread_t; ``` 其中一些关键字段解释如下: - `name`: 线程的名称,用于标识线程。 - `entry`: 线程的入口函数,即线程开始执行的函数地址。 - `sp`: 栈顶指针,指向线程栈的顶部。 - `current_priority`: 当前线程的优先级。 - `stat`: 线程的状态,如就绪、运行、等待或挂起。 - `thread_timer`: 内置的线程定时器,可用于实现周期性的任务。 ## 基本系统线程(mian、空闲) RT-Thread中的 `main()` 函数是主线程,称为主线程。在系统启动时,系统会创建main线程,其入口函数为 `main_thread_entry()`。用户的应用入口函数 `main()` 就是从这里真正开始的。系统调度器启动后,main线程开始运行,用户可以在 `main()` 函数里添加自己的应用程序初始化代码。 空闲线程是系统创建的最低优先级线程,线程状态永远为就绪态。当系统中无其他就绪态线程存在时,调度器将调度到空闲线程。空闲线程通常是一个死循环,且永远不能被挂起。 ## 静态与动态线程创建异同 静态线程创建与动态线程创建的主要区别在于线程的生命周期管理和资源分配上。静态线程创建使用`rt_thread_init()`函数,需要手动分配和管理线程栈,而动态线程创建使用`rt_thread_create()`函数,线程栈由内核自动分配和管理,因此更简洁方便,但可能会导致额外的内存开销。 其中静态线程在编译时确定,动态线程在运行时创建。 ### 动态线程创建 ```c rt_thread_t rt_thread_create(const char *name, void (*entry)(void *parameter), void *parameter, rt_uint32_t stack_size, rt_uint8_t priority, rt_uint32_t tick) ``` ### 静态线程创建 ```c rt_err_t rt_thread_init(struct rt_thread *thread, const char *name, void (*entry)(void *parameter), void *parameter, void *stack_start, rt_uint32_t stack_size, rt_uint8_t priority, rt_uint32_t tick) ``` ## 编写一个小案例 知道上面的线程基础知识我们就可以写一个简单的小程序了 ```c /* 线程入口函数 */ void thread_entry1(void *parameter) { int count = 0; while (count < 5) { count++; rt_kprintf("This is thread 1 with high priority. count=%d\n", count); rt_thread_mdelay(1000); } } void thread_entry2(void *parameter) { int count = 0; while (count < 5) { count++; rt_kprintf("This is thread 2 with medium priority. count=%d\n", count); rt_thread_mdelay(1500); } } void thread_entry3(void *parameter) { int count = 0; while (count < 5) { count++; rt_kprintf("This is thread 3 with low priority. count=%d\n", count); rt_thread_mdelay(2000); } } int main(void) { rt_thread_t tid1, tid2, tid3; /* 创建线程1,优先级10 */ tid1 = rt_thread_create("thread1", thread_entry1, RT_NULL, 1024, 10, 10); /* 创建线程2,优先级20 */ tid2 = rt_thread_create("thread2", thread_entry2, RT_NULL, 1024, 20, 10); /* 创建线程3,优先级30 */ tid3 = rt_thread_create("thread3", thread_entry3, RT_NULL, 1024, 30, 10); /* 启动线程 */ if (tid1 != RT_NULL) rt_thread_startup(tid1); if (tid2 != RT_NULL) rt_thread_startup(tid2); if (tid3 != RT_NULL) rt_thread_startup(tid3); return 0; } ``` 三个线程入口函数分别为 `thread_entry1`、`thread_entry2` 和 `thread_entry3`,每个线程循环打印不同的消息,并调用 `rt_thread_mdelay()` 函数延时。 线程1,优先级为10。 线程2,优先级为20。 线程3,优先级为30。 然后使用rt_thread_startup()函数启动每个线程。 在env环境下输入 `scons -j12` 编译我们的工程,然后在vscode中烧录,就可以观察到实验现象了。 ```shell (.venv) nie@DESKTOP-N804TTB G:\STM32_Code\a_stm32f407_spark\sdk-bsp-stm32f407-spark\projects\01_kernel $ scons -j12 scons: Reading SConscript files ... Newlib version:4.1.0 scons: done reading SConscript files. scons: Building targets ... scons: building associated VariantDir targets: build CC build\applications\main.o LINK rt-thread.elf arm-none-eabi-objcopy -O binary rt-thread.elf rtthread.bin arm-none-eabi-size rt-thread.elf text data bss dec hex filename 84140 1556 22920 108616 1a848 rt-thread.elf scons: done building targets. ``` 实验现象如下: ``` \ | / - RT - Thread Operating System / | \ 4.1.1 build Jul 23 2024 19:04:42 2006 - 2022 Copyright by RT-Thread team This is thread 1 with high priority. count=1 This is thread 2 with medium priority. count=1 msh >This is thread 3 with low priority. count=1 This is thread 1 with high priority. count=2 This is thread 2 with medium priority. count=2 This is thread 1 with high priority. count=3 This is thread 3 with low priority. count=2 This is thread 1 with high priority. count=4 This is thread 2 with medium priority. count=3 This is thread 1 with high priority. count=5 This is thread 3 with low priority. count=3 This is thread 2 with medium priority. count=4 This is thrThis is thread 2 with medium priority. count=5 ead 2 with medium priority. countThis is thread 3 with low priority. count=5 ``` 可以看到线程1(高优先级)开始执行,线程2(中优先级)开始执行,线程3(低优先级)开始执行。 然后由于延时时间不一致的原因: - 线程1的优先级最高,因此在等待时间到达后,线程1再次被调度执行。 - 线程2的优先级次高,因此在线程1等待期间,线程2被调度执行。 - 线程3的优先级最低,因此在线程1和线程2等待期间,线程3被调度执行。
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
luyism
用舍由时,行藏在我
文章
4
回答
0
被采纳
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
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
ulog
C++_cpp
at_device
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
13
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
本月文章贡献
程序员阿伟
8
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
5
次点赞
ThinkCode
1
篇文章
1
次点赞
Betrayer
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部