千家信息网

如何进行rt-thread中的压栈与出栈分析

发表于:2025-01-29 作者:千家信息网编辑
千家信息网最后更新 2025年01月29日,如何进行rt-thread中的压栈与出栈分析,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。rt-thread中的压栈与出栈1.说明主要想
千家信息网最后更新 2025年01月29日如何进行rt-thread中的压栈与出栈分析

如何进行rt-thread中的压栈与出栈分析,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

rt-thread中的压栈与出栈

1.说明

主要想分析一下rt-thread中线程的压栈与入栈的相关操作。从而更好的掌握线程切换与线程恢复的相关知识。

2.使用场景

首先需要明白的是什么情况下需要进行压栈与出栈的操作?对于这个问题可以做这样的设想,当程序一直做一件事的时候,是顺序执行的,不会有任何干扰。但是此时来了一个中断,那么程序逻辑肯定会优先去处理中断。那么这时需要做哪些事情?

也许这个例子有点脱离实际,讲的通俗明白一些,就是一个人在专注的完成一件事,此时应该是很顺利的进行。但是当事情还没有做完,但是又有一个更加紧急的事情需要你去处理,这时应该怎么做?

对于一个人来讲:

(1)将手里没有做完的事情保留起来,保留当前的进度

(2)将大脑清空,全力完成更加重要的事情

(3)恢复到没有做完的事情的现场,去接着完成没有做完的事情

人的大脑是这样工作的,其实芯片的逻辑也需要这样执行,我们知道芯片如何知道当前程序的状态,无外乎几个重要的寄存器,sp(程序指针寄存器),通用寄存器Rx,以及LR链接寄存器等等。有了这些信息,就可以知道程序当前运行的状态了,这个就是程序的现场。

对于armv7来说,寄存器可以分为以下几种:

armasm_pge1464343210583

在rt-thread操作系统中,涉及到压栈与出栈操作的有两个地方,第一个是中断的进入与中断处理完成后的退出,第二个是线程的切换。

3.简单分析一下rt-thread线程栈的初始化

对于/bsp/qemu-vexpress-a9来说,系统上电后执行rtt的第一行代码在/libcpu/arm/cortex-a/start_gcc.S文件。

然后执行_reset函数,这个函数是汇编函数写的,因为前期没有栈空间,所以代码需要采用汇编指令完成。

然后分配栈空间等等。执行到rtt的其他部分逻辑。这里就不赘述了。这里主要分析的是线程的初始化。

每一个线程在初始化的时候,需要分配栈空间

rt_thread_create/rt_thread_init --> _rt_thread_init --> rt_hw_stack_init

最后调用到了/libcpu/arm/cortex-a/stack.c文件。

rt_uint8_t *rt_hw_stack_init(void *tentry, void *parameter,
rt_uint8_t *stack_addr, void *texit)
{
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)tentry; /* 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) = 0xdeadbeef; /* r1 */
*(--stk) = (rt_uint32_t)parameter; /* r0 : argument */
/* cpsr */
if ((rt_uint32_t)tentry & 0x01)
*(--stk) = SVCMODE | 0x20; /* thumb mode */
else
*(--stk) = SVCMODE; /* arm mode */

#ifdef RT_USING_LWP
*(--stk) = 0; /* user lr */
*(--stk) = 0; /* user sp*/
#endif
#ifdef RT_USING_FPU
*(--stk) = 0; /* not use fpu*/
#endif

/* return task's current stack address */
return (rt_uint8_t *)stk;
}

初始化线程的时候,每个线程都是有一个栈空间的,这个栈空间不仅仅保存一下参数变量,还在栈地址的首地址处保存了线程执行需要的现场。而且每个线程都有一个独立的栈内存,这个内存就是在栈的入口处。

当线程发生切换的时候,需要取出这些寄存器

.globl rt_thread_switch_interrupt_flag
.globl rt_interrupt_from_thread
.globl rt_interrupt_to_thread
.globl rt_hw_context_switch_interrupt
rt_hw_context_switch_interrupt:
#ifdef RT_USING_SMP
/* r0 :svc_mod context
* r1 :addr of from_thread's sp
* r2 :addr of to_thread's sp
* r3 :to_thread's tcb
*/

str r0, [r1]

ldr sp, [r2]
mov r0, r3
bl rt_cpus_lock_status_restore

b rt_hw_context_switch_exit

执行到rt_hw_context_switch_exit函数

.global rt_hw_context_switch_exit
rt_hw_context_switch_exit:

#ifdef RT_USING_SMP
#ifdef RT_USING_SIGNALS
mov r0, sp
cps #Mode_IRQ
bl rt_signal_check
cps #Mode_SVC
mov sp, r0
#endif
#endif
#ifdef RT_USING_FPU
/* fpu context */
ldmfd sp!, {r6}
vmsr fpexc, r6
tst r6, #(1<<30)
beq 1f
ldmfd sp!, {r5}
vmsr fpscr, r5
vldmia sp!, {d16-d31}
vldmia sp!, {d0-d15}
1:
#endif

#ifdef RT_USING_LWP
ldmfd sp, {r13, r14}^ /* usr_sp, usr_lr */
add sp, #8
#endif
ldmfd sp!, {r1}
msr spsr_cxsf, r1 /* original mode */
ldmfd sp!, {r0-r12,lr,pc}^ /* irq return */

该函数可能看起来有些费劲,我来解释一下大概的内容:

当线程间要从上一个线程切换到下一个线程的时候,首先会将切换之前现场保存起来,也就是将这些寄存器的知保存到内存中,然后将sp指向下线程的地址。此时需要恢复下一个需要切换的线程的寄存器。

如果需要理清楚rt-thread的栈空间的压栈与入栈,其实最根本的问题就是如何去处理现场状态的问题。也就是每个线程都需要有一个独立的栈空间,然后这些栈空间除了保存数据,还需要保存寄存器。当进行任务切换的时候,当前线程的寄存器需要保存该线程的栈内存中,而下个线程的栈空间则会从自己的栈空间的起始地址处恢复。这个就是rt-thread栈运作的实现逻辑。

看完上述内容,你们掌握如何进行rt-thread中的压栈与出栈分析的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注行业资讯频道,感谢各位的阅读!

线程 寄存器 空间 事情 切换 时候 程序 分析 函数 就是 问题 内存 地址 逻辑 处理 内容 状态 重要 个人 也就是 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 网络安全关联与金钥管理协定 保定商库网络技术有限公司应该 湖北工程学院网络安全专业 计算机网络技术 百度文档 超凡先锋如何更换服务器 华为服务器面板红灯一直闪烁 怎么找软件开发者 境外服务器开发平台 如何验证数据库用户名 计算机系统与网络技术学士 网络安全知识工作篇数据备份 网络安全和信息化招标 多台电脑管理服务器 数据库管理是操作系统的功能吗 南京正人集团软件开发 杀手6连接服务器 中国水稻数据库天农20 博雅数据库山东2020投档线 服务器如何查询centos版本 查询数据库是否有这条数据 临沂用友软件开发公司地址 网络技术客服怎么样 软件开发过程中什么时候建模 申通网络技术有限公司 南京运行智慧小区软件开发 2017中国网络安全调查 服务器怎么赚钱 霞浦县绕简网络技术服务部 博雅数据库山东2020投档线 灵武小程序软件开发公司
0