Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3003497
  • 博文数量: 523
  • 博客积分: 11908
  • 博客等级: 上将
  • 技术积分: 5475
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-03 15:50
文章分类

全部博文(523)

文章存档

2019年(3)

2013年(4)

2012年(71)

2011年(78)

2010年(57)

2009年(310)

分类:

2009-05-09 18:14:39

Ucosii实时系统概述(转载)

任务,UCOS-ii实时系统中,一个任务其实就是一个线程,该任务可以认为CPU完全属于它自己。UCOS-ii中的任务拥有自己的堆栈和CPU寄存器,并且赋予一定的优先级,在UCOS-ii中,它可能处于睡眠、就绪、运行、等待、中断服务状态之一。

基于优先级的内核有两种:不可剥夺型和可剥夺型。不可剥夺型内核它要求每个任务互相合作,也称为合作型内核,每个任务不会被其它任务剥夺去,除非中断的到来,即便如此,当中断结束后,还是会回到原来被中断的程序,而不会切换到具有高优先级的任务中去,这样,高优先级的任务就不能够及时得到执行,所以它的实时性是比较差的。但是它有一个很重要的特点,就是它可以使用不可重入函数,因为每个任务必须执行完,才能释放CPU,这样它对其它任务调用可重入函数没有影响;同理在大多数情况下它无须使用信号量来保护资源。[2007-9-19补记:我们有时候写前后台的程序时,通常利用了分时的观念,即我们在延迟中执行其他代码,这从本质上也可以说是借鉴不可剥夺的内核特点来写的,至少它们有相同之处吧,不过这要求程序对自己的程序运行过程非常清楚,其实这也是正确使用UCOS实现它最优实时性的前提,不过这境界有点难咯,我自己也没有做到,让我们努力吧!]

可重入型函数可以被一个以上的任务调用,而不必担心数据的破坏。可重入型函数任何时候都可以被中断,一段时间以后又可以运行,而相应数据不会丢失。可重入型函数或者只使用局部变量,即变量保存在CPU寄存器中或堆栈中。如果使用全局变量,则要对全局变量予以保护。

   如果运用了全局变量而又不给矛保护,此程序运行了一半,改变了全局变量的值.而此时又有更高一级的中断程序运行而把当前的程序挂起,同时这一函数被更高一级的程序又调用,那么全局变量中的数据就有可能不是你想像中的数据.所以在实时的操作系统中使用不可重入函数时要特别注意,而同时这种错误很难发现.
有几种方法使函数变成可重入性:
(1):把全局变量改成局部变量.
(2):在调用此函数之前把中断关闭,调动后再开中断,当然这种方法是以降低系统的实时性为代价的.
(3):用信号量禁止该函数在使用过程中再次调用.

    对于可剥夺型内核,只要高优先级任务一就绪,那它就会被执行,而当前正在执行的任务就会被挂起;正因为如此,对于系统的资源就不能像不可剥夺型那样去使用,而是在使用前必须检查是否可以使用,即互斥机制来保护临界资源,如果不用的话,那么如果低优先级在使用临界资源时,突然被高优先级把CPU给抢过去了,那么低优先级的临界资源就可能会被高优先级任务给破坏掉;可剥夺型的优点是是系统的响应时间得到了优化,且是可知的。

实时系统中,中断优先级反转这一问题是出现得最多的,这问题的本质其实也不难理解,就是低优先级的任务占有高优先级的任务所需要的资源,而使高优先级不得不等低优先级把资源释放才能执行。

任务控制块OS_TCB,它是一个有结构体组成的,每当一个任务被建立时,它就会被建立,并且和系统中的任务组成一个双向链表,它的第一个结构体成员是堆栈栈顶指针,这样做的好处避免在移植过程中出现不必要的偏移地址计算过程,节省了CPU的时间,由于它是一个双向链表,自然会有前向指针【指向前面一个任务控制块】和后向指针【指向后面一个任务控制块指针,最后一个任务块指针指向空指针】,还包含有与该任务优先级以及与优先级相关的数据成员和与该任务有关的事件、邮箱、队列等,其它的成员我暂时用不着,先PK掉,以后用时再看。在这个结构体中,我觉得应该值得时刻注意的是:任务堆栈指针,任务状态,任务优先级。

任务控制块的初始化,它其实就是从空任务控制块中取一个任务控制块,并对它的成员进行初始化。

