前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >RT-Thread在Cortex-A上下文切换流程分析

RT-Thread在Cortex-A上下文切换流程分析

作者头像
bigmagic
发布2020-03-19 21:17:38
1.6K0
发布2020-03-19 21:17:38
举报
文章被收录于专栏:嵌入式iot嵌入式iot

01本文目的

这篇文章主要分析在arm cortex-a系列芯片上,上下文切换的分析,通过这篇文章,对大多数的cortex-a系列芯片的上下文切换有一定的了解。

我们知道,在进行上下文切换的时候,需要保存现场,线程线程切回来时要恢复现场,同样的处理中断的时候,也是这个逻辑,还有就是切换后不用返回的情况。所以我们需要从三个方面去分析这个问题:

1.任务切换后不返回(rt_hw_context_switch_to)

2.用户上下文线程切换(rt_hw_context_switch)

3.中断上下文线程切换(rt_hw_context_switch_interrupt)

栈的寄存器从栈顶依次向下为pc, lr, r0-r12, cpsr。

02rt_hw_context_switch_to

该函数将目标线程的栈顶指针载入到sp,线程在栈中保存的寄存器自顶向下依次为pc, lr, r0-r12, cpsr。首先恢复目标线程的cpsr,然后恢复换入线程的上下文并执行。代码注释如下:

rt_hw_context_switch_to:

ldr sp, [r0] @ 将目标线程的栈顶指针载入到sp

ldmfd sp!, {r4} @ 恢复目标线程的cpsr

msr spsr_cxsf, r4

ldmfd sp!, {r0-r12, lr, pc}^ @ 恢复换入线程的上下文并执行

ldmfd的寻址方式是事后递增方式(Increase After),所以我们将sp指针自增后,读到r4寄存器,然后通过msr写到cpsr寄存器中。然后再依次恢复其他的寄存器。

该函数在rt_system_scheduler_start函数最后被调用,也就是当系统初始化完成后,然后进行调度第一个最高优先级并已经就绪的任务。

而这次的初始化函数就不需要返回了。

03rt_hw_context_switch

该函数将当前线程的上下文压入栈中,根据当前Thumb状态调整返回时状态寄存器,并保存在栈中。线程在栈中保存的寄存器自顶向下依次为pc, lr, r0-r12, cpsr,栈顶指针sp保存参数1所指向的线程控制块中。随后从参数2指向的线程控制块中载入目标线程的栈顶指针到sp,从目标线程的栈中恢复线程上下文并执行。代码注释如下:

rt_hw_context_switch:

stmfd sp!, {lr} @ 线程恢复执行时的PC指入栈

stmfd sp!, {r0-r12, lr} @ 将当前线程的上下文压入栈中

mrs r4, cpsr

tst lr, #0x01

orrne r4, r4, #0x20 @ 根据当前Thumb状态调整返回时状态寄存器

stmfd sp!, {r4} @ 并保存在栈中

str sp, [r0] @ 栈顶指针sp保存参数1所指向的线程控制块中

ldr sp, [r1] @ 从参数2指向的线程控制块中载入目标线程的栈顶指针到sp

ldmfd sp!, {r4} @ 恢复目标线程的cpsr

msr spsr_cxsf, r4

ldmfd sp!, {r0-r12, lr, pc}^ @ 从目标线程的栈中恢复线程上下文并执行

这个函数就是将当前的栈现场寄存器保存,然后恢复下个寄存器的现场,典型的就是进行线程调度。

04rt_hw_context_switch_interrupt

该函数在中断上下文中进行线程切换,为了不影响当前中断的执行,该例程将换入和换出线程的栈顶指针分别存放在全局变量rt_interrupt_from_thread和rt_interrupt_to_thread中,并设置rt_thread_switch_interrupt_flag为1;若该变量值已经设为1,说明之前已经准备好线程切换了,此时只需设置新的换入线程即可。等到中断处理完毕后再进行线程切换。代码注释如下:

rt_hw_context_switch_interrupt:

ldr r2, =rt_thread_switch_interrupt_flag

ldr r3, [r2]

cmp r3, #1 @ 判断rt_thread_switch_interrupt_flag是否为1

beq _reswitch @ 若该变量值已经设为1,说明之前已经准备好线程切换了,此时只需设置新的换入线程即可。

ldr ip, =rt_interrupt_from_thread @ 将换入线程的栈顶指针存放在全局变量rt_interrupt_from_thread

mov r3, #1 @ 设置rt_thread_switch_interrupt_flag为1

str r0, [ip]

str r3, [r2]

_reswitch:

ldr r2, =rt_interrupt_to_thread @ 将换出线程的栈顶指针存放在全局变量rt_interrupt_to_thread

str r1, [r2]

bx lr @ 中断返回

流程图如下所示:

4.1 rt_hw_context_switch_interrupt_do

中断处理过程中并不执行线程切换,只是通过函数rt_hw_context_switch_interrupt设置相关的全局变量。当中断处理完毕后,检查rt_thread_switch_interrupt_flag变量,若值不为1则正常返回;若值为1,则进行线程切换,实际的切换工作由中断入口函数中rt_hw_context_switch_interrupt_do部分来完成。代码注释如下:

rt_hw_context_switch_interrupt_do:

mov r1, #0 @ 设置rt_thread_switch_interrupt_flag为0

str r1, [r0]

mov r1, sp @ 中断上下文栈顶地址保存到r1

add sp, sp, #4*4

ldmfd sp!, {r4-r12,lr}@ 将中断中压栈的寄存器r4-r12和lr弹出

mrs r0, spsr @ 获取中断线程的cpsr

sub r2, lr, #4 @ 计算中断线程的PC

msr cpsr_c, #I_Bit|F_Bit|Mode_SVC @ 切换至SVC并禁止中断

stmfd sp!, {r2}

stmfd sp!, {r4-r12,lr}

ldmfd r1, {r1-r4}

stmfd sp!, {r1-r4}

stmfd sp!, {r0} @ 在新栈上保存中断线程的上下文

ldr r4, =rt_interrupt_from_thread

ldr r5, [r4]

str sp, [r5] @ 保存栈顶指针到换出线程的线程控制块

ldr r6, =rt_interrupt_to_thread

ldr r6, [r6]

ldr sp, [r6] @ 从新线程的线程控制块中载入栈顶指针到sp

ldmfd sp!, {r4} @ 弹出新线程的cpsr到spsr

msr spsr_cxsf, r4

ldmfd sp!, {r0-r12,lr,pc}^ @ 弹出线程上下文,切换至新线程执行

05总结

通过以上的分析,大概叙述了rt-thread的上下文切换时栈的运行情况,同理可以看cortex-m系列的上下文切换流程。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 嵌入式IoT 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 01本文目的
  • 02rt_hw_context_switch_to
  • 03rt_hw_context_switch
  • 04rt_hw_context_switch_interrupt
    • 4.1 rt_hw_context_switch_interrupt_do
    • 05总结
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档