WRTOS工作原理(一)——总述与任务切换(一)

  RTOS是实时操作系统内核的简称,现在的RTOS有很多很多,这里不多提概念性的东西了,以后若哪天要写中国式论文了,再去慢慢查资料吧。但是我认为,RTOS是实时的开放性的操作内核,它给了我们一个编写程序和运行程序的基本框架,它的实时性,并不是它最显著的特点,相反,由于引入了系统现场保存和恢复时间开销,其实时性能反而比一般程序(结构优化过的)下降,但也仅仅是相对而言,开放性才是RTOS的真正特点,真正吸引人的地方。
  RTOS一般都是多任务的,在嵌入式系统里面,限于RAM,FLASH的限制,一般任务都是单级的,任务和线程是对等的,区别于大型操作系统里面的线程和任务的概念,只是人们在RTOS里面习惯叫任务罢了。多任务让RTOS变得更加灵活,适用性更强,但并不是所有的RTOS都是多任务的,我只想研究多任务方式的,单任务的懒得提。
  RTOS的基本功能,就是搭建一个脱离于具体应用环境的公共平台,提供一种程序运行的模式或框架。它不是万能的,从本质上来说,RTOS实际上并不能完成任何功能,但它却可以管理用户程序,管理系统资源,它可以非常合理的分配系统资源,并提供强大的资源协调机制,使系统的资源和效率发挥到极限。从某种意义上来说,RTOS其实就是一段程序。
  RTOS的组成,我所指的RTOS是真正能够在小MCU里面跑的,真正占用资源极少,要求极低的系统。当我们的芯片特性很高时,我们应该用中大型的操作系统,那时再采用RTOS小系统,就有点小牛拉大车的感觉了,毕竟RTOS提供的API和管理能力有限。一个良好的RTOS,一般包括以下几个部分:
  1、任务管理(说明,任务在小RTOS里和线程是一个概念)。包括任务创建、删除、挂起、恢复、动态优先级等等。
  2、消息管理。包括队列消息,循环消息,突发消息,点对点消息,栈消息等等,RTOS可以对其进行全方位操作。
  3、设备管理。RTOS必须提供互斥设备和共享设备的访问机制,多任务环境下非常重要,设备两个线程同时使用一个串口,结果你接收到的数据一会是A的,一会儿是B的,互相掺杂,整个数据都废掉了。
  4、时间管理。包括超时管理、运行延迟管理、以及系统时钟管理。时间让任务之间有了节奏,同时RTOS能接管任务的空闲时间,并合理分配CPU时间。
  5、内存管理。这部分我认为是可选的,在没有MMU的CPU里,要从真正意义上实现内存管理是很困难的,同时其带来的碎片问题非常严重,于是微RTOS里面我认为不必做内存管理,管理啥?总共也才那么几K的内存。
  6、文件管理。实时的小RTOS里面没有文件的概念,只有数据的概念。
总体说来,RTOS其实是很简单的,当RTOS足够强大时,自然会俱备内存管理和文件管理功能。但我认为没有这些功能,在应用最广的MCU里面RTOS照样牛X,带来了程序编写时完全不同的风格和方式。
任务切换----RTOS的心脏。任何一个RTOS,其所有的上层架构,都基于一个任务级的切换函数,这个函数是如此的神秘,也是RTOS里面最神奇最让人不解的地方。但我认为,你掌握了这个切换过程,你便掌握了RTOS的灵魂,自己写个适合自己使用的RTOS也并非难事,所以我们先研究RTOS的任务切换过程及其实现,之后,通过任换切换功能,便可以实现RTOS的任务调度。
任务切换,事实上就是指程序运行时PC地址人为干预跳转,程序正常运行的时候,PC地址跟据指令流程进行变化,跳转的时候,PC地址出现突变,直接到达目的位置取指,我们把它提升一下,若我们跳转到相对应的任务程序段,那么便实现了任务的跳转,当PC地址人为干预在任务段和任务程序段之间来回跳转时,便实现的任务的切换,这是最基本的原理,但也是最真理的东西,任何一个操作系统,都是这样而为之,尽管其表现和实现形式多样~~~
  考虑下面的程序:
  

 

  事实上我们实现了三个函数,假如这三个函数分别实现了一个功能,我们可以称它们为三个任务,任务在某种情况下是可以返回的,但真正的RTOS里面,任务只有创建和删除两种存在状态,没有返回状态。
