Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
源码分析
RT thread源码分析——多线程机制的基石
发布于 2025-02-12 12:02:55 浏览:60
订阅该版
[tocm] # 背景 以arm架构,单核cpu的系统板为例。 rt thread commit:9afe6a51826fb84de229de220dab2077aa5734b2 # 引入 RT thread的调度器模块其实没有挂起队列,只有就绪队列。如果RT thread只有thread,调度器,中断管理这三个模块,线程就不能阻塞,线程初始化完毕后要么处于就绪状态要么处于运行状态。 信号量,事件等机制会引入线程被阻塞的概念。多线程机制如同步的信号量,事件,互斥量等是依赖于挂起队列模块,上下文切换,临界区保护这三大部分。 # 上下文切换 上下文在代码里则是指CPU里所有寄存器的瞬时状态。 需要用汇编语言操作寄存器,上下文切换在`libcpu`目录下。 以arm为例,核心是保存寄存器到栈上,栈指针切换到另一个栈上。 注意这里也是有顺序的,CPU状态寄存器肯定要最后保存,而PC寄存器(r13)则第一个保存。这样再切换回来时,栈弹出时优先恢复CPU执行状态,最后加载PC,跳转回去。 ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20250212/af7daeec583efaceab439bf4bcd130df.png.webp) 如果线程是第一个,调度时上下文从哪里加载? ```C++ thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter, (rt_uint8_t *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t)), (void *)_thread_exit); rt_uint32_t *stk; stack_addr += sizeof(rt_uint32_t); stack_addr = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stack_addr, 8); stk = (rt_uint32_t *)stack_addr; *(--stk) = (rt_uint32_t)_thread_start; /* entry point */ *(--stk) = (rt_uint32_t)texit; /* lr */ *(--stk) = 0xdeadbeef; /* r12 */ *(--stk) = 0xdeadbeef; /* r11 */ *(--stk) = 0xdeadbeef; /* r10 */ *(--stk) = 0xdeadbeef; /* r9 */ *(--stk) = 0xdeadbeef; /* r8 */ *(--stk) = 0xdeadbeef; /* r7 */ *(--stk) = 0xdeadbeef; /* r6 */ *(--stk) = 0xdeadbeef; /* r5 */ *(--stk) = 0xdeadbeef; /* r4 */ *(--stk) = 0xdeadbeef; /* r3 */ *(--stk) = 0xdeadbeef; /* r2 */ *(--stk) = (rt_uint32_t)tentry; /* r1 : argument 2 for trampoline */ *(--stk) = (rt_uint32_t)parameter; /* r0 : argument 1 */ /* cpsr */ if ((rt_uint32_t)tentry & 0x01) *(--stk) = SVCMODE | 0x20; /* thumb mode */ else *(--stk) = SVCMODE; ``` 在创建线程时会在栈创建一个对应的初始帧,线程第一次被执行后,控制权跳到这个初始帧,再跳到函数入口。 # 挂起队列模块 当线程A不被调度时,调度器模块实际上只做了从调度队列移去线程A的操作。(调度器模块只有调度队列),那么线程A何去何从,又该在哪个队列里? 线程等待IPC对象时,线程会移入IPC对象的挂起队列。 IPC对象共用一套挂起线程的服务。这个服务主要由以下这组函数实现: `rt_susp_list_dequeue` `rt_susp_list_enqueue` `rt_susp_list_resume_all` `rt_thread_suspend_with_flag` 这里实现比较简单,不再赘述。 # 临界区保护 在单核系统内,保持共享数据的一致性方法有两类: 第一个是直接关闭中断。关闭了中断就没有线程被中断,切换到其他线程的可能。 `rt_hw_interrupt_enable` `rt_hw_interrupt_disable` 第二个是关闭调度器,同理,关闭调度器,当前就没有切换到其他就绪线程的可能。 `rt_enter_critical` `rt_exit_critical` # 总结 IPC对象在挂起队列时使用队列挂起服务,临界区保护机制来保持IPC对象数据的一致性,获取到IPC对象的线程会移到就绪队列里,等待调度器调度,进行上下文切换。
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
catcatbing
这家伙很懒,什么也没写!
文章
7
回答
0
被采纳
0
关注TA
发私信
相关文章
1
kawaii mqtt 严谨使用注意事项,求助
2
关于阅读 device.c 的几点疑惑
3
rt thread studio软件中怎么设置添加pc lint工具
推荐文章
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
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
I2C_IIC
ESP8266
UART
cubemx
WIZnet_W5500
ota在线升级
PWM
BSP
flash
freemodbus
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
ulog
SFUD
msh
C++_cpp
MicroPython
本月问答贡献
RTT_逍遥
7
个答案
2
次被采纳
三世执戟
7
个答案
1
次被采纳
KunYi
6
个答案
1
次被采纳
winfeng
2
个答案
1
次被采纳
chenyaxing
2
个答案
1
次被采纳
本月文章贡献
出出啊
1
篇文章
4
次点赞
小小李sunny
1
篇文章
1
次点赞
张世争
1
篇文章
3
次点赞
crystal266
2
篇文章
2
次点赞
whj467467222
2
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部