5.线程切换的本质?
到现在我们知道,每个线程的执行需要一定的“物质”基础。首先,需要获得CPU的使用权,这就包括CPU内部各寄存器的使用,然后有自己独立的栈空间,这部分的空间每个线程应该各自独立。然后,每个线程都有一段独特的指令以完成特定的功能。由这些就组成了“线程上下文”,线程的切换就是线程上下文的切换。在RT-Thread中有两个架构相关的函数来完成这项工作:rt_hw_context_switch,rt_hw_context_switch_interrupt。
那么这两个函数有什么区别呢?显然,rt_hw_context_switch是在非中断中进行上下文切换,而rt_hw_context_switch_interrupt则是在中断上下文中完成线程切换的。这里以S3C2440处理为例:
/*******************************************************************************************
** 函数名称: rt_hw_context_switch
** 函数功能: 非中断中进行上下文切换
** 入口参数: from 被切出的线程的栈顶指针
** to 被切入的线程的栈顶指针
** 返 回 值: 无
** 调 用:
*******************************************************************************************/
.globl rt_hw_context_switch
rt_hw_context_switch:
stmfd sp!, {lr} /** 将被中断的线程的下一条要执行的指令的地址压入栈中
* (LR存放下一条将要执行的指令地址)
*/
stmfd sp!, {r0-r12, lr}/** 将LR,R12-R0寄存器依次入栈
*/
mrs r4, cpsr /** 读取CPSR寄存器的值到R4中
*/
stmfd sp!, {r4} /** 将R4寄存器的值(CPSR)压入栈中
*/
mrs r4, spsr /** 读取SPSR寄存器的值到R4寄存器中
*/
stmfd sp!, {r4} /** 将R4寄存器的值(SPSR)压入栈中
*/
str sp, [r0] /** 将线程的栈顶指针保存到线程结构的sp中
*/
ldr sp, [r1] /** 从新线程的线程结构的sp中取出该线程的栈顶指针
*/
ldmfd sp!, {r4} /** 从线程的栈中弹出SPSR寄存器值到R4寄存器中 */
msr spsr_cxsf, r4 /** 将值写入SPSR寄存器中 */
ldmfd sp!, {r4} /** 从线程的栈中弹出CPSR寄存器值到R4寄存器中 */
msr spsr_cxsf, r4 /** 将值写入CPSR寄存器中 */
ldmfd sp!, {r0-r12, lr, pc}^ /** 恢复该线程其他寄存器的值PC,LR,R12 - R0 */
/*******************************************************************************************
** 函数名称: rt_hw_context_switch_interrupt
** 函数功能: 中断中进行上下文切换
** 入口参数: from 被切出的线程的栈顶指针
** to 被切入的线程的栈顶指针
** 返 回 值: 无
** 调 用:
******************************************************************************************/
.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:
/** 1.判断rt_thread_switch_interrupt_flag变量的值是否为1 */
ldr r2, =rt_thread_switch_interrupt_flag
/** 加载变量rt_thread_switch_interrupt_flag
* 的地址到r2寄存器中
*/
ldr r3, [r2]
/** 读取rt_thread_switch_interrupt_flag寄存器的值到R3寄存器中 */
cmp r3, #1 /**
判断rt_thread_switch_interrupt_flag的值是否为1 */
/** 如果rt_thread_switch_interrupt_flag值为1 */
beq
_reswitch /** 如果rt_thread_switch_interrupt_flag值为1,跳转到标号_reswitch执行 */
mov r3, #1 /**
如果rt_thread_switch_interrupt_flag值为0,将其值设置为1 */
str r3, [r2]
ldr r2, =rt_interrupt_from_thread
/** 加载rt_interrupt_from_thread变量的地址到R2寄存
* 器中
*/
str r0, [r2]
/** 将被切换出的线程的栈顶地址保存到变量rt_interrupt_from_thread中 */
_reswitch:
ldr r2, =rt_interrupt_to_thread
/** 加载rt_interrupt_to_thread变量的地址到R2寄存器
* 中
*/
str r1, [r2] /**
将被切入的线程的栈顶地址保存到变量rt_interrupt_to_thread中 */
mov pc, lr
/*******************************************************************************************
** 函数名称: rt_hw_context_switch_interrupt
** 函数功能: 中断中进行上下文切换
** 入口参数: from 被切出的线程的栈顶指针
** to 被切入的线程的栈顶指针
** 返 回 值: 无
** 调 用:
*******************************************************************************************/
.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:
/** 1.判断rt_thread_switch_interrupt_flag变量的值是否为1 */
ldr r2, =rt_thread_switch_interrupt_flag/**
加载变量rt_thread_switch_interrupt_flag的地址到r2寄存器中 */
ldr r3, [r2] /** 读取rt_thread_switch_interrupt_flag寄存器的值到R3寄存器中 */
cmp r3, #1 /** 判断rt_thread_switch_interrupt_flag的值是否为1 */
/** 如果rt_thread_switch_interrupt_flag值为1 */
beq _reswitch /** 如果rt_thread_switch_interrupt_flag值为1,跳转到标号_reswitch执行 */
mov r3, #1 /** 如果rt_thread_switch_interrupt_flag值为0,将其值设置为1 */
str r3, [r2]
ldr r2, =rt_interrupt_from_thread /**
加载rt_interrupt_from_thread变量的地址到R2寄存器中 */
str r0, [r2] /** 将被切换出的线程的栈顶地址保存到变量rt_interrupt_from_thread中 */
_reswitch:
ldr r2, =rt_interrupt_to_thread /**
加载rt_interrupt_to_thread变量的地址到R2寄存器中 */
str r1, [r2] /** 将被切入的线程的栈顶地址保存到变量rt_interrupt_to_thread中 */
mov pc, lr
我们发现rt_hw_context_switch_interrupt并没有完成线程的切换,只是用全局变rt_interrupt_from_thread
和rt_interrupt_to_thread保存了被换出和换入的线程的栈顶指针,而真正的切换过程在中断处理中完成。
.globl rt_interrupt_enter
.globl rt_interrupt_leave
.globl rt_thread_switch_interrupt_flag
.globl rt_interrupt_from_thread
.globl rt_interrupt_to_thread
vector_irq:
stmfd sp!, {r0-r12,lr} /** 使用中断模式的栈空间来存储SVC模式下的PC, R12 - R0 */
bl
rt_interrupt_enter /** 调用rt_interrupt_enter函数: 中断嵌套的层数加1 */
bl
rt_hw_trap_irq /** 根据中断号去调用中断处理程序:由于中断处理程序是在IRQ模式执行,
* 因此系统是不支持中断嵌套的
*/
bl
rt_interrupt_leave /** 调用rt_interrupt_leave函数: 中断嵌套的层数减1 */
/** 在中断退出之前,判断rt_thread_switch_interrupt_flag变量的值是否为1 */
ldr r0, =rt_thread_switch_interrupt_flag /** 读取变量rt_thread_switch_interrupt_flag
* 的地址到r0寄存器中
*/
ldr r1, [r0] /** 读取变量rt_thread_switch_interrupt_flag的值 */
cmp r1, #1 /** 判断变量rt_thread_switch_interrupt_flag的值是否为1 */
beq
_interrupt_thread_switch /** 如果为1说明在退出中断模式之前还需要进行任务切换工作;
* 如果为0则可以安全的退出中断模式了
*/
ldmfd sp!, {r0-r12,lr} /** 恢复SVC模式下的各个寄存器值 */
subs pc, lr, #4 /** 继续从被中断点执行 */
_interrupt_thread_switch:
/** 1.将变量rt_thread_switch_interrupt_flag的值清0
*/
mov r1, #0 /**
设置R1寄存器的值为0 */
str r1, [r0] /**
将变量rt_thread_switch_interrupt_flag的值设置为0 */
ldmfd sp!, {r0-r12,lr} /**
恢复保存在IRQ模式中的各寄存器值 */
stmfd sp!, {r0-r3} /**
将R0-R3寄存器入栈 */
mov r1, sp /**
将此时的栈指针保存在R1中 */
add sp, sp, #16 /**
将SP的值加16,SP重新指向R0-R3入栈时的位置 */
sub r2, lr, #4 /**
计算出被中断的线程的PC值保存到R2中 */
mrs r3, spsr /**
加载被中断的线程的CPSR寄存器值到R3寄存器中 */
orr r0, r3, #NOINT /**
屏蔽中断位 */
msr spsr_c, r0 /**
将设置后的值写回IRQ模式的SPSR寄存器中 */
ldr r0, =.+8 /**
通过反汇编查看: 是将下面第二条指令的地址存到R0中 */
movs pc, r0 /**
movs指令会影响到CPSR,包括N,Z,C标志位,CPSR会被SPSR覆盖
* 因此执行此条指令相当于完成处理器从IRQ到SVC模式的切换
* 下面指令的sp将为SVC下的sp寄存器,而非IRQ模式的sp
*/
stmfd sp!, {r2} /**
将被中断的线程的PC值入栈 */
stmfd sp!, {r4-r12,lr} /**
将被中断的线程的LR,R12-R4寄存器入栈 */
mov r4, r1 /**
将R1的值保存到R4 */
mov r5, r3 /**
将R3的值保存到R5(IRQ_SPSR) */
ldmfd r4!, {r0-r3} /**
将栈中保存的R0-R3寄存器值恢复 */
stmfd sp!, {r0-r3} /**
将R3-R0寄存器值入栈 */
stmfd sp!, {r5} /**
将旧任务的CPSR值入栈 */
mrs r4, spsr
stmfd sp!, {r4} /**
将旧任务的SPSR值入栈 */
/** 读取保存在变量rt_interrupt_from_thread的旧线程的sp值
*/
ldr r4, =rt_interrupt_from_thread
ldr r5, [r4]
str sp, [r5] /**
保存换出任务的栈顶指针 */
/** 获取新线程的栈顶指针 */
ldr r6, =rt_interrupt_to_thread /**
加载变量rt_interrupt_to_thread的地址到R6寄存器
* 中
*/
ldr r6, [r6] /**
加载变量rt_interrupt_to_thread的值到R6中 */
ldr sp, [r6] /**
加载变量rt_interrupt_to_thread的值到SP寄存器中 */
ldmfd sp!, {r4} /**
弹出新线程的SPSR寄存器值 */
msr SPSR_cxsf, r4
ldmfd sp!, {r4} /**
弹出新线程的CPSR寄存器值 */
msr CPSR_cxsf, r4
ldmfd sp!, {r0-r12,lr,pc} /**
弹出新线程的其他各寄存器,线程恢复 */
版权声明:本文为博主原创文章,未经博主允许不得转载。