本文简单讨论rt-thread在启动后,逐步进入到处于就绪态最高优先级main线程的全过程。部分内容涉及到汇编指令,但通俗易懂。通过简化工程,配合debug过程,逐步观察寄存器的变化、绘制栈帧结构、绘制线程控制块和rt_interrupt_from_thread、rt_interrupt_to_thread等典型变量取值(指向,虽然是rt_uint32_t类型,但实际在汇编中是作为指针使用),能有效帮助理解rtos的线程栈的恢复与启动过程。
通过本文对线程启动过程的了解,对于两个线程/多个线程之间的互相切换能奠定坚实的基础,化繁为简,结合论坛关于上下文切换的代码注释,能帮助快速抓住主线。
使用的软硬件环境如下:
ide工具 - rt-thread studio 2.2.6
硬件 - stm32l431rct6,cortex m4内核
软件 - rt-thread 4.0.5版本
配置 - 仅使能main线程和tidle0线程
一、工程设置
step 1. 新建名称为evbmx_rtthread405_switch的4.0.5版本工程
step 2. 不使能软件定时器,使能线程状态更改的调试
关闭软件定时器线程,避免干扰。
step 3. 关闭msh shell,禁用finsh
关闭tshell线程,避免干扰。仅仅保留main线程和tidle0线程。
step 4. 修改main函数
修改main函数后,线程进入一次,休眠且切换1次,再次切回且return,然后彻底退出,只留下tidle0线程。
#include
#define dbg_tag main
#define dbg_lvl dbg_log
#include
int main(void)
{
rt_thread_mdelay(1000);
return rt_eok;
}
step 5. 下载程序,观察输出结果
读完全文后,对下方输出结果的每一行语句所代表的含义和发生时刻,能有更深刻体会。
二、调试运行
step 6. 在component.c中257行按f9设置断点;f5全速运行到此处后,再按f9关闭此处断点。
step 7. 依次进入rt_thread_create, _thread_init, 停留在thread.c的164行。
将变量thread添加到表达式窗口,可以查看各个成员的值,其中,thread->stack_addr = 0x20001138, thread->stack_size = 0x800,分别表示栈底位置和栈空间大小。
164行的函数rt_hw_stack_init对于理解线程切换是一个相当重要的函数,其形参分别为:
线程入口函数:main_thread_entry
线程参数rt_null:
线程栈栈顶地址:thread->stack_addr + thread->stack_size - 4 = 0x20001138 + 0x800 - 4 = 0x20001934
step 8. 单步进入到rt_hw_stack_init函数内部,开展分析
149行,由于传递进来的stack_addr = 0x20001934,执行完毕后,stk为0x20001938。从0x20001138(含)到0x20001934(含),合计是0x800 = 2048字节。stm32使用的满递减栈,所以此处的stk是0x20001938。
150行,此处设置8字节对齐。由于0x20001938 = (536877368)decimal,该数据除8等于67109671,能被8整除,该语句执行栈对齐操作后,stk依然为0x20001938。
step 9. 继续了解rt_hw_stack_init函数。
151行,更新stk的值,减去struct stack_frame结构体的大小。执行完毕后,stk = 0x200018f4。
153行,stack_frame指针指向0x200018f4。
156至159行,通过for循环将0x200018f4至0x20001938的所有内存变成0xdeadbeaf魔法字。
161行至168行,将stack_frame成员的exception_stack_frame中的r0~psr共8个寄存器分别设置为:线程参数,4个0,线程返回地址,线程入口地址,0x01000000。
175行,返回stk的值,此时变成0x200018f4。这个值在初始化线程时,将返回给thread->sp,即线程栈的临时栈顶指针。
依次将线程的形参、r1-r3, r12, 线程返回地址、线程入口地址,线程的xpsr写入异常栈帧结构中。
在初入门时,这里是难点。c语言中使用结构体定义的栈结构,如何和实际寄存器的顺序进行一一对应?,后文会通过逐步debug揭示这个问题答案。
至此,main线程创建完毕后,线程结构体和线程栈空间如下所示。
step 10. 继续单步到rt_system_scheduler_start函数处,并单独跟踪进入到该函数内部。
期间,rt-thread会调用rt_thread_idle_init函数,在该函数中使用静态创建方式初始化tidle0线程。可以按照上述过程记录tidle0线程的栈空间。
粉尘浓度传感器的特点及技术参数介绍
中兴正式在国内发布了天机Axon 10 Pro,中兴首款搭载5G手机
广东电信FTTR全光组网将通过生态和体验升级打造600个千兆示范小区
新型LED氮氧化物荧光粉技术
铝基板生产厂家有哪些_铝基板八大厂家排名
RT-Thread启动进入就绪态最高优先级线程的全过程与栈帧分析(上)
二维码的作用有多大
无人机和电网建设有什么联系
基于热电偶传感器对冷结点补偿的实现
云网协同实现专线端到端管控,测评体系为专线品质提升保驾护航
美国能源部宣布对OLED固态照明技术正在进行的研究测试结果
长亭科技推流量分析产品 提升企业网络防护能力
华为表示射频芯片领域有差距 对美系厂商依赖严重
ADI 中国大学创新设计竞赛复赛结果正式揭晓
DVP15MC:一款基于CANopen的高速总线型运动控制器
数据显示今年11月我国动力电池产量共计12.7GWh
Linux中/dev/tty、/dev/tty0和/dev/console之间的区别在哪?
怎么使用蒸馏法对低精度推理出浮点网络进行高保真的转换?
iPhone X人脸识技术解析
阿里云进一步加速数据中心的上云率