转载于: http://blog.csdn.net/lpjybn/article/details/46911559

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

转载于:https://www.cnblogs.com/tureno/articles/7999942.html

RT-Thread内核之线程调度(5)相关推荐

  1. 关于RT thread系统节拍时钟的配置

    关于RT thread系统节拍时钟的配置                  -----本文基于rt-thread-3.1.3版本编写 首先,使用RTthread OS时,要配置(或者明白)它的系统节拍 ...

  2. Yeelink平台使用——远程控制 RT Thread + LwIP+ STM32

    1.前言     [2014年4月重写该博文]     经过若干时间的努力终于搞定了STM32+LwIP和yeelink平台的数据互通,在学习的过程中大部分时间花在以太网协议栈学习上,但是在RT Th ...

  3. 基于rt thread smart构建EtherCAT主站

    我把源码开源到到了gitee,https://gitee.com/rathon/rt-thread-smart-soem 有兴趣的去可以下载下来跑一下 软件工程推荐用vscode 打开.rt thre ...

  4. rt thread studio使用QBOOT和片外flash实现OTA升级

    我们这里要使用单片机外部flash作为OTA的下载分区,外部flash硬件连接关系 PB3-->SPI3_CLK PB4-->SPI3_MISO PB5-->SPI3_MOSI PE ...

  5. rt thread 使用FAL遇到fal_init() undefined reference

    rt thread FAL 0.5版,之前有没有不知道,遇到一个坑. 在main.cpp里面已经 #include <fal.h> fal_init() 编译报错,说 fal_init() ...

  6. RT Thread Free Modbus移植问题整理

    RT Thread Free Modbus移植问题整理 问题描述: 在读写寄存器中,写数据正常,只能读1个寄存器的值,多个值会异常. 在移植过程中发现串口(或RS485)数据接收长度异常. 一.环境描 ...

  7. RT Thread根据开发板制作BSP方法

    之前一直不懂怎么使用RT Thread的软件包,感谢网上的大神,看了你们的博客后大概了解一些,在此做下记录.用RT Thread软件包需要RT Thread的系统,但是RT Thread和RT Thr ...

  8. RT Thread之 Uart2 操作

    官网连接:https://docs.rt-thread.org/#/rt-thread-version/rt-thread-standard/programming-manual/device/uar ...

  9. RT Thread利用STM32CUBEMX和RT Thread studio来创建模板工程

    (1)RT Thread利用STM32CUBEMX来创建模板工程 1.参考文档: 基于 CubeMX 移植 RT-Thread Nano:RT-Thread 文档中心 注意:串口2必须使能异步模式(启 ...

  10. rt thread系统下添加wiznet软件包后,不插网线CPU利用率100%问题

    rt thread系统下添加wiznet软件包后如果不插网线的话其他任务运行很卡,使用ps命令发现优先级低的任务很多都超时了 rt thread线程错误码 添加了一个可以查看CPU利用率的软件包CPU ...

最新文章

  1. JavaScript初体验之冲出迷雾,我四个多小时的经验教训_AX
  2. 全局异常捕获处理-@ControllerAdvice+@HandleException
  3. 14大招打造你的平面设计创意
  4. c++全局类对象_史上最全 Python 面向对象编程
  5. 计算机网络讨论4,计算机网络实验四
  6. Redis(四):String字符串数据类型详解
  7. ajax离开页面方法,如果用户在页面加载完成之前离开页面,则触发jQuery ajaxError()处理程序...
  8. HDU(1175),连连看,BFS
  9. struts2令牌(token)内部原理
  10. redis 重新加载_Redis持久化和Redis持久化方式
  11. Ansible(二)——基本环境的部署以及常用模块的使用
  12. DWR整合Spring
  13. mysql cluster集群安装全纪录
  14. 十大超级老牌黑客逐个曝光
  15. Excel画的图复制到Word中变形的解决办法
  16. namenode元数据多目录配置及测试
  17. 讯搜 配置mysql_迅搜,十分钟搭建一个搜索引擎
  18. [C++]小根堆 插入/删除/初始化
  19. 上网本不会是一场流星雨
  20. 毕业四年后一次同学聚会-性格决定命运

热门文章

  1. 企业必须由真正在乎它的人掌控
  2. 在CMD窗口中使用javac和java命令进行编译和执行带有包名的具有继承关系的类
  3. 传递结构体变量解决方案,资料整理一
  4. 李开复老师:微博改变一切
  5. 算法导论之单源最短路径
  6. Discuz1.5 密码错误次数过多,请 15 分钟后重新登录
  7. springsecurity的工作原理
  8. java程序中,数据验证,如何判断输入的文本框中的值是不是int型?
  9. 另外一篇关于JS页面跳转代码
  10. Nginx独立图片服务器搭建教程