下载

2下载券

加入VIP
  • 专属下载特权
  • 现金文档折扣购买
  • VIP免费专区
  • 千万文档免费下载

上传资料

关闭

关闭

关闭

封号提示

内容

首页 第2章

第2章.doc

第2章

linwhwylb
2018-09-05 0人阅读 举报 0 0 暂无简介

简介:本文档为《第2章doc》,可适用于IT/计算机领域

第章实时系统概念前后台系统(ForegroundBackgroundSystem)代码的临界段资源共享资源多任务任务任务切换(ContextSwitchorTaskSwitch)内核(Kernel)调度(Scheduler)不可剥夺型内核(NonPreemptiveKernel)可剥夺型内核可重入性(Reentrancy)时间片轮番调度法任务优先级静态优先级动态优先级优先级反转任务优先级分配互斥条件关中断和开中断测试并置位禁止,然后允许任务切换信号量(Semaphores)死锁(或抱死)(Deadlock(orDeadlyEmbrace))同步事件标志(EventFlags)任务间的通讯(IntertaskCommunication)消息邮箱(MessageMailboxes)消息队列(MessageQueue)中断中断延迟中断响应中断恢复时间(InterruptRecovery)中断延迟、响应和恢复中断处理时间非屏蔽中断(NMI)时钟节拍(ClockTick)对存储器的需求使用实时内核的优缺点实时系统小结第章实时系统概念实时系统的特点是如果逻辑和时序出现偏差将会引起严重后果的系统。有两种类型的实时系统:软实时系统和硬实时系统。在软实时系统中系统的宗旨是使各个任务运行得越快越好并不要求限定某一任务必须在多长时间内完成。在硬实时系统中各任务不仅要执行无误而且要做到准时。大多数实时系统是二者的结合。实时系统的应用涵盖广泛的领域而多数实时系统又是嵌入式的。这意味着计算机建在系统内部用户看不到有个计算机在系统里面。以下是一些嵌入式系统的例子:实时应用软件的设计一般比非实时应用软件设计难一些。本章讲述实时系统概念。前后台系统(ForegroundBackgroundSystem)不复杂的小系统一般设计成如图所示的样子。这种系统可称为前后台系统或超循环系统(SuperLoops)。应用程序是一个无限的循环循环中调用相应的函数完成相应的操作这部分可以看成后台行为(background)。中断服务程序处理异步事件这部分可以看成前台行为(foreground)。后台也可以叫做任务级。前台也叫中断级。时间相关性很强的关键操作(Criticaloperation)一定是靠中断服务来保证的。因为中断服务提供的信息一直要等到后台程序走到该处理这个信息这一步时才能得到处理这种系统在处理信息的入运行着的任务。中断服务完成以后将CPU控制权还给被中断了的任务。任务级响应时间要大大好于前后系统但仍是不可知的商业软件几乎没有不可剥夺型内核。可剥夺型内核当系统响应时间很重要时要使用可剥夺型内核。因此μCOSⅡ以及绝大多数商业上销售的实时内核都是可剥夺型内核。最高优先级的任务一旦就绪总能得到CPU的控制权。当一个运行着的任务使一个比它优先级高的任务进入了就绪态当前任务的CPU使用权就被剥夺了或者说被挂起了那个高优先级的任务立刻得到了CPU的控制权。如果是中断服务子程序使一个高优先级的任务进入就绪态中断完成时中断了的任务被挂起优先级高的那个任务开始运行。如图所示。图可剥夺型内核使用可剥夺型内核最高优先级的任务什么时候可以执行可以得到CPU的控制权是可知的。使用可剥夺型内核使得任务级响应时间得以最优化。使用可剥夺型内核时应用程序不应直接使用不可重入型函数。调用不可重入型函数时要满足互斥条件这一点可以用互斥型信号量来实现。如果调用不可重入型函数时低优先级的任务CPU的使用权被高优先级任务剥夺不可重入型函数中的数据有可能被破坏。综上所述可剥夺型内核总是让就绪态的高优先级的任务先运行中断服务程序可以抢占CPU到中断服务完成时内核让此时优先级最高的任务运行(不一定是那个被中断了的任务)。任务级系统响应时间得到了最优化且是可知的。μCOSⅡ属于可剥夺型内核。可重入性(Reentrancy)可重入型函数可以被一个以上的任务调用而不必担心数据的破坏。可重入型函数任何时候都可以被中断一段时间以后又可以运行而相应数据不会丢失。可重入型函数或者只使用局部变量即变量保存在CPU寄存器中或堆栈中。如果使用全局变量则要对全局变量予以保护。程序是一个可重入型函数的例子。程序清单可重入型函数voidstrcpy(char*dest,char*src){while(*dest=*src){}*dest=NUL}函数Strcpy()做字符串复制。因为参数是存在堆栈中的故函数Strcpy()可以被多个任务调用而不必担心各任务调用函数期间会互相破坏对方的指针。不可重入型函数的例子如程序所示。Swap()是一个简单函数它使函数的两个形式变量的值互换。为便于讨论假定使用的是可剥夺型内核中断是开着的Temp定义为整数全程变量。程序清单不可重入型函数intTempvoidswap(int*x,int*y){Temp=*x*x=*y*y=Temp}程序员打算让Swap()函数可以为任何任务所调用如果一个低优先级的任务正在执行Swap()函数而此时中断发生了于是可能发生的事情如图所示。F()表示中断发生时Temp已被赋值中断服务子程序使更优先级的任务就绪当中断完成时F()内核(假定使用的是μCOSⅡ)使高优先级的那个任务得以运行F(),高优先级的任务调用Swap()函数是Temp赋值为。这对该任务本身来说实现两个变量的交换是没有问题的交换后Z的值是X的值是。然后高优先级的任务通过调用内核服务函数中的延迟一个时钟节拍F(),释放了CPU的使用权低优先级任务得以继续运行F()注意此时Temp的值仍为!在低优先级任务接着运行时Y的值被错误地赋为而不是正确值。图不可重入性函数请注意这只是一个简单的例子如何能使代码具有可重入性一看就明白。然而有些情况下问题并非那么易解。应用程序中的不可重入函数引起的错误很可能在测试时发现不了直到产品到了现场问题才出现。如果在多任务上您还是把新手使用不可重入型函数时千万要当心。使用以下技术之一即可使Swap()函数具有可重入性:·把Temp定义为局部变量·调用Swap()函数之前关中断调动后再开中断·用信号量禁止该函数在使用过程中被再次调用如果中断发生在Swap()函数调用之前或调用之后两个任务中的XY值都会是正确的。时间片轮番调度法当两个或两个以上任务有同样优先级内核允许一个任务运行事先确定的一段时间叫做时间额度(quantum)然后切换给另一个任务。也叫做时间片调度。内核在满足以下条件时把CPU控制权交给下一个任务就绪态的任务:·当前任务已无事可做·当前任务在时间片还没结束时已经完成了。目前μCOSⅡ不支持时间片轮番调度法。应用程序中各任务的优先级必须互不相同。任务优先级每个任务都有其优先级。任务越重要赋予的优先级应越高。静态优先级应用程序执行过程中诸任务优先级不变则称之为静态优先级。在静态优先级系统中诸任务以及它们的时间约束在程序编译时是已知的。动态优先级应用程序执行过程中任务的优先级是可变的则称之为动态优先级。实时内核应当避免出现优先级反转问题。优先级反转使用实时内核,优先级反转问题是实时系统中出现得最多的问题。图解释优先级反转是如何出现的。如图任务优先级高于任务任务优先级高于任务。任务和任务处于挂起状态等待某一事件的发生任务正在运行如图()。此时任务要使用其共享资源。使用共享资源之前首先必须得到该资源的信号量(Semaphore)(见信号量)。任务得到了该信号量并开始使用该共享资源图()。由于任务优先级高它等待的事件到来之后剥夺了任务的CPU使用权图()任务开始运行图()。运行过程中任务也要使用那个任务正在使用着的资源由于该资源的信号量还被任务占用着任务只能进入挂起状态等待任务释放该信号量图()。任务得以继续运行图()。由于任务的优先级高于任务当任务等待的事件发生后任务剥夺了任务的CPU的使用权图()并开始运行。处理它该处理的事件图()直到处理完之后将CPU控制权还给任图()。任务接着运行图()直到释放那个共享资源的信号量图()。直到此时,由于实时内核知道有个高优先级的任务在等待这个信号量内核做任务切换使任务得到该信号量并接着运行图()。在这种情况下,任务优先级实际上降到了任务的优先级水平。因为任务要等,直等到任务释放占有的那个共享资源。由于任务剥夺任务的CPU使用权,使任务的状况更加恶化,任务使任务增加了额外的延迟时间。任务和任务的优先级发生了反转。纠正的方法可以是,在任务使用共享资源时,提升任务的优先级。任务完成时予以恢复。任务的优先级必须升至最高,高于允许使用该资源的任何任务。多任务内核应允许动态改变任务的优先级以避免发生优先级反转现象。然而改变任务的优先级是很花时间的。如果任务并没有先被任务剥夺CPU使用权又被任务抢走了CPU使用权,花很多时间在共享资源使用前提升任务的优先级,然后又在资源使用后花时间恢复任务的优先级,则无形中浪费了很多CPU时间。真正需要的是,为防止发生优先级反转,内核能自动变换任务的优先级,这叫做优先级继承(Priorityinheritance)但μCOSⅡ不支持优先级继承,一些商业内核有优先级继承功能。图优先级反转问题图解释如果内核支持优先级继承的话,在上述例子中会是怎样一个过程。任务在运行图(),任务申请信号量以获得共享资源使用权图(),任务得到并开始使用共享资源图()。后来CPU使用权被任务剥夺图(),任务开始运行图(),任务申请共享资源信号量图()。此时,内核知道该信号量被任务占用了,而任务的优先级比任务低,内核于是将任务的优先级升至与任务一样,,然而回到任务继续运行,使用该共享资源图(),直到任务释放共享资源信号量图。()。这时,内核恢复任务本来的优先级并把信号量交给任务,任务得以顺利运行。图(),任务完成以后图()那些任务优先级在任务与任务之间的任务例如任务才能得到CPU使用权,并开始运行图()。注意,任务在从图()到图()的任何一刻都有可能进入就绪态,并不影响任务、任务的完成过程。在某种程度上,任务和任务之间也还是有不可避免的优先级反转。图任务优先级分配给任务定优先级可不是件小事因为实时系统相当复杂。许多系统中并非所有的任务都至关重要。不重要的任务自然优先级可以低一些。实时系统大多综合了软实时和硬实时这两种需求。软实时系统只是要求任务执行得尽量快并不要求在某一特定时间内完成。硬实时系统中任务不但要执行无误还要准时完成。一项有意思的技术可称之为单调执行率调度法RMS(RateMonotonicScheduling),用于分配任务优先级。这种方法基于哪个任务执行的次数最频繁,执行最频繁的任务优先级最高。见图。图基于任务执行频繁度的优先级分配法任务执行频繁度(Hz)RMS做了一系列假设:·所有任务都是周期性的·任务间不需要同步没有共享资源,没有任务间数据交换等问题·CPU必须总是执行那个优先级最高且处于就绪态的任务。换句话说要使用可剥夺型调度法。给出一系列n值表示系统中的不同任务数要使所有的任务满足硬实时条件必须使不等式成立这就是RMS定理:这里Ei是任务i最长执行时间Ti是任务i的执行周期。换句话说EiTi是任务i所需的CPU时间。表给出n(n)的值n是系统中的任务数。对于无穷多个任务极限值是或。这就意味着基于RMS要任务都满足硬实时条件所有有时间条件要求的任务i总的CPU利用时间应小于!请注意这是指有时间条件要求的任务系统中当然还可以有对时间没有什么要求的任务使得CPU的利用率达到。使CPU利用率达到并不好因为那样的话程序就没有了修改的余地也没法增加新功能了。作为系统设计的一条原则CPU利用率应小于到。RMS认为最高执行率的任务具有最高的优先级但最某些情况下最高执行率的任务并非是最重要的任务。如果实际应用都真的像RMS说的那样也就没有什么优先级分配可讨论了。然而讨论优先级分配问题RMS无疑是一个有意思的起点。表基于任务到CPU最高允许使用率任务数n(n)∞互斥条件实现任务间通讯最简便到办法是使用共享数据结构。特别是当所有到任务都在一个单一地址空间下能使用全程变量、指针、缓冲区、链表、循环缓冲区等使用共享数据结构通讯就更为容易。虽然共享数据区法简化了任务间的信息交换但是必须保证每个任务在处理共享数据时的排它性以避免竞争和数据的破坏。与共享资源打交道时使之满足互斥条件最一般的方法有:·关中断·使用测试并置位指令·禁止做任务切换·利用信号量关中断和开中断处理共享数据时保证互斥最简便快捷的办法是关中断和开中断。如示意性代码程序所示:程序清单关中断和开中断Disableinterrupts*关中断*Accesstheresource(readwritefromtovariables)*读写变量*Reenableinterrupts*重新允许中断*μCOSⅡ在处理内部变量和数据结构时就是使用的这种手段即使不是全部也是绝大部分。实际上μCOSⅡ提供两个宏调用允许用户在应用程序的C代码中关中断然后再开中断:OSENTERCRITICAL()和OSEXITCRITICAL()参见OSENTERCRITICAL()和OSEXITCRITICALL(),这两个宏调用的使用法见程序程序清单利用μCOSⅡ宏调用关中断和开中断voidFunction(void){OSENTERCRITICAL()*在这里处理共享数据*OSEXITCRITICAL()}可是必须十分小心关中断的时间不能太长。因为它影响整个系统的中断响应时间即中断延迟时间。当改变或复制某几个变量的值时应想到用这种方法来做。这也是在中断服务子程序中处理共享变量或共享数据结构的唯一方法。在任何情况下关中断的时间都要尽量短。如果使用某种实时内核一般地说关中断的最长时间不超过内核本身的关中断时间就不会影响系统中断延迟。当然得知道内核里中断关了多久。凡好的实时内核厂商都提供这方面的数据。总而言之要想出售实时内核时间特性最重要。测试并置位如果不使用实时内核当两个任务共享一个资源时一定要约定好先测试某一全程变量如果该变量是允许该任务与共享资源打交道。为防止另一任务也要使用该资源前者只要简单地将全程变量置为这通常称作测试并置位(TestAndSet),或称作TAS。TAS操作可能是微处理器的单独一条不会被中断的指令或者是在程序中关中断做TAS操作再开中断如程序清单所示。程序清单利用测试并置位处理共享资源Disableinterrupts关中断if(‘AccessVariable’is){如果资源不可用标志为Setvariableto置资源不可用标志为Reenableinterrupts重开中断Accesstheresource处理该资源Disableinterrupts关中断Setthe‘AccessVariable’backto清资源不可使用标志为Reenableinterrupts重新开中断}else{否则Reenableinterrupts开中断*Youdon’thaveaccesstotheresource,trybacklater**资源不可使用以后再试*}有的微处理器有硬件的TAS指令(如Motorola系列,就有这条指令)禁止,然后允许任务切换如果任务不与中断服务子程序共享变量或数据结构,可以使用禁止、然后允许任务切换。(参见给任务切换上锁和开锁)。如程序清单所示以μCOSⅡ的使用为例两个或两个以上的任务可以共享数据而不发生竞争。注意此时虽然任务切换是禁止了但中断还是开着的。如果这时中断来了中断服务子程序会在这一临界区内立即执行。中断服务子程序结束时尽管有优先级高的任务已经进入就绪态内核还是返回到原来被中断了的任务。直到执行完给任务切换开锁函数OSSchedUnlock(),内核再看有没有优先级更高的任务被中断服务子程序激活而进入就绪态如果有则做任务切换。虽然这种方法是可行的但应该尽量避免禁止任务切换之类操作因为内核最主要的功能就是做任务的调度与协调。禁止任务切换显然与内核的初衷相违。应该使用下述方法。程序清单用给任务切换上锁然后开锁的方法实现数据共享voidFunction(void){OSSchedLock()*Youcanaccessshareddatainhere(interruptsarerecognized)**在这里处理共享数据(中断是开着的)*OSSchedUnlock()}信号量(Semaphores)信号量是年代中期EdgserDijkstra发明的。信号量实际上是一种约定机制在多任务内核中普遍使用信号量用于:·控制共享资源的使用权(满足互斥条件)·标志某事件的发生·使两个任务的行为同步(译者注:信号与信号量在英文中都叫做Semaphore并不加以区分而说它有两种类型二进制型(binary)和计数器型(counting)。本书中的二进制型信号量实际上是只取两个值和的信号量。实际上这个信号量只有一位这种信号量翻译为信号更为贴切。而二进制信号量通常指若干位的组合。而本书中解释为事件标志的置位与清除(见))。信号像是一把钥匙任务要运行下去得先拿到这把钥匙。如果信号已被别的任务占用该任务只得被挂起直到信号被当前使用者释放。换句话说申请信号的任务是在说:“把钥匙给我如果谁正在用着我只好等!”信号是只有两个值的变量信号量是计数式的。只取两个值的信号是只有两个值和的量因此也称之为信号量。计数式信号量的值可以是到或到或到取决于信号量规约机制使用的是位、位还是位。到底是几位实际上是取决于用的哪种内核。根据信号量的值内核跟踪那些等待信号量的任务。一般地说对信号量只能实施三种操作:初始化(INITIALIZE)也可称作建立(CREATE)等信号(WAIT)也可称作挂起(PEND)给信号(SIGNAL)或发信号(POST)。信号量初始化时要给信号量赋初值等待信号量的任务表(Waitinglist)应清为空。想要得到信号量的任务执行等待(WAIT)操作。如果该信号量有效(即信号量值大于)则信号量值减任务得以继续运行。如果信号量的值为等待信号量的任务就被列入等待信号量任务表。多数内核允许用户定义等待超时如果等待时间超过了某一设定值时该信号量还是无效则等待信号量的任务进入就绪态准备运行并返回出错代码(指出发生了等待超时错误)。任务以发信号操作(SIGNAL)释放信号量。如果没有任务在等待信号量信号量的值仅仅是简单地加。如果有任务在等待该信号量那么就会有一个任务进入就绪态信号量的值也就不加。于是钥匙给了等待信号量的诸任务中的一个任务。至于给了那个任务要看内核是如何调度的。收到信号量的任务可能是以下两者之一。·等待信号量任务中优先级最高的或者是·最早开始等待信号量的那个任务即按先进先出的原则(FirstInFirstOutFIFO)有的内核有选择项允许用户在信号量初始化时选定上述两种方法中的一种。但μCOSⅡ只支持优先级法。如果进入就绪态的任务比当前运行的任务优先级高(假设是当前任务释放的信号量激活了比自己优先级高的任务)。则内核做任务切换(假设使用的是可剥夺型内核)高优先级的任务开始运行。当前任务被挂起。直到又变成就绪态中优先级最高任务。程序清单示意在μCOSⅡ中如何用信号量处理共享数据。要与同一共享数据打交道的任务调用等待信号量函数OSSemPend()。处理完共享数据以后再调用释放信号量函数OSSemPost()。这两个函数将在以后的章节中描述。要注意的是在使用信号量之前一定要对该信号量做初始化。作为互斥条件信号量初始化为。使用信号量处理共享数据不增加中断延迟时间如果中断服务程序或当前任务激活了一个高优先级的任务高优先级的任务立即开始执行。程序清单通过获得信号量处理共享数据OSEVENT*SharedDataSemvoidFunction(void)INTUerrOSSemPend(SharedDataSem,,err)*Youcanaccessshareddatainhere(interruptsarerecognized)**共享数据的处理在此进行(中断是开着的)*OSSemPost(SharedDataSem)}当诸任务共享输入输出设备时信号量特别有用。可以想象如果允许两个任务同时给打印机送数据时会出现什么现象。打印机会打出相互交叉的两个任务的数据。例如任务要打印“IamTask!”而任务要打印“IamTask!”可能打印出来的结果是:“IIaammTTasaskk!!”在这种情况下使用信号量并给信号量赋初值(用二进制信号量)。规则很简单要想使用打印机的任务先要得到该资源的信号量。图两个任务竞争得到排它性打印机使用权图中信号量用一把钥匙表示想使用打印机先要得到这把钥匙。图用获取信号量来得到打印机使用权上例中每个任务都知道有个信号表示资源可不可以使用。要想使用该资源要先得到这个信号。然而有些情况下最好把信号量藏起来各个任务在同某一资源打交道时并不知道实际上是在申请得到一个信号量。例如多任务共享一个RSC外设接口各任务要送命令给接口另一端的设备并接收该设备的回应。如图所示。调用向串行口发送命令的函数CommSendCmd()该函数有三个形式参数:Cmd指向送出的ASCII码字符串命令。Response指向外设回应的字符串。timeout指设定的时间间隔。如果超过这段时间外设还不响应则返回超时错误信息。函数的示意代码如程序清单所示。程序清单隐含的信号量。INTUCommSendCmd(char*cmd,char*response,INTUtimeout){Acquireport'ssemaphoreSendcommandtodeviceWaitforresponse(withtimeout)if(timedout){Releasesemaphorereturn(errorcode)}else{Releasesemaphorereturn(noerror)}}要向外设发送命令的任务得调用上述函数。设信号量初值为表示允许使用。初始化是在通讯口驱动程序的初始化部分完成的。第一个调用CommSendCmd()函数的任务申请并得到了信号量开始向外设发送命令并等待响应。而另一个任务也要送命令此时外设正“忙”则第二个任务被挂起直到该信号量重新被释放。第二个任务看起来同调用了一个普通函数一样只不过这个函数在没有完成其相应功能时不返回。当第一个任务释放了那个信号量第二个任务得到了该信号量第二个任务才能使用RS口。图在任务级看不到隐含的信号量计数式信号量用于某资源可以同时为几个任务所用。例如用信号量管理缓冲区阵列(bufferpool)如图所示。缓冲区阵列中共有个缓冲区任务通过调用申请缓冲区函数BufReq()向缓冲区管理方申请得到缓冲区使用权。当缓冲区使用权还不再需要时通过调用释放缓冲区函数BufRel()将缓冲区还给管方。函数示意码如程序清单所示程序清单用信号量管理缓冲区。BUF*BufReq(void){BUF*ptrAcquireasemaphoreDisableinterruptsptr=BufFreeListBufFreeList=ptr>BufNextEnableinterruptsreturn(ptr)}voidBufRel(BUF*ptr){Disableinterruptsptr>BufNext=BufFreeListBufFreeList=ptrEnableinterruptsReleasesemaphore}图计数式信号量的用法缓冲区阵列管理方满足前十个申请缓冲区的任务就好像有把钥匙可以发给诸任务。当所有的钥匙都用完了申请缓冲区的任务被挂起直到信号量重新变为有效。缓冲区管理程序在处理链表指针时为满足互斥条件中断是关掉的(这一操作非常快)。任务使用完某一缓冲区通过调用缓冲区释放函数BufRel()将缓冲区还给系统。系统先将该缓冲区指针插入到空闲缓冲区链表中(Linkedlist)然后再给信号量加或释放该信号量。这一过程隐含在缓冲区管理程序BufReq()和BufRel()之中调用这两个函数的任务不用管函数内部的详细过程。信号量常被用过了头。处理简单的共享变量也使用信号量则是多余的。请求和释放信号量的过程是要花相当的时间的。有时这种额外的负荷是不必要的。用户可能只需要关中断、开中断来处理简单共享变量以提高效率。(参见关中断和开中断)。假如两个任务共享一个位的整数变量一个任务给这个变量加另一个任务给这个变量清。如果注意到不管哪种操作对微处理器来说只花极短的时间就不会使用信号量来满足互斥条件了。每个任务只需操作这个任务前关中断之后再开中断就可以了。然而如果这个变量是浮点数而相应微处理器又没有硬件的浮点协处理器浮点运算的时间相当长关中断时间长了会影响中断延迟时间这种情况下就有必要使用信号量了。死锁(或抱死)(Deadlock(orDeadlyEmbrace))死锁也称作抱死指两个任务无限期地互相等待对方控制着的资源。设任务T正独享资源R任务T在独享资源T而此时T又要独享RT也要独享R于是哪个任务都没法继续执行了发生了死锁。最简单的防止发生死锁的方法是让每个任务都:·先得到全部需要的资源再做下一步的工作·用同样的顺序去申请多个资源·释放资源时使用相反的顺序内核大多允许用户在申请信号量时定义等待超时以此化解死锁。当等待时间超过了某一确定值信号量还是无效状态就会返回某种形式的出现超时错误的代码这个出错代码告知该任务不是得到了资源使用权而是系统错误。死锁一般发生在大型多任务系统中在嵌入式系统中不易出现。同步可以利用信号量使某任务与中断服务同步(或者是与另一个任务同步这两个任务间没有数据交换)。如图所示。注意图中用一面旗帜或称作一个标志表示信号量。这个标志表示某一事件的发生(不再是一把用来保证互斥条件的钥匙)。用来实现同步机制的信号量初始化成信号量用于这种类型同步的称作单向同步(unilateralrendezvous)。一个任务做IO操作然后等信号回应。当IO操作完成中断服务程序(或另外一个任务)发出信号该任务得到信号后继续往下执行。图用信号量使任务与中断服务同步如果内核支持计数式信号量信号量的值表示尚未得到处理的事件数。请注意可能会有一个以上的任务在等待同一事件的发生则这种情况下内核会根据以下原则之一发信号给相应的任务:·发信号给等待事件发生的任务中优先级最高的任务或者·发信号给最先开始等待事件发生的那个任务根据不同的应用发信号以标识事件发生的中断服务或任务也可以是多个。两个任务可以用两个信号量同步它们的行为。如图所示。这叫做双向同步(bilateralrendezvous)。双向同步同单向同步类似只是两个任务要相互同步。例如则程序清单中运行到某一处的第一个任务发信号给第二个任务L()然后等待信号返回L()。同样当第二个任务运行到某一处时发信号给第一个任务()等待返回信号L()。至此两个任务实现了互相同步。在任务与中断服务之间不能使用双向同步因为在中断服务中不可能等一个信号量。图两个任务用信号量同步彼此的行为程序清单双向同步Task(){for(){PerformoperationSignaltask#()Waitforsignalfromtask#()Continueoperation}}Task(){for(){PerformoperationSignaltask#()Waitforsignalfromtask#()Continueoperation}}事件标志(EventFlags)当某任务要与多个事件同步时要使用事件标志。若任务需要与任何事件之一发生同步可称为独立型同步(即逻辑或关系)。任务也可以与若干事件都发生了同步称之为关联型(逻辑与关系)。独立型及关联型同步如图所示。图独立型及关联型同步可以用多个事件的组合发信号给多个任务。如图所示典型地个、个或个事件可以组合在一起取决于用的哪种内核。每个事件占一位(bit)以位的情况为多。任务或中断服务可以给某一位置位或复位当任务所需的事件都发生了该任务继续执行至于哪个任务该继续执行了是在一组新的事件发生时辨定的。也就是在事件位置位时做辨断。内核支持事件标志提供事件标志置位、事件标志清零和等待事件标志等服务。事件标志可以是独立型或组合型。μCOSⅡ目前不支持事件标志任务间的通讯(IntertaskCommunication)有时很需要任务间的或中断服务与任务间的通讯。这种信息传递称为任务间的通讯。任务间信息的传递有两个途径:通过全程变量或发消息给另一个任务。用全程变量时必须保证每个任务或中断服务程序独享该变量。中断服务中保证独享的唯一办法是关中断。如果两个任务共享某变量各任务实现独享该变量的办法可以是关中断再开中断或使用信号量(如前面提到的那样)。请注意任务只能通过全程变量与中断服务程序通讯而任务并不知道什么时候全程变量被中断服务程序修改了除非中断程序以信号量方式向任务发信号或者是该任务以查询方式不断周期性地查询变量的值。要避免这种情况用户可以考虑使用邮箱或消息队列。图事件标志消息邮箱(MessageMailboxes)通过内核服务可以给任务发送消息。典型的消息邮箱也称作交换消息是用一个指针型变量通过内核服务一个任务或一个中断服务程序可以把一则消息(即一个指针)放到邮箱里去。同样一个或多个任务可以通过内核服务接收这则消息。发送消息的任务和接收消息的任务约定该指针指向的内容就是那则消息。每个邮箱有相应的正在等待消息的任务列表要得到消息的任务会因为邮箱是空的而被挂起且被记录到等待消息的任务表中直到收到消息。一般地说内核允许用户定义等待超时等待消息的时间超过了仍然没有收到该消息这任务进入就绪态并返回出错信息报告等待超时错误。消息放入邮箱后或者是把消息传给等待消息的任务表中优先级最高的那个任务(基于优先级)或者是将消息传给最先开始等待消息的任务(基于先进先出)。图示意把消息放入邮箱。用一个I字表示邮箱旁边的小砂漏表示超时计时器计时器旁边的数字表示定时器设定值即任务最长可以等多少个时钟节拍(ClockTicks)关于时钟节拍以后会讲到。内核一般提供以下邮箱服务:·邮箱内消息的内容初始化邮箱里最初可以有也可以没有消息·将消息放入邮箱(POST)·等待有消息进入邮箱(PEND)·如果邮箱内有消息就接受这则消息。如果邮箱里没有消息则任务并不被挂起(ACCEPT)用返回代码表示调用结果是收到了消息还是没有收到消息。消息邮箱也可以当作只取两个值的信号量来用。邮箱里有消息表示资源可以使用而空邮箱表示资源已被其它任务占用。图消息邮箱消息队列(MessageQueue)消息队列用于给任务发消息。消息队列实际上是邮箱阵列。通过内核提供的服务任务或中断服务子程序可以将一条消息(该消息的指针)放入消息队列。同样一个或多个任务可以通过内核服务从消息队列中得到消息。发送和接收消息的任务约定传递的消息实际上是传递的指针指向的内容。通常先进入消息队列的消息先传给任务也就是说任务先得到的是最先进入消息队列的消息即先进先出原则(FIFO)。然而μCOSⅡ也允许使用后进先出方式(LIFO)。像使用邮箱那样当一个以上的任务要从消息队列接收消息时每个消息队列有一张等待消息任务的等待列表(WaitingList)。如果消息队列中没有消息即消息队列是空等待消息的任务就被挂起并放入等待消息任务列表中直到有消息到来。通常内核允许等待消息的任务定义等待超时的时间。如果限定时间内任务没有收到消息该任务就进入就绪态并开始运行同时返回出错代码指出出现等待超时错误。一旦一则消息放入消息队列该消息将传给等待消息的任务中优先级最高的那个任务或是最先进入等待消息任务列表的任务。图示意中断服务子程序如何将消息放入消息队列。图中两个大写的I表示消息队列“”表示消息队列最多可以放条消息沙漏旁边的表示任务没有定义超时将永远等下去直至消息的到来。典型地内核提供的消息队列服务如下:·消息队列初始化。队列初始化时总是清为空。·放一则消息到队列中去(Post)·等待一则消息的到来(Pend)·如果队列中有消息则任务可以得到消息但如果此时队列为空内核并不将该任务挂起(Accept)。如果有消息则消息从队列中取走。没有消息则用特别的返回代码通知调用者队列中没有消息。图消息队列中断中断是一种硬件机制用于通知CPU有个异步事件发生了。中断一旦被识别CPU保存部分(或全部)现场(Context)即部分或全部寄存器的值跳转到专门的子程序称为中断服务子程序(ISR)。中断服务子程序做事件处理处理完成后程序回到:·在前后台系统中程序回到后台程序·对不可剥夺型内核而言程序回到被中断了的任务·对可剥夺型内核而言让进入就绪态的优先级最高的任务开始运行中断使得CPU可以在事件发生时才予以处理而不必让微处理器连续不断地查询(Polling)是否有事件发生。通过两条特殊指令:关中断(Disableinterrupt)和开中断(Enableinterrupt)可以让微处理器不响应或响应中断。在实时环境中关中断的时间应尽量的短。关中断影响中断延迟时间(见中断延迟)。关中断时间太长可能会引起中断丢失。微处理器一般允许中断嵌套也就是说在中断服务期间微处理器可以识别另一个更重要的中断并服务于那个更重要的中断如图所示。中断延迟可能实时内核最重要的指标就是中断关了多长时间。所有实时系统在进入临界区代码段之前都要关中断执行完临界代码之后再开中断。关中断的时间越长中断延迟就越长。中断延迟由表达式给出。中断延迟=关中断的最长时间开始执行中断服务子程序的第一条指令的时间图中断嵌套中断响应中断响应定义为从中断发生到开始执行用户的中断服务子程序代码来处理这个中断的时间。中断响应时间包括开始处理这个中断前的全部开销。典型地执行用户代码之前要保护现场将CPU的各寄存器推入堆栈。这段时间将被记作中断响应时间。对前后台系统保存寄存器以后立即执行用户代码中断响应时间由给出。中断响应时间=中断延迟保存CPU内部寄存器的时间对于不可剥夺型内核微处理器保存内部寄存器以后用户的中断服务子程序代码全立即得到执行。不可剥夺型内核的中断响应时间由表达式给出。中断响应时间=中断延迟保存CPU内部寄存器的时间对于可剥夺型内核则要先调用一个特定的函数该函数通知内核即将进行中断服务使得内核可以跟踪中断的嵌套。对于μCOSⅡ说来这个函数是OSIntEnter()可剥夺型内核的中断响应时间由表达式给出:中断响应=中断延迟保存CPU内部寄存器的时间内核的进入中断服务函数的执行时间中断响应是系统在最坏情况下的响应中断的时间某系统次中有次在μs之内响应中断只有一次响应中断的时间是μs只能认为中断响应时间是μs。中断恢复时间(InterruptRecovery)中断恢复时间定义为微处理器返回到被中断了的程序代码所需要的时间。在前后台系统中中断恢复时间很简单只包括恢复CPU内部寄存器值的时间和执行中断返回指令的时间。中断恢复时间由式给出。中断恢复时间=恢复CPU内部寄存器值的时间执行中断返回指令的时间和前后台系统一样不可剥夺型内核的中断恢复时间也很简单只包括恢复CPU内部寄存器值的时间和执行中断返回指令的时间如表达式所示。中断恢复时间=恢复CPU内部寄存器值的时间执行中断返回指令的时间对于可剥夺型内核中断的恢复要复杂一些。典型地在中断服务子程序的末尾要调用一个由实时内核提供的函数。在μCOSⅡ中这个函数叫做OSIntExit()这个函数用于辨定中断是否脱离了所有的中断嵌套。如果脱离了嵌套(即已经可以返回到被中断了的任务级时)内核要辨定由于中断服务子程序ISR的执行是否使得一个优先级更高的任务进入了就绪态。如果是则要让这个优先级更高的任务开始运行。在这种情况下被中断了的任务只有重新成为优先级最高的任务而进入就绪态时才能继续运行。对于可剥夺型内核中断恢复时间由表达式给出。中断恢复时间=判定是否有优先级更高的任务进入了就绪态的时间恢复那个优先级更高任务的CPU内部寄存器的时间执行中断返回指令的时间中断延迟、响应和恢复图到图示意前后台系统、不可剥夺性内核、可剥夺性内核相应的中断延迟、响应和恢复过程。注意对于可剥夺型实时内核中断返回函数将决定是返回到被中断的任务图A还是让那个优先级最高任务运行。是中断服务子程序使那个优先级更高的任务进入了就绪态图B。在后一种情况下恢复中断的时间要稍长一些因为内核要做任务切换。在本书中我做了一张执行时间表此表多少可以衡量执行时间的不同假定μCOSⅡ是在MHZIntel微处理器上运行的。此表可以使读者看到做任务切换的时间开销。(见表在MHZ上μCOSⅡ服务的执行时间)中断处理时间虽然中断服务的处理时间应该尽可能的短但是对处理时间并没有绝对的限制。不能说中断服务必须全部小于μSμS或mS。如果中断服务是在任何给定的时间开始且中断服务程序代码是应用程序中最重要的代码则中断服务需要多长时间就应该给它多长时间。然而在大多数情况下中断服务子程序应识别中断来源从叫中断的设备取得数据或状态并通知真正做该事件处理的那个任务。当然应该考虑到是否通知一个任务去做事件处理所花的时间比处理这个事件所花的时间还多。在中断服务中通知一个任务做时间处理(通过信号量、邮箱或消息队列)是需要一定时间的如果事件处理需花的时间短于给一个任务发通知的时间就应该考虑在中断服务子程序中做事件处理并在中断服务子程序中开中断以允许优先级更高的中断打入并优先得到服务。图中断延迟、响应和恢复(前后台模式)非屏蔽中断(NMI)有时中断服务必须来得尽可能地快内核引起的延时变得不可忍受。在这种情况下可以使用非屏蔽中断绝大多数微处理器有非屏蔽中断功能。通常非屏蔽中断留做紧急处理用如断电时保存重要的信息。然而如果应用程序没有这方面的要求非屏蔽中断可用于时间要求最苛刻的中断服务。下列表达式给出如何确定中断延迟、中断响应时间和中断恢复时间。中断延迟时间=指令执行时间中最长的那个时间开始做非屏蔽中断服务的时间中断响应时间=中断延迟时间保存CPU寄存器花的时间中断恢复时间=恢复CPU寄存器的时间执行中断返回指令的时间。在一项应用中我将非屏蔽中断用于可能每μS发生一次的中断。中断处理时间在至μS之间。所使用的内核的关中断时间是μS。可以看出如果使用可屏蔽中断的话中断响应会推迟μS。在非屏蔽中断的中断服务子程序中不能使用内核提供的服务因为非屏蔽中断是关不掉的故不能在非屏蔽中断处理中处理临界区代码。然而向非屏蔽中断传送参数或从非屏蔽中断获取参数还是可以进行的。参数的传递必须使用全程变量全程变量的位数必须是一次读或写能完成的即不应该是两个分离的字节要两次读或写才能完成。图中断延迟、响应和恢复(不可剥夺型内核)图中断延迟、响应和恢复(可剥夺型内核)非屏蔽中断可以用增加外部电路的方法禁止掉如图所示。假定中断源和非屏蔽中断都是正逻辑用一个简单的“与”门插在中断源和微处理器的非屏蔽中断输入端之间。向输出口(OutputPort)写就将中断关了。不一定要以这种关中断方式来使用内核服务但可以用这种方式在中断服务子程序和任务之间传递参数(大的、多字节的一次读写不能完成的变量)。图非屏蔽中断的禁止假定非屏蔽中断服务子程序每次执行中有一次要给任务发信号如果非屏蔽中断μS执行一次则每mS(*μS)给任务发一次信号。在非屏蔽中断服务子程序中不能使用内核服务给任务发信号但可以使用如图所示的中断机制。即用非屏蔽中断产生普通可屏蔽中断的机制。在这种情况下非屏蔽中断通过某一输出口产生硬件中断(置输出口为有效电平)。由于非屏蔽中断服务通常具有最高的优先级在非屏蔽中断服务过程中不允许中断嵌套普通中断一直要等到非屏蔽中断服务子程序运行结束后才能被识别。在非屏蔽中断服务子程序完成以后微处理器开始响应这个硬件中断。在这个中断服务子程序中要清除中断源(置输出口为无效电平)然后用信号量去唤醒那个需要唤醒的任务。任务本身的运行时间和信号量的有效时间都接近mS实时性得到了满足。图非屏蔽中断产生普通可屏蔽中断时钟节拍(ClockTick)时钟节拍是特定的周期性中断。这个中断可以看作是系统心脏的脉动。中断之间的时间间隔取决于不同的应用一般在mS到mS之间。时钟的节拍式中断使得内核可以将任务延时若干个整数时钟节拍以及当任务等待事件发生时提供等待超时的依据。时钟节拍率越快系统的额外开销就越大。各种实时内核都有将任务延时若干个时钟节拍的功能。然而这并不意味着延时的精度是个时钟节拍只是在每个时钟节拍中断到来时对任务延时做一次裁决而已。图到图示意任务将自身延迟一个时钟节拍的时序。阴影部分是各部分程序的执行时间。请注意相应的程序运行时间是长短不一的这反映了程序中含有循环和条件转移语句(即ifelse,switch,:等语句)的典型情况。时间节拍中断服务子程序的运行时间也是不一样的。尽管在图中画得有所夸大。第一种情况如图所示优先级高的任务和中断服务超前于要求延时一个时钟节拍的任务运行。可以看出虽然该任务想要延时mS但由于其优先级的缘故实际上每次延时多少是变化的这就引起了任务执行时间的抖动。第二种情况如图所示所有高优先级的任务和中断服务的执行时间略微小于一个时钟节拍。如果任务将自己延时一个时钟节拍的请求刚好发生在下一个时钟节拍之前这个任务的再次执行几乎是立即开始的。因此如果要求任务的延迟至少为一个时钟节拍的话则要多定义一个延时时钟节拍。换句话说如果想要将一个任务至少延迟个时钟节拍的话得在程序中延时个时钟节拍。图将任务延迟一个时钟节拍(第一种情况)图将任务延迟一个时钟节拍(第二种情况)图将任务延迟一个时钟节拍(第三种情况)第三种情况如图所示所有高优先级的任务加上中断服务的执行时间长于一个时钟节拍。在这种情况下拟延迟一个时钟节拍的任务实际上在两个时钟节拍后开始运行引起了延迟时间超差。这在某些应用中或许是可以的而在多数情况下是不可接受的。上述情况在所有的实时内核中都会出现这与CPU负荷有关也可能与系统设计不正确有关。以下是这类问题可能的解决方案:·增加微处理器的时钟频率·增加时钟节拍的频率·重新安排任务的优先级·避免使用浮点运算(如果非使用不可尽量用单精度数)·使用能较好地优化程序代码的编译器·时间要求苛刻的代码用汇编语言写·如果可能用同一家族的更快的微处理器做系统升级。如从向升级从向升级等不管怎么样抖动总是存在的。对存储器的需求如果设计是前后台系统对存储器容量的需求仅仅取决于应用程序代码。而使用多任务内核时的情况则很不一样。内核本身需要额外的代码空间(ROM)。内核的大小取决于多种因素取决于内核的特性从K到K字节都是可能的。位CPU用的最小内核只提供任务调度、任务切换、信号量处理、延时及超时服务约需要K到K代码空间。代码空间总需求量由表达式给出。总代码量=应用程序代码内核代码因为每个任务都是独立运行的必须给每个任务提供单独的栈空间(RAM)。应用程序设计人员决定分配给每个任务多少栈空间时应该尽可能使之接近实际需求量(有时这是相当困难的一件事)。栈空间的大小不仅仅要计算任务本身的需求(局部变量、函数调用等等)还需要计算最多中断嵌套层数(保存寄存器、中断服务程序中的局部变量等)。根据不同的目标微处理器和内核的类型任务栈和系统栈可以是分开的。系统栈专门用于处理中断级代码。这样做有许多好处每个任务需要的栈空间可以大大减少。内核的另一个应该具有的性能是每个任务所需的栈空间大小可以分别定义(µCOS-II可以做到)。相反有些内核要求每个任务所需的栈空间都相同。所有内核都需要额外的栈空间以保证内部变量、数据结构、队列等。如果内核不支持单独的中断用栈总的RAM需求由表达式给出。RAM总需求=应用程序的RAM需求(任务栈需求最大中断嵌套栈需求)*任务数如果内核支持中断用栈分离总RAM需求量由表达式给出=RAM总需求=应用程序的RAM需求内核数据区的RAM需求各任务栈需求之总和最多中断嵌套之栈需求除非有特别大的RAM空间可以所用对栈空间的分配与使用要非常小心。为减少应用程序需要的RAM空间对每个任务栈空间的使用都要非常小心特别要注意以下几点:·定义函数和中断服务子程序中的局部变量特别是定义大型数组和数据结构·函数(即子程序)的嵌套·中断嵌套·库函数需要的栈空间·多变元的函数调用综上所述多任务系统比前后台系统需要更多的代码空间(ROM)和数据空间(RAM)。额外的代码空间取决于内核的大小而RAM的用量取决于系统中的任务数。使用实时内核的优缺点实时内核也称为实时操作系统或RTOS。它的使用使得实时应用程序的设计和扩展变得容易不需要大的改动就可以增加新的功能。通过将应用程序分割成若干独立的任务RTOS使得应用程序的设计过程大为减化。使用可剥夺性内核时所有时间要求苛刻的事件都得到了尽可能快捷、有效的处理。通过有效的服务如信号量、邮箱、队列、延时、超时等RTOS使得资源得到更好的利用。如果应用项目对额外的需求可以承受应该考虑使用实时内核。这些额外的需求是:内核的价格额外的ROMRAM开销到百分点的CPU额外负荷。还没有提到的一个因素是使用实时内核增加的价格成本。在一些应用中价格就是一切以至于对使用RTOS连想都不敢想。当今有个以上的RTOS商家生产面向位、位、位、甚至是位的微处理器的RTOS产品。一些软件包是完整的操作系统不仅包括实时内核还包括输入输出管理、视窗系统(用于显示)、文件系统、网络、语言接口库、调试软件、交叉平台编译(CrossPlatformcompilers)。RTOS的价格从美元到,美元。RTOS制造商还可能索取每个目标系统的版权使用费。就像从RTOS商家那买一个芯片安装到每一个产品上然后一同出售。RTOS商家称之为硅片软件(SiliconSoftware)。每个产品的版权费从美元到美元不等。同如今的其它软件包一样还得考虑软件维护费这部分开销为每年还得花到,美元!实时系统小结三种类型的实时系统归纳于表中这三种实时系统是:前后台系统不可剥夺型内核和可剥夺型内核。表实时系统小结ForegroundBackgroundNonPreemptiveKernelPreemptiveKernelInterruptlatency(Time)MAX(Longestinstruction,Userintdisable)VectortoISRMAX(Longestinstruction,Userintdisable,Kernelintdisable)VectortoISRMAX(Longestinstruction,Userintdisable,Kernelintdisable)VectortoISRInterruptresponse(Time)IntlatencySaveCPU’scontextIntlatencySaveCPU’scontextInterruptlatencySaveCPU’scontextKernelISRentryfunctionInterruptrecovery(Time)Restorebackground’scontextReturnfromintRestoretask’scontextReturnfromintFindhighestprioritytaskRestorehighestprioritytask’scontextReturnfrominterruptTaskresponse(Time)BackgroundLongesttaskFindhighestprioritytaskContextswitchFindhighestprioritytaskContextswitchROMsizeApplicationcodeApplicationcodeKernelcodeApplicationcodeKernelcodeRAMsizeApplicationcodeApplicationcodeKernelRAMSUM(TaskstacksMAX(ISRstack))ApplicationcodeKernelRAMSUM(TaskstacksMAX(ISRstack))ServicesavailableApplicationcodemustprovideYesYes过程控制食品加工化工厂汽车业发动机控制防抱死系统(ABS)办公自动化传真机复印机计算机外设打印机计算机终端扫描仪调制解调器通讯类SwitchHurb路由器机器人航空航天飞机管理系统武器系统喷气发动机控制民用消费品微波炉洗碗机洗依机稳温调节器�EMBEDEquation����EMBEDEquation���Iunknownunknow

用户评价(0)

关闭

新课改视野下建构高中语文教学实验成果报告(32KB)

抱歉,积分不足下载失败,请稍后再试!

提示

试读已结束,如需要继续阅读或者下载,敬请购买!

文档小程序码

使用微信“扫一扫”扫码寻找文档

1

打开微信

2

扫描小程序码

3

发布寻找信息

4

等待寻找结果

我知道了
评分:

/40

第2章

VIP

在线
客服

免费
邮箱

爱问共享资料服务号

扫描关注领取更多福利