继续,我们若要用到任务一,我们直接调用:
  

  这是最为常规的用法和思路,但是我们考虑另一种方法:

  
  那么我们接下来可以这样用:

  
  也许你看明白了些什么,或多或少有了点感觉,这是一种基本的归类和抽像的方法,事实上,这种方法正是一种框架式的方法。它是万用的,实现它的一个重要角色,是指针。应该明白,在RTOS里面指针是随处可见的,因为到处都是抽像出来的类似的框架,随处都是灵活多变的。
  有了上面的东西,我们便可以写出一个类似的极简单的任务切换方法:
  

  当然上面的程序不可能实际拿去运行,但从原理上实际了一个时间片公平调度的方法,时间到了,就轮到下一个任务运行,没有纷争。RTOS实际上多是采用基于优先级调度和切换的,这点以后再慢慢提到。
  回到刚才的主题,我们进一步考虑,任务的切换过程和机理。任务好端端的运行着,突然被迫中断了,而CPU去运行另一个任务了,这之间就是一次切换过程,任务运行的任何时刻,我们把所有寄存器的状态和内存状态合称为现场。
  CPU在A现场执行任务,这时候突然跑B现场去了,过一会儿可能回到A现场继续执行,那种我们很容易想到,若A现场在这段时间里被破坏了怎么办?那返回A时岂不是没法继续了?事实上正是如此,其原因,我们继续往更深的地方看。
  CPU里面有一组公用寄存器,所有的任务都能够使用它们,而且它们用完从不负责清理。如有R0---R12一共13个寄存器,这些每个任务都能用,做局部变量用,但从A现场切到B现场的时候,B任务可能把A现场用到的那些寄存器更改了,CPU回到A现场时很伤心,靠,面目全非了。所以寄存器在进行现场切换时必须先保存起来,在返回原现场时应该先进行恢复,这时CPU才能继续。
  除了寄存器之外,RAM资源也是各任务间争夺的重点,假如,你在任务A中用到了20个局部变量,如果R0-R12都用到了(事实上不可能),那还有7个怎么办?这时候它们被放在RAM中。在编译原理中,这种局部变量用一种叫做栈的结构来管理,关于栈的运行原理和特点,不想多说。栈在运行的时候是有时间性的,先入后出,但是栈若只进不出,则你无法伤到以前入栈的那部分,因为它们被永远的埋在了栈底部。也就是说另外7个变量按顺序被放到了栈中。进一步考虑,若A任务和B任务都用到了20个局部变量,则A任务将7个变量入栈,B任务又将7个变量入栈,两者互不影响(用同一个栈,只入不出),但事实上,变量在运行时即可读,也可写,导致栈即有入,又有出,这样栈顶的位置是未知的,同样会导致彻底的伤害。但有一个办法,我们为每个任务建立一个独立的栈空间,并且只允许该任务自己来访问,这部分空间就是每个任务的私有空间。问题立即解决,我在我的电脑上玩,你在你的电脑上玩,玩一天也与你无关。这样,当CPU从A现场到B现场时,B任务对自己的栈进行操作,返回A现场时,A任务对自己的栈进行操作。这样互不相干,后7个变量也得到解决。
  栈这种结构的引入导致了可重入,同时也提供了多任务运行环境,几乎每个编译器都支持栈方式的局部变量分配。但是一般情况下,共用寄存器的使用数量,每个编译器的做法是不一样的。
  栈解决了现场中过多变量的保护问题,但栈的位置也需要保存,从栈的结构而知,栈只需要知道栈顶位置,即可得到栈中的内容。在此先不讨论栈的方向和栈的高级操作。
  所以我们总结一下,现场切换的时候需要先保存原现场用到的所有寄存器,以及原现场用到的栈区域栈顶位置。运行某个现场时,需要先恢复该现场使用的栈区域的栈顶位置,以及该现场所用到的所有寄存器。
  当一个任务完全运行完毕,那么它的所有用过的资源对它来说并不重要了(就像人一样,哎!),跟本不用做任何保存,因为它不再需要使用这些资源。但事实上从一个现场切换到另一个现场时,我们不能事先确认CPU正在A现场干什么,但CPU是忠诚的,它只会很专一的去做一件事,这样,切到B现场后它便忘记了A现场。当CPU再次回到A现场时,它自己已经不记得当时它正在干什么了,所以我们需要帮助它记录下来,当时它正在A现场做什么,做到哪一步了。我们深层思考,其实决定CPU运行位置的也只是一个寄存器,它叫做PC寄存器,PC指的是程序地址,我们需要把PC记录下来,当返回时,先把PC告诉CPU,它就会想起来,哦,原来我那时候在干那事~~~
  于是我们得到如下过程:
  

  上面的过程即是RTOS从任务A切换到任务B的切换过程,也是任务切换过程的所有内容,实际上,任务切换除了保存R0-R12外,还可以保存很多有意义的寄存器,这和具体CPU架构有关,如STM32中还需要保存CPU核状态寄存器用来保存ACC的状态标志位组。PC在更新之后立即生效,所以Resume_TaskB_PC()之后,CPU立即奔向该现场去执行。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值