1、ucosii源代码开放的确良嵌入式os教程2第2章实时系统概念12.0前后台系统 (FOREGROUND/BACKGROUND SYSTEM)12.1代码的临界段22.2资源22.3共享资源22.4多任务22.5任务22.6任务切换(CONTEXT SWITCH OR TASK SWITCH)32.7内核(KERNEL)32.8调度(SCHEDULER)42.9不可剥夺型内核 (NON-PREEMPTIVE KERNEL)42.10可剥夺型内核52.11可重入性(REENTRANCY)52.12时间片轮番调度法72.13任务优先级72.142.14静态优先级72.15动态优先级72.16优先级
2、反转72.17任务优先级分配82.18互斥条件102.18.1关中断和开中断102.18.2测试并置位112.18.3禁止,然后允许任务切换112.18.4信号量(Semaphores)122.19死锁(或抱死)(DEADLOCK (OR DEADLY EMBRACE))162.20同步162.21事件标志(EVENT FLAGS)182.22任务间的通讯(INTERTASK COMMUNICATION)182.23消息邮箱(MESSAGE MAIL BOXES)182.24消息队列(MESSAGE QUEUE)192.25中断202.26中断延迟202.27中断响应212.28中断恢复时间(
3、INTERRUPT RECOVERY)212.29中断延迟、响应和恢复222.30中断处理时间222.31非屏蔽中断(NMI)232.32时钟节拍(CLOCK TICK)242.33对存储器的需求252.34使用实时内核的优缺点262.35实时系统小结26第2章实时系统概念实时系统的特点是,如果逻辑和时序出现偏差将会引起严重后果的系统。有两种类型的实时系统:软实时系统和硬实时系统。在软实时系统中系统的宗旨是使各个任务运行得越快越好,并不要求限定某一任务必须在多长时间内完成。在硬实时系统中,各任务不仅要执行无误而且要做到准时。大多数实时系统是二者的结合。实时系统的应用涵盖广泛的领域,而多数实时系
4、统又是嵌入式的。这意味着计算机建在系统内部,用户看不到有个计算机在系统里面。以下是一些嵌入式系统的例子:实时应用软件的设计一般比非实时应用软件设计难一些。本章讲述实时系统概念。2.0前后台系统(Foreground/Background System)不复杂的小系统一般设计成如图2.1所示的样子。这种系统可称为前后台系统或超循环系统(Super-Loops)。应用程序是一个无限的循环,循环中调用相应的函数完成相应的操作,这部分可以看成后台行为(background)。中断服务程序处理异步事件,这部分可以看成前台行为(foreground)。后台也可以叫做任务级。前台也叫中断级。时间相关性很强的
5、关键操作(Critical operation)一定是靠中断服务来保证的。因为中断服务提供的信息一直要等到后台程序走到该处理这个信息这一步时才能得到处理,这种系统在处理信息的及时性上,比实际可以做到的要差。这个指标称作任务级响应时间。最坏情况下的任务级响应时间取决于整个循环的执行时间。因为循环的执行时间不是常数,程序经过某一特定部分的准确时间也是不能确定的。进而,如果程序修改了,循环的时序也会受到影响。图2-1前后台系统 很多基于微处理器的产品采用前后台系统设计,例如微波炉、电话机、玩具等。在另外一些基于微处理器的应用中,从省电的角度出发,平时微处理器处在停机状态(halt),所有的事都靠中断
6、服务来完成。2.1代码的临界段代码的临界段也称为临界区,指处理时不可分割的代码。一旦这部分代码开始执行,则不允许任何中断打入。为确保临界段代码的执行,在进入临界段之前要关中断,而临界段代码执行完以后要立即开中断。(参阅2.03共享资源)2.2 资源任何为任务所占用的实体都可称为资源。资源可以是输入输出设备,例如打印机、键盘、显示器,资源也可以是一个变量,一个结构或一个数组等。2.3共享资源可以被一个以上任务使用的资源叫做共享资源。为了防止数据被破坏,每个任务在与共享资源打交道时,必须独占该资源。这叫做互斥(mutual exclusion)。在2.18节“互斥”中,将对技术上如何保证互斥条件做
7、进一步讨论。2.4多任务多任务运行的实现实际上是靠CPU(中央处理单元)在许多任务之间转换、调度。CPU只有一个,轮番服务于一系列任务中的某一个。多任务运行很像前后台系统,但后台任务有多个。多任务运行使CPU的利用率得到最大的发挥,并使应用程序模块化。在实时应用中,多任务化的最大特点是,开发人员可以将很复杂的应用程序层次化。使用多任务,应用程序将更容易设计与维护。2.5任务一个任务,也称作一个线程,是一个简单的程序,该程序可以认为CPU完全只属该程序自己。实时应用程序的设计过程,包括如何把问题分割成多个任务,每个任务都是整个应用的某一部分,每个任务被赋予一定的优先级,有它自己的一套CPU寄存器
8、和自己的栈空间(如图2.2所示)。图2.2多任务。典型地、每个任务都是一个无限的循环。每个任务都处在以下5种状态之一的状态下,这5种状态是休眠态,就绪态、运行态、挂起态(等待某一事件发生)和被中断态(参见图2.3)休眠态相当于该任务驻留在内存中,但并不被多任务内核所调度。就绪意味着该任务已经准备好,可以运行了,但由于该任务的优先级比正在运行的任务的优先级低,还暂时不能运行。运行态的任务是指该任务掌握了CPU的控制权,正在运行中。挂起状态也可以叫做等待事件态WAITING,指该任务在等待,等待某一事件的发生,(例如等待某外设的I/O操作,等待某共享资源由暂不能使用变成能使用状态,等待定时脉冲的到
9、来或等待超时信号的到来以结束目前的等待,等等)。最后,发生中断时,CPU提供相应的中断服务,原来正在运行的任务暂不能运行,就进入了被中断状态。图2.3表示C/OS-中一些函数提供的服务,这些函数使任务从一种状态变到另一种状态。图2.3任务的状态2.6任务切换(Context Switch or Task Switch)Context Switch 在有的书中翻译成上下文切换,实际含义是任务切换,或CPU寄存器内容切换。当多任务内核决定运行另外的任务时,它保存正在运行任务的当前状态(Context),即CPU寄存器中的全部内容。这些内容保存在任务的当前状况保存区(Tasks Context St
10、orage area),也就是任务自己的栈区之中。(见图2.2)。入栈工作完成以后,就是把下一个将要运行的任务的当前状况从该任务的栈中重新装入CPU的寄存器,并开始下一个任务的运行。这个过程叫做任务切换。任务切换过程增加了应用程序的额外负荷。CPU的内部寄存器越多,额外负荷就越重。做任务切换所需要的时间取决于CPU有多少寄存器要入栈。实时内核的性能不应该以每秒钟能做多少次任务切换来评价。2.7内核(Kernel)多任务系统中,内核负责管理各个任务,或者说为每个任务分配CPU时间,并且负责任务之间的通讯。内核提供的基本服务是任务切换。之所以使用实时内核可以大大简化应用系统的设计,是因为实时内核允
11、许将应用分成若干个任务,由实时内核来管理它们。内核本身也增加了应用程序的额外负荷,代码空间增加ROM的用量,内核本身的数据结构增加了RAM的用量。但更主要的是,每个任务要有自己的栈空间,这一块吃起内存来是相当厉害的。内核本身对CPU的占用时间一般在2到5个百分点之间。单片机一般不能运行实时内核,因为单片机的RAM很有限。通过提供必不可缺少 的系统服务,诸如信号量管理,邮箱、消息队列、延时等,实时内核使得CPU的利用更为有效。一旦读者用实时内核做过系统设计,将决不再想返回到前后台系统。2.8调度(Scheduler)调度(Scheduler),英文还有一词叫dispatcher,也是调度的意思。
12、这是内核的主要职责之一,就是要决定该轮到哪个任务运行了。多数实时内核是基于优先级调度法的。每个任务根据其重要程度的不同被赋予一定的优先级。基于优先级的调度法指,CPU总是让处在就绪态的优先级最高的任务先运行。然而,究竟何时让高优先级任务掌握CPU的使用权,有两种不同的情况,这要看用的是什么类型的内核,是不可剥夺型的还是可剥夺型内核。2.9不可剥夺型内核(Non-Preemptive Kernel)不可剥夺型内核要求每个任务自我放弃CPU的所有权。不可剥夺型调度法也称作合作型多任务,各个任务彼此合作共享一个CPU。异步事件还是由中断服务来处理。中断服务可以使一个高优先级的任务由挂起状态变为就绪状
13、态。但中断服务以后控制权还是回到原来被中断了的那个任务,直到该任务主动放弃CPU的使用权时,那个高优先级的任务才能获得CPU的使用权。不可剥夺型内核的一个优点是响应中断快。在讨论中断响应时会进一步涉及这个问题。在任务级,不可剥夺型内核允许使用不可重入函数。函数的可重入性以后会讨论。每个任务都可以调用非可重入性函数,而不必担心其它任务可能正在使用该函数,从而造成数据的破坏。因为每个任务要运行到完成时才释放CPU的控制权。当然该不可重入型函数本身不得有放弃CPU控制权的企图。使用不可剥夺型内核时,任务级响应时间比前后台系统快得多。此时的任务级响应时间取决于最长的任务执行时间。不可剥夺型内核的另一个
14、优点是,几乎不需要使用信号量保护共享数据。运行着的任务占有CPU,而不必担心被别的任务抢占。但这也不是绝对的,在某种情况下,信号量还是用得着的。处理共享I/O设备时仍需要使用互斥型信号量。例如,在打印机的使用上,仍需要满足互斥条件。图2.4示意不可剥夺型内核的运行情况,任务在运行过程之中,L2.4(1)中断来了,如果此时中断是开着的,CPU由中断向量F2.4(2)进入中断服务子程序,中断服务子程序做事件处理F2.4(3),使一个有更高级的任务进入就绪态。中断服务完成以后,中断返回指令F2.4(4), 使CPU回到原来被中断的任务,接着执行该任务的代码F2.4(5)直到该任务完成,调用一个内核服
15、务函数以释放CPU控制权,由内核将控制权交给那个优先级更高的、并已进入就绪态的任务F2.4(6),这个优先级更高的任务才开始处理中断服务程序标识的事件F2.4(7)。图2.4不可剥夺型内核不可剥夺型内核的最大缺陷在于其响应时间。高优先级的任务已经进入就绪态,但还不能运行,要等,也许要等很长时间,直到当前运行着的任务释放CPU。与前后系统一样,不可剥夺型内核的任务级响应时间是不确定的,不知道什么时候最高优先级的任务才能拿到CPU的控制权,完全取决于应用程序什么时候释放CPU。总之,不可剥夺型内核允许每个任务运行,直到该任务自愿放弃CPU的控制权。中断可以打入运行着的任务。中断服务完成以后将CPU
16、控制权还给被中断了的任务。任务级响应时间要大大好于前后系统,但仍是不可知的,商业软件几乎没有不可剥夺型内核。2.10可剥夺型内核当系统响应时间很重要时,要使用可剥夺型内核。因此,C/OS-以及绝大多数商业上销售的实时内核都是可剥夺型内核。最高优先级的任务一旦就绪,总能得到CPU的控制权。当一个运行着的任务使一个比它优先级高的任务进入了就绪态,当前任务的CPU使用权就被剥夺了,或者说被挂起了,那个高优先级的任务立刻得到了CPU的控制权。如果是中断服务子程序使一个高优先级的任务进入就绪态,中断完成时,中断了的任务被挂起,优先级高的那个任务开始运行。如图2.5所示。 图2.5可剥夺型内核使用可剥夺型
17、内核,最高优先级的任务什么时候可以执行,可以得到CPU的控制权是可知的。使用可剥夺型内核使得任务级响应时间得以最优化。使用可剥夺型内核时,应用程序不应直接使用不可重入型函数。调用不可重入型函数时,要满足互斥条件,这一点可以用互斥型信号量来实现。如果调用不可重入型函数时,低优先级的任务CPU的使用权被高优先级任务剥夺,不可重入型函数中的数据有可能被破坏。综上所述,可剥夺型内核总是让就绪态的高优先级的任务先运行,中断服务程序可以抢占CPU,到中断服务完成时,内核让此时优先级最高的任务运行(不一定是那个被中断了的任务)。任务级系统响应时间得到了最优化,且是可知的。C/OS-属于可剥夺型内核。2.11
18、可重入性(Reentrancy)可重入型函数可以被一个以上的任务调用,而不必担心数据的破坏。可重入型函数任何时候都可以被中断,一段时间以后又可以运行,而相应数据不会丢失。可重入型函数或者只使用局部变量,即变量保存在CPU寄存器中或堆栈中。如果使用全局变量,则要对全局变量予以保护。程序2.1是一个可重入型函数的例子。程序清单2.1可重入型函数void strcpy(char *dest, char *src) while (*dest+ = *src+) ; *dest = NUL;函数Strcpy()做字符串复制。因为参数是存在堆栈中的,故函数Strcpy()可以被多个任务调用,而不必担心各任
19、务调用函数期间会互相破坏对方的指针。不可重入型函数的例子如程序2.2所示。Swap()是一个简单函数,它使函数的两个形式变量的值互换。为便于讨论,假定使用的是可剥夺型内核,中断是开着的,Temp定义为整数全程变量。 程序清单 2.2 不可重入型函数int Temp;void swap(int *x, int *y) Temp = *x; *x = *y; *y = Temp;程序员打算让Swap() 函数可以为任何任务所调用,如果一个低优先级的任务正在执行Swap()函数,而此时中断发生了,于是可能发生的事情如图2.6所示。F2.6(1)表示中断发生时Temp已被赋值1,中断服务子程序使更优先
20、级的任务就绪,当中断完成时F2.6(2),内核(假定使用的是C/OS-)使高优先级的那个任务得以运行F2.6(3),高优先级的任务调用Swap()函数是Temp赋值为3。这对该任务本身来说,实现两个变量的交换是没有问题的,交换后Z的值是4,X的值是3。然后高优先级的任务通过调用内核服务函数中的延迟一个时钟节拍F2.6(4),释放了CPU的使用权,低优先级任务得以继续运行F2.6(5).注意,此时Temp的值仍为3!在低优先级任务接着运行时,Y的值被错误地赋为3,而不是正确值1。 图2.6不可重入性函数请注意,这只是一个简单的例子,如何能使代码具有可重入性一看就明白。然而有些情况下,问题并非那么
21、易解。应用程序中的不可重入函数引起的错误很可能在测试时发现不了,直到产品到了现场问题才出现。如果在多任务上您还是把新手,使用不可重入型函数时,千万要当心。使用以下技术之一即可使Swap()函数具有可重入性:把Temp定义为局部变量调用Swap()函数之前关中断,调动后再开中断用信号量禁止该函数在使用过程中被再次调用如果中断发生在Swap()函数调用之前或调用之后,两个任务中的X,Y值都会是正确的。2.12时间片轮番调度法当两个或两个以上任务有同样优先级,内核允许一个任务运行事先确定的一段时间,叫做时间额度(quantum),然后切换给另一个任务。也叫做时间片调度。内核在满足以下条件时,把CPU
22、控制权交给下一个任务就绪态的任务:当前任务已无事可做当前任务在时间片还没结束时已经完成了。目前,C/OS-不支持时间片轮番调度法。应用程序中各任务的优先级必须互不相同。2.13任务优先级每个任务都有其优先级。任务越重要,赋予的优先级应越高。2.142.14静态优先级应用程序执行过程中诸任务优先级不变,则称之为静态优先级。在静态优先级系统中,诸任务以及它们的时间约束在程序编译时是已知的。2.15动态优先级应用程序执行过程中,任务的优先级是可变的,则称之为动态优先级。实时内核应当避免出现优先级反转问题。2.16优先级反转使用实时内核,优先级反转问题是实时系统中出现得最多的问题。图2.7解释优先级反
23、转是如何出现的。如图,任务1优先级高于任务2,任务2优先级高于任务3。任务1和任务2处于挂起状态,等待某一事件的发生,任务3正在运行如图2.7(1)。此时,任务3要使用其共享资源。使用共享资源之前,首先必须得到该资源的信号量(Semaphore)(见2. 18.04信号量)。任务3得到了该信号量,并开始使用该共享资源图2.7(2)。由于任务1优先级高,它等待的事件到来之后剥夺了任务3的CPU使用权图2.7(3),任务1开始运行图2.7(4)。运行过程中任务1也要使用那个任务3正在使用着的资源,由于该资源的信号量还被任务3占用着,任务1只能进入挂起状态,等待任务3释放该信号量图2.7(5)。任务
24、3得以继续运行图2.7(6)。由于任务2的优先级高于任务3,当任务2等待的事件发生后,任务2剥夺了任务3的CPU的使用权图2.7(7)并开始运行。处理它该处理的事件图2.7(8),直到处理完之后将CPU控制权还给任3图2.7(9)。任务3接着运行图2.7(10),直到释放那个共享资源的信号量图27(11)。直到此时,由于实时内核知道有个高优先级的任务在等待这个信号量,内核做任务切换,使任务1得到该信号量并接着运行图2.7(12)。在这种情况下,任务1优先级实际上降到了任务3 的优先级水平。因为任务1要等,直等到任务3释放占有的那个共享资源。由于任务2剥夺任务3的CPU使用权,使任务1的状况更加
25、恶化,任务2使任务1增加了额外的延迟时间。任务1和任务2的优先级发生了反转。纠正的方法可以是,在任务3使用共享资源时,提升任务3的优先级。任务完成时予以恢复。任务3的优先级必须升至最高,高于允许使用该资源的任何任务。多任务内核应允许动态改变任务的优先级以避免发生优先级反转现象。然而改变任务的优先级是很花时间的。如果任务3并没有先被任务1剥夺CPU使用权,又被任务2抢走了CPU使用权,花很多时间在共享资源使用前提升任务3的优先级,然后又在资源使用后花时间恢复任务3的优先级,则无形中浪费了很多CPU时间。真正需要的是,为防止发生优先级反转,内核能自动变换任务的优先级,这叫做优先级继承(Priori
26、ty inheritance)但C/OS-不支持优先级继承,一些商业内核有优先级继承功能。 图2.7优先级反转问题图2.8解释如果内核支持优先级继承的话,在上述例子中会是怎样一个过程。任务3在运行图2.8(1),任务3申请信号量以获得共享资源使用权图2.8(2),任务3得到并开始使用共享资源图2.8(3)。后来CPU使用权被任务1剥夺图2.8(4),任务1开始运行图2.8(5),任务1申请共享资源信号量图2.8(6)。此时,内核知道该信号量被任务3占用了,而任务3的优先级比任务1低,内核于是将任务3的优先级升至与任务1一样,然而回到任务3继续运行,使用该共享资源图2.7(7),直到任务3释放共
27、享资源信号量图2。8(8)。这时,内核恢复任务3本来的优先级并把信号量交给任务1,任务1得以顺利运行。 图2.8(9),任务1完成以后图2.8(10)那些任务优先级在任务1与任务3之间的任务例如任务2才能得到CPU使用权,并开始运行 图2.8(11)。注意,任务2在从图2.8(3)到图2.8(10)的任何一刻都有可能进入就绪态,并不影响任务1、任务3的完成过程。在某种程度上,任务2和任务3之间也还是有不可避免的优先级反转。 图2.82.17任务优先级分配给任务定优先级可不是件小事,因为实时系统相当复杂。许多系统中,并非所有的任务都至关重要。不重要的任务自然优先级可以低一些。实时系统大多综合了软
28、实时和硬实时这两种需求。软实时系统只是要求任务执行得尽量快,并不要求在某一特定时间内完成。硬实时系统中,任务不但要执行无误,还要准时完成。一项有意思的技术可称之为单调执行率调度法RMS(Rate Monotonic Scheduling),用于分配任务优先级。这种方法基于哪个任务执行的次数最频繁,执行最频繁的任务优先级最高。见图2.9。图2.9 基于任务执行频繁度的优先级分配法任务执行频繁度(Hz)RMS做了一系列假设:所有任务都是周期性的任务间不需要同步,没有共享资源,没有任务间数据交换等问题CPU必须总是执行那个优先级最高且处于就绪态的任务。换句话说,要使用可剥夺型调度法。给出一系列n值表
29、示系统中的不同任务数,要使所有的任务满足硬实时条件,必须使不等式2.1成立,这就是RMS定理:2.1 这里Ei是任务i最长执行时间,Ti是任务i的执行周期。换句话说,Ei/Ti是任务i所需的CPU时间。表2.1给出n(21/n - 1 )的值,n是系统中的任务数。对于无穷多个任务,极限值是 或0.693。这就意味着,基于RMS,要任务都满足硬实时条件,所有有时间条件要求的任务i总的CPU利用时间应小于70%!请注意,这是指有时间条件要求的任务,系统中当然还可以有对时间没有什么要求的任务,使得CPU的利用率达到100%。使CPU利用率达到100%并不好,因为那样的话程序就没有了修改的余地,也没法
30、增加新功能了。作为系统设计的一条原则,CPU利用率应小于60%到70%。 RMS认为最高执行率的任务具有最高的优先级,但最某些情况下,最高执行率的任务并非是最重要的任务。如果实际应用都真的像RMS说的那样,也就没有什么优先级分配可讨论了。然而讨论优先级分配问题,RMS无疑是一个有意思的起点。表2.1基于任务到CPU最高允许使用率.表2.1基于任务到CPU最高允许使用率.任务数n(21/n - 1) 11.000 20.828 30.779 40.756 50.743 . . . 0.6932.18互斥条件实现任务间通讯最简便到办法是使用共享数据结构。特别是当所有到任务都在一个单一地址空间下,能使用全程变量、指针、缓冲区、链表、循环缓冲区等,使用共享数据结构通讯就更为容易。虽然共享数据区法简化了任务间的信息交换,但是必须保证每个任务在处理共享数据时的排它性,以避免竞争和数据的
copyright@ 2008-2023 冰点文库 网站版权所有
经营许可证编号:鄂ICP备19020893号-2