Board logo

标题: [转帖]从UCOS到多任务实现--初学者的一些认识 [打印本页]

作者: jjwwdd    时间: 2005-10-18 14:01     标题: [转帖]从UCOS到多任务实现--初学者的一些认识

前段时间学了学UCOS,有了点感想,想说说,它也许对DX来说是垃圾,但对有些人或许还有那么一丁点作用,如果其中有不妥之处,请各位指出来,
所谓的多任务有下面几个实现方式:
方案:
1.
    时间片轮回,每个任务分一个时间片,调度时到达该任务的时间片即开始运行该任务,达到并行的目的,这种方案缺点是任务响应的实时性与时间片的大小关系很大,时间片大,任务多时,一些任务可能会得不到及时的响应.但实现容易.
2.
    任务间使用信号量或者消息队列或者消息等机制通讯的发式,这种发式建立了任务间的通讯机制,一但有任务满足了执行条件,立刻开使执行,实时性较好.
3.
    任务间中断的方式,一旦有中断进行,即刻进入一个任务,实时性较高.
实现:
    1.任务的运行:每个任务即是一个死循环,该任务一直运行直到有其他任务到达运行时间或满足运行条件.
    2.任务的切换:其他任务到达运行时间或满足了运行条件,就需要切换到其它任务中,称作任务切换.相反,在其它任务运行中,如果又有任务满足执行条件,即刻任务切换.
    3.任务切换的实现:
          在此我们先假定任务间没有通讯,没有时间片的轮回,没有中断,一个任务仅仅是我们普通的一个死循环的函数.
    类似于这样的函数任务A:VOID TASK(VOID* P){FOR(;;){...}}
                    任务B:VOID TASK(VOID* P){FOR(;;){...}}
在这个假定下,我们设想有两个任务A,B.当前正在A中,由于A为死循环,正如我们想象的,它将无止境的运行下去,看来这样任务B是没指望得到机会运行了,怎么办呢,让我们一部一部将任务机制建立起来吧:
    A)让任务B得到运行:我们想到一个软中断可以使一段程序得到运行,我们试试它吧,以INTEL80X86CPU为例,一条INT N指令即可以将CPU的控制权交给中断象量在4*N地址的程序段,现在我们要做的就是将任务B程序起始地址放在4*N处,然后在任务A中假如INT N指令,
    现在任务A变成了这样VOID TASK(VOID* P){FOR(;;){...;INT N}}
         任务B仍然为:  VOID TASK(VOID* P){FOR(;;){...}}
    B)让任务A得到运行:现在就实现了我们的目的,任务A可以切换到任务B了,我们终于迈出了一步,编译,连接,运行我们惊奇的发现当任务A运行到INT N时,果然开始了任务B的运行,这样就实现了A到B的切换,可是随之而来我们发现任务B原来还是一个死循环,它无止境的运行以至于任务A被"饿死了",仔细一看原来是我们忘了也给任务B一个INT N了,好,仿照任务A,我们做类似的处理,这样他们就变成了下面这样:
     现在任务A变成了这样VOID TASK(VOID* P){FOR(;;){...;INT N}}
         任务B仍然为:  VOID TASK(VOID* P){FOR(;;){...;INT M}}
   编译,联结,运行,看看运行情况,这个时候两个任务果然每次到切换点INT X时开使互相切换,
     到这时,我们已经实现了最简单的多任务,但是这种方法大家已经看到,每个任务有一个中断号,即N!=M,似乎不是很好,那么下面我们让它们相等,即试着用一个中断号实现多任务的切换.
    C)任务内容的保存:我们可以想象的到,要使用同一个中断号,那么任务切换前必须预先将要切换到的任务的执行地址装入4*N处(以使用中断号N为例),怎么实现呢,当然有很多方法,其中比较好的一个方法就是每个任务对应一个存储区(任务STACK),该任务的起始地址都有保存于其中(这种方案当然不是仅仅为了保存任务地址而设),任务切换前,先访问该任务的任务STACK,取出该任务执行地址装入4*N处,然后执行任务切换,这样就达到了使用一个中断号的效果.
      现在任务A变成了这样VOID TASK(VOID* P){FOR(;;){...;安装N;INT N}}
         任务B仍然为:  VOID TASK(VOID* P){FOR(;;){...;安装N;INT N}}
   至此,我们已经构筑了多任务实现的一个机制,
  现在,我们回到前面提到的三个多任务实现的方案:
   1.时间片的实现:其实就照上面的方法已经是最简单的时间片实现了,较复杂的实现要结合任务通讯机制来实现.
   2.所谓的信号量(semaphore)其实就是一个标志位FLAG,消息(MESSAGE)机制其实就是一个指针,消息队列(MESSAGE QUEUE是多个消息组成的一个队列机制,拿最简单的信号量来说,最简单的通讯机制你可以这样:
        现在任务A变成了这样VOID TASK(VOID* P){FOR(;;){IF(CONDITION )SET FLAG;...;IF(FLAG){安装N;INT N}}}
         任务B仍然为:  VOID TASK(VOID* P){FOR(;;){...IF(CONDITION) SET FLAG;...;IF(FLAG){安装N;INT N}}
     不过,真正的任务通讯没有这样做的,基于任务切换,你可以设计你自己的任务通讯方式,你可以想出各种方法来实现这种通讯,关于任务间通讯的学问很大,基本上多任务实现的核心知识可能就是这个了.


   3.任务间中断的方式,就是任务中产生中段,标志某个事件发生或条件满足,然后满足该条件的任务得到执行.值得注意的是中断代码应该尽量短小精悍,占时少.
  
    最基本的多任务就是这样的!

    最后我再结合我自己说说如何学习UCOS.其实呢,只要你理解了我上面说的多任务切换的基本原理,然后再拿一个简单的实际例子,比如UCOS作者本人的著作“µC/OS, The Real-Time Kernel”中给出的例子中的一个,展开所有的调用函数,从头到尾,细细研读,再加上做者本人的讲解,理解应该不难.其中我要强调的是初始化函数OSInit(),把它展开,勾画出所有的数据结构,你就对UCOS所用的数据结构成竹在胸,在后面其它的阅读中追踪这些数据结构,你会发现其实UCOS很简单.一旦读懂一个例子,不论它使用哪个任务通讯机制,或是DELAY(),基本上大体框架你已经没问题了,后面再看其他例子,用到哪个通讯机制(其实也就三个semaphore,MESSAGE,QUEUE)查哪个,基本上做的事情大同小异.


以上仅是个人理解,如有不妥之处,还请各位指教.




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0