WRTOS工作原理(二)——任务切换(二)

  上文提到了任务切换的一般实现方法,可以归结为一个伪函数,当各任务有私有变量存在时,这个函数还应该再加上私有资源保护,事实上这部分很容易理解,于是得到如下伪函数。
  

  上面的切换过程即是WRTOS所采用的方法,你可能观察到保存和恢复的顺序相反,这点可以先不担心,主要是因为保存过程一般在任务栈中进行,而栈的操作特性使然。
  下面要重点分析任务切换的出发点和具体作用。在前后台式的程序中,程序都在一个while循环里面运行,周而复始,暂且称之为单任务体系,我们为这个循环设置了众多的标志量,用来指示当前应该触发的工作,主循环中反复检测这些标志位用于控制程序流程,当标志位都不成立时,所有功能都处于等待,但这种查询仍然持续着。但如果有突发任务到来,我们如何做到快速响应呢?我们便用到了中断……
  中断机制无疑是任何一个CPU核心非常重要的构成部分,中断的管理能力是CPU核的重要指标之一,中断过程是一种硬件对程序的管理过程。中断使得CPU能快速响应突发事件,同时也能打破程序执行流程,从某体意义上来说,中断的发生是随机的,对内而言其不可测,中断能迫使CPU强制离开当前正在运行和执行的任务,而转去执行其它的任务,当中断返回原任务时,CPU能够继续执行原任务,到这里,你可能明白了,其实任务切换,就是模拟了一次中断过程。两者在内在处理上大同小异,但中断是不可控制的具有时间上的不确定性,而任务切换切是随时的,可人工干预和控制,下面讨论CPU的中断变量保护过程。
  中断在处理方式上有可嵌套和不可嵌套两种,在硬件处理上,中断有硬件自动入栈中断和非自动入栈中断。假设任务A正运行到了A现场,这时中断发生了,则CPU核中断机构便强制PC跳转到该中断向量指定的位置,若中断管理器有硬件自动入栈功能,则硬件自动将相关寄存器组R0-R12(具体和CPU核有关)入栈,否则只能手动入栈,这个入栈过程是必须有的,要么硬件实现,要么手动实现(一般手动时由编译器完成),然后执行中断向量处指向的程序段,这段程序一般称之为中断服务程序,当这段程序运行完毕后,中断返回,若有硬件入栈,则进行寄存器硬件自动出栈过程,否则手动出栈,之后CPU返回刚才离开的A现场,继续执行。
  我们可以看到,对于A任务来说,就好像是在A现场被暂停了一会儿,它不知道发生了什么,只是被暂停而已,时间到了,便继续运行,好像任何事情都没有发生过一样。而任务切换过程也就是需要模拟这样一种过程,对任务而言,CPU资源切换到其它任务了,则它本身好像被暂停了,当CPU资源被切回来的时候,它好像被唤醒了,对它而言,仅仅是暂停了一会,在任务的私有空间和所用到的资源上一切都恢复到了切换前的状态,貌似什么都没有发生过一样, 这种状态就是任务切换真正要实现的,也是任何一个RTOS的必经之路。
  任务切换模拟了CPU的中断过程,但是它们是有区别的。任务切换可以把CPU资源从A任务带到B任务,也可以带到C任务以及任意一个任务,其目的和下一个目的是随机的对等的。但是中断过程只能在A任务和中断服务程序中切换,我们假定中断服务程序为任务U,则中断过程中CPU资源从A任务切换到U任务,完成U任务后必然返回到A任务(正常的执行流程,事实上也可以通过软件手段改变),它不能实现任务之间的跳转,任务切换过程貌似一种很特殊的没有返回的中断。
  “让任务仅仅只是感觉到被暂停了一会儿”,这便是任务切换的出发点,事实上它确实只有这点作用。但正是这种作用,它体现了一种资源调动,一种时间分配,同时也体现了多任务。当任务A在家等待彩虹的时候,任务B却急着去吃饭,这时候CPU为什么要和任务A一起等呢,鬼知道什么时候才有彩虹?所以CPU把A暂停了,你Y就等吧,CPU转到C去,然后和C去吃饭,也许在等待上菜的过程中,CPU又切换到D任务去了……这种切换带来了CPU利用率的提高,它总是有事可做,并且永远保持着一种青春的活力。当CPU在切换时也伴随着资源(没有CPU资源,拿到任何资源也没用)的切换,当切换的速度足够迅速以及切换的时机足够合理时,我们从外在看,仿佛对于每个任务而言,它自己总是能顺利的执行完自己该做的事,貌似所有任务都在做自己的事,这样一个CPU便成为N个CPU,这正体现了一种多任务机制。任务切换是用于实现多任务方式的最底层的操作,而CPU本身也以极快的速度和及时的态度(取决于上层调度算法)流窜于各个任务之间~~~
  合理分配CPU资源,并协调各任务互存关系,这正是任务切换的作用和目的。
  实际的任务切换过程是需要编译器和具体CPU核支持的,这种操作往往是一种硬件级别的,不可移植的,对于不同的CPU和编译环境而言,这部分实现也是有所不同,我们常说的移植OS系统,任务切换是必须要被移植的。现在任务切换过程在理解上遇到了困难,其涉及到了编译器的特性以及CPU架构,我们需要先中断任务切换过程的讨论,先顺着这条线讨论编译器特性以及CPU架构,然后再回到这个话题。
  先讨论一些CPU架构方面的东西。任何一个CPU,都是由中央累算单元ALU,指令、数据、寄存器组等等概念组成,不论是哈佛结构还是冯洛依曼结构,或者是多级流水线结构。CPU核的每一个动作都由指令控制,而CPU取指由取指总线控制(冯结构中指令总线和数据总线合一),而指令的位置由PC寄存器控制(只是一般叫做PC,也有其它叫法)。所以控制PC寄存器内容,即可控制CPU的取指位置,也即控制了CPU的运行位置。值得一提的是目前的所有CPU架构,都具有PC寄存器,并且都能够利用它来控制CPU取指位置,这是实现任务跳转的基本点,也是最关键的寄存器之一。
  堆栈,在CPU架构里是由纯硬件实现的,堆栈具有后入先出的特性,其貌似于很多人在一起叠罗汉,总是最上面一个人先下来,对于任何一个栈结构,我们只需要知道它的栈底位置,便可以获得栈中的任何一个元素的内容。而CPU中硬件实现栈的方式有很多种,从方向上分,有递增栈和递减栈,从栈顶指针的管理方式分有满栈和空栈。递增是指栈是向上增长的,数据从RAM的低地址向高地址挨着存放;递减是指栈是向下减小的,数据从RAM的高地址向低地址挨着存放,两者都仅仅只需要指定一个栈基地址,然后分别把这个地址做为最低或者最高RAM地址。满栈是指当前栈顶指针所指向的位置已经存放有数据了,如把0,1,2分别放到RAM地址为1000开始的递增栈里,则最后一个2会被放到1002位置处,这时栈顶指针若指向1002,则为满栈,说明此处已经放过元素,下一个数据将被放在栈顶指针加一的位置,若栈顶指针此时指向了1003位置,则为空堆栈,说明此处还是空的,还未有数据放入,下一个数据将被放在栈顶所指的位置。对于上面两组特点,两两组合便产生了常用的四种栈方式,其硬件处理方法略有不同,但是都有一个特点,我们只需要给它一个起始位置,同时关注栈顶位置即可,几乎所有的CPU架构都有这样一个寄存器用来指示栈顶位置,即SP(也只是一般的叫法)寄存器,RTOS在初始化的时候,也将SP初始化,然后CPU按照其核的特性进行栈的出入操作,SP总是指向栈顶,即当前栈中最后入栈的数据。
  寄存器组,寄存器组是直接与CPU累算单元关联的一组高速通用寄存器,它是ALU与外界数据RAM连通的桥梁,CPU取数据时通过数据加载指令将数据从RAM搬到内部寄存器中,然后提供给ALU使用,当输出数据的时候先从ALU搬运到内部寄存器中,然后通过数据存储指令将数据写到RAM中,在这个过程中,内部寄存器组所起的作用是临时中转,当程序运行的时候,多个操作数被搬运到寄存器组中等待运算处理。不同的CPU架构,其内部寄存器组的配置是不同的,如51核有四组通用寄存器组,共16个通用寄存器分成重叠的R0-R3共四组,而AVR8核有一个通用寄存器组从R0-R15共16个寄存器,Cortex-M3核有一个通用寄存器组R0-R15共16个和一个额外的栈寄存器,这些寄存器的作用和定义都种具体的CPU架构,但是正因为CPU运行任何任务的时候都会用到它们,并且是完全公用的,不可能被任务真正意义上私有化(虽然有的编译器可以指定保留寄存器),所以不同任务运行时,通用寄存器的值便被随着被修改,并且不能够恢复,而这些值在任何一个场现都不尽相同,所以这些寄存器组必须在任务切换前被保存起来,当任务切回时,必须先将期恢复,否则我们无法保证不被另一个任务所破坏。
  PC、SP、R0-Rx,是任务切换对于CPU来说必须进行保存和复出的,除此之外,我们要做的就是如何实现它们的保存以及复出,若将这些量保存起来,保存到什么地方呢?才能避免不被其它任务所伤害,否则我们白保存了~~~这些都和编译器有关,并且需要进行更加深入的理解,才能完全明了。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值