任务的就绪表,我认为这时UCOS中的几个经典部分之一,记得我当时第一次看它时,发现这很奇妙,心中直夸作者的智慧。我们在创建任务时,系统跟住优先级来分配控制块和堆栈,首先系统会对优先级进行一个检查,如果该优先级没有被其它任务占有,那么它就分配给该任务;同时这很明显会出现一个问题,怎样去管理这些优先级以及怎样知道优先级的使用情况就成了该系统自然要解决的问题,该系统的作者很有智慧,发现了优先级的数字有这么一个功能,系统有64个优先级,由于我们的字节是8位的,呵呵,很明显来个8*8的矩阵,再仔细一看,原来一个优先级的位3~510进制数可以作为优先级在哪一组,位0~210进制数可以作为在该组中某位为1的位,然后一算,呵呵恰好是优先级的数,这种思维在UCOS中用了好几次。系统在进行任务切换时,是把CPU分配就绪表中优先级最高的任务,那怎样来找呢,因为我们已经知道优先级的数字有那么一个巧妙在里面,何不好好利用之呢,我们要想知道就绪任务的最高优先级的值,就要知道优先级所在的组以及所在列,我们先来找组,因为优先级的值越小优先级越高,那么无论就绪表中的表那几组为1,就绪任务肯定在最低一位为1所在的列中找,那么很自然我们可以这样来见表,依次从0~255开始来设,即优先级组的值为1时是在第几组,为253时又在第几组,这个表一建成,就是一个1*256的数组,再仔细看发现找列时也可以跟住这个表来查找,真是得来全不费功夫。优先级的低3位和3~5位都知道了,然后在跟住优先级就绪表的原理反过来就可以计算出就绪表中任务最高优先级的值拉。

     OS_ENTER_CRITICAL()OS_EXIT_CRITICAL()也可以用来保护应用程序中的临界代码;然而要特别小心,如果在调用一些如OSTimeDel()之类的功能函数之前关中断,应用程序将会死机;原因是任务被挂起一段时间,直到挂起时间到,但由于中断关掉了,时钟节拍中断一直得不到服务,显然所有的挂起类调用都有这样的问题,所以要特别小心。作为一条普遍适用的规则,调用UCOS-ii功能函数时,中断总是开着的。

     任务调度,因为UCOS总是运行优先级最高的任务,而CPU的使用权转移到优先级最高的任务这一过程是通过任务调度来完成的,具体它的实现步骤在UCOS中其实现代码是比较简单的,首先是关中断,其次检查其调用是否来自中断或者至少调用了一次给任务调度上锁函数,并且没有释放锁,都将不能执行调度,否则发生死锁,简单说明一下,在中断中,肯定是不能调度的,应该不难理解,如果任务调用了上锁函数,但是又没有释放,即没有释放资源,那么很有可能其它任务需要那资源而发生优先级反转现象。然后在执行任务的切换。

任务级的任务切换和中断级的任务切换,其具体思路清参照我写得

给调度器上锁和开锁,给调度器上锁用于禁止任务调度,一般它开锁成对使用,其在UCOS中的实现代码很简单,只是要特别注意的是,上锁时要先关中断。

空闲任务,UCOS中至少要有一个任务,那就是空闲任务,不过其优先级最低,也就是说当其它任务没有进入就绪表时,它才运行,因为它总是处于就绪状态,它只是一个简单的计数器计数。在统计任务中要用到它。

统计任务,它就做一件事,就是计算CPU的利用率,可这过程我个人认为要一定的时间,因为里面尽是计算,并且牵涉到除法。在UCOS启动时,如果要启动统计任务,首先要把OS_CFG.H中的OS_TASK_STAT_EN设为1,那么这个任务就会建立,且一直处于就绪状态,并且刚开始时,由于空闲任务,肯定会运行的,运行多久,1秒,为什么是1秒,自己分析下代码就会明白啦,计数1秒又有什么用呢,为以后计算CPU的利用率提供一个基准值,因为这个值将会一直保存起来的,保存在哪,保存在统计任务的堆栈里,且不会改变,除非重新启动CPU;那么以后空闲任务不一定会有1秒的时间连续执行,空闲任务执行时,没次被其它任务抢走CPU时,它里面的计数器就会直接记录下CPU空闲的时间,怎么算呢,1s内它的计数值我们是知道的,那么CPU以后空闲的计数值我们也有记录,只是这些计算过程由统计任务来完成。

UCOS的初始化,它首先是系统所用的全局变量比如上锁次数,中断次数,任务次数,任务切换次数等等的初始化,然后是就绪列表、任务控制块、事件控制块等的建立,其次是空闲任务或和统计任务的创建。

UCOS的启动,找出就绪列表中的优先级最高的任务,并启动任务调度,且永不返回。

阅读(1901) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~