RT-Thread内核之线程调度(六)

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}   /**
弹出新线程的其他各寄存器,线程恢复 */

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-11 18:42:11

RT-Thread内核之线程调度(六)的相关文章

LINUX内核分析第六周学习总结——进程的描述和进程的创建

LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.知识概要 进程的描述 进程描述符task_struct数据结构(一) 进程描述符task_struct数据结构(二) 进程的创建 进程的创建概览及fork一个进程的用户态代码 理解进程创建过程复杂代码的方法 浏览进程创建过程相关的关键代码 创建的新进程是从哪里开始执行的

RT Thread学习历程(1):串口乱码问题

因为学习实时系统,最近接触到RT Thread. 把RT Thread官网上的示例代码烧录到STM32的板子上之后,在串口软件上接收到的全是乱码,一开始以为是串口软件的问题,换了2个软件之后情况都一样,最后发现是晶振的问题,我用的是STM32F407VGT6,晶振要设为8MHz,代码相应的设置晶振的部分也要修改.

java基础知识回顾之java Thread类学习(六)--java多线程同步函数用的锁

1.验证同步函数使用的锁----普通方法使用的锁 思路:创建两个线程,同时操作同一个资源,还是用卖票的例子来验证.创建好两个线程t1,t2,t1线程走同步代码块操作tickets,t2,线程走同步函数封装的代码操作tickets,同步代码块中的锁我们可以指定.假设我们事先不知道同步函数用的是什么锁:如果在同步代码块中指定的某个锁(测试)和同步函数用的锁相同,就不会出现线程安全问题,如果锁不相同,就会发生线程安全问题. 看下面的代码:t1线程用的同步锁是obj,t2线程在操作同步函数的资源,假设不

Linux内核设计基础(六)之块I/O层

块设备是指能随机访问固定大小数据片的设备,如硬盘:字符设备(如串口和键盘)是按照字符流的方式有序访问.区别在于是否可以随机访问数据--也就是能否在访问设备时随意地从一个位置跳转到另一个位置.我们可以感觉到块设备的控制要比字符设备复杂多,实际上内核在块设备上下了大工夫--块I/O层. 基础概念 块设备中最小的可寻址单元是扇区. 文件系统的最小寻址单元是块. 所谓的缓冲区是块在内存中的表示. 对于一个缓冲区(块),内核需要知道它的控制信息,这时需要一个结构进行描述--缓冲区头. I/O调度机制 首先

Linux内核分析(六)----字符设备控制方法实现|揭秘系统调用本质

原文:Linux内核分析(六)----字符设备控制方法实现|揭秘系统调用本质 Linux内核分析(六) 昨天我们对字符设备进行了初步的了解,并且实现了简单的字符设备驱动,今天我们继续对字符设备的某些方法进行完善. 今天我们会分析到以下内容: 1.      字符设备控制方法实现 2.      揭秘系统调用本质 在昨天我们实现的字符设备中有open.read.write等方法,由于这些方法我们在以前编写应用程序的时候,相信大家已经有所涉及所以就没单独列出来分析,今天我们主要来分析一下我们以前接触

Linux内核学习第六周 进程描述与进程创建

1.task_struct的数据结构 1235struct task_struct { 1236 volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ 1237 void *stack; 1238 atomic_t usage; 1239 unsigned int flags; /* per process flags, defined below */ 1240 unsigned int ptrace; 1241 12

RT-thread内核之线程调度算法分析

一个操作系统如果只是具备了高优先级任务能够“立即”获得处理器并得到执行的特点,那么它仍然不算是实时操作系统.因为这个查找最高优先级线程的过程决定了调度时间是否具有确定性,例如一个包含n个就绪任务的系统中,如果仅仅从头找到尾,那么这个时间将直接和n相关,而下一个就绪线程抉择时间的长短将会极大的影响系统的实时性.当所有就绪线程都链接在它们对应的优先级队列中时,抉择过程就将演变为在优先级数组中寻找具有最高优先级线程的非空链表. RT-Thread内核中采用了基于位图(bitmap)的优先级算法(时间复

RT-thread内核之线程调度器分析

一.前言 RT-Thread中提供的线程调度器是基于全抢占式优先级的调度,在系统中除了中断处理函数.调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的,包括线程调度器自身.系统总共支持256个优先级(0 - 255,数值越小的优先级越高,0为最高优先级,255分配给空闲线程使用,一般用户不使用.在一些资源比较紧张的系统中,可以根据情况选择只支持8个或32个优先级的系统配置).在系统中,当有比当前线程优先级还要高的线程就绪时,当前线程将立刻被换出,高优先级线程抢占处

RT-thread内核之线程调度器

http://www.cnblogs.com/King-Gentleman/p/4278012.html 一.前言 RT-Thread中提供的线程调度器是基于全抢占式优先级的调度,在系统中除了中断处理函数.调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的,包括线程调度器自身.系统总共支持256个优先级(0 - 255,数值越小的优先级越高,0为最高优先级,255分配给空闲线程使用,一般用户不使用.在一些资源比较紧张的系统中,可以根据情况选择只支持8个或32个优先