chap02-实时系统概念.ppt
1,2020/10/15,嵌入式实时操作系统 C/OS-II,2020/10/15,2,第2章 实时系统概念,3,2020/10/15,实时系统概念,软实时系统-各个任务尽快运行,不要求限定某一任务在多长时间内完成。 硬实时系统-任务要做到准时。,2.0 前/后台系统,不复杂的小系统一般设计成如下图所示的样子。这种系统可称为前后台系统或超循环系统(Super-Loops)。,4,2020/10/15,5,2020/10/15,后台是一个循环轮询系统一直在运行。 前台是由一些中断处理过程组成的。 当有一前台事件(外部事件)发生时,引起中断, 进行前台处理, 处理完成后又回到后台(通常又称主程序)。,中断1,中断2,ISR1,ISR2,前台处理,后台处理,ISR3,实时性?,认为所有的任务具有相同的优先级别 任务的执行通过FIFO队列排队 某执行任务崩溃会造成整个系统的崩溃,应用程序是一个无限的循环,循环中调用相应的函数完成相应的操作,这部分可以看成后台行为(background)。中断服务程序处理异步事件,这部分可以看成前台行为(foreground)。后台也可以叫做任务级。前台也叫中断级。,8,2020/10/15,9,2020/10/15,时间相关性很强的关键操作(Critical operation)一定是靠中断服务来保证的。因为中断服务提供的信息一直要等到后台程序走到该处理这个信息这一步时才能得到处理,这种系统在处理信息的及时性上,比实际可以做到的要差。这个指标称作任务级响应时间。最坏情况下的任务级响应时间取决于整个循环的执行时间。因为循环的执行时间不是常数,程序经过某一特定部分的准确时间也是不能确定的。进而,如果程序修改了,循环的时序也会受到影响。,2.01 代码的临界段,代码的临界段也称为临界区,指处理时不可分割的代码。 一旦这部分代码开始执行,则不允许任何中断打入。为确保临界段代码的执行,在进入临界段之前要关中断,而临界段代码执行完以后要立即开中断。,10,2020/10/15,2.02 资源,资源-任何为任务所占用的实体都可称为资源。 资源可以是输入输出设备,例如打印机、键盘、显示器,资源也可以是一个变量,一个结构或一个数组等。,11,2020/10/15,2.03 共享资源,共享资源-可以被一个以上任务使用的资源叫做共享资源。 为了防止数据被破坏,每个任务在与共享资源打交道时,必须独占该资源。这叫做互斥(mutual exclusion)。,12,2020/10/15,2.04 多任务,多任务运行的实现实际上是靠CPU(中央处理单元)在许多任务之间转换、调度。CPU只有一个,轮番服务于一系列任务中的某一个。多任务运行很像前后台系统,但后台任务有多个。多任务运行使CPU的利用率得到最大的发挥,并使应用程序模块化。在实时应用中,多任务化的最大特点是,开发人员可以将很复杂的应用程序层次化。使用多任务,应用程序将更容易设计与维护。,13,2020/10/15,流程,并发多任务,:,:,后台,前台,ISRs,Tasks,多任务系统 多个顺序执行的程序并行运行。 宏观上看,所有的程序同时运行,每个程序运行在自己独立的CPU上。 实际上,不同的程序是共享同一个CPU和其它硬件。因此,需要RTOS来对这些共享的设备和数据进行管理。 每个程序都被编制成无限循环的程序,等待特定的输入,执行相应的任务等。 这种程序模型将系统分成相对简单的,相互合作的模块。,单处理器多任务系统,优点 将复杂的系统分解为相对独立的多个线程, 达到“分而治之”的目的,从而降低系统的复杂性。 保证系统的实时性。 系统的模块化好,提高系统的可维护性。 缺点 需要采用一些新的软件设计方法。 需要增加功能:线程间的协调,同步和通信功能。 需要对每一个共享资源互斥。 导致线程间的竞争。 需要使用RTOS,RTOS要增加系统的开销。,单处理器多任务系统,多处理器多任务系统,多任务可运行在多个处理器上,由操作系统统一调度,处理。 宏观上看是并发的,微观上看也是并发的。 多处理机系统分为紧耦合系统(tightly-coupled system)和松耦合系统(loosely-coupled system)两种。 多处理器多任务系统目前还不成熟。,2.05 任务,18,2020/10/15,一个任务,也称作一个线程,是一个简单的程序,该程序可以认为CPU 完全只属该程序自己。实时应用程序的设计过程,包括如何把问题分割成多个任务,每个任务都是整个应用的某一部分,每个任务被赋予一定的优先级,有它自己的一套CPU 寄存器和自己的栈空间。,19,每个任务都是一个无限的循环。每个任务都处在以下5种状态之一的状态下,这5 种状态是休眠态,就绪态、运行态、挂起态(等待某一事件发生)和被中断态。 休眠态-相当于该任务驻留在内存中,但并不被多任务内核所调度。 就绪态-意味着该任务已经准备好,可以运行了,但由于该任务的优先级比正在运行的任务的优先级低,还暂时不能运行。,20,运行态-是指该任务掌握了CPU 的控制权,正在运行中。 挂起状态-也可以叫做等待事件态WAITING,指该任务在等待,等待某一事件的发生,(例如等待某外设的I/O 操作,等待某共享资源由暂不能使用变成能使用状态,等待定时脉冲的到来或等待超时信号的到来以结束目前的等待,等等)。 被中断态-发生中断时,CPU提供相应的中断服务,原来正在运行的任务暂不能运行,就进入了被中断状态。,21,22,休眠态,挂起态,被中断态,运行态,就绪态,2.06 任务切换(Context Switch or Task Switch),Context Switch 在有的书中翻译成上下文切换,实际含义是任务切换,或CPU 寄存器内容切换。 当多任务内核决定运行另外的任务时,它保存正在运行任务的当前状态(Context),即CPU寄存器中的全部内容。这些内容保存在任务的当前状况保存区(Tasks Context Storage area),也就是任务自己的栈区之中。 任务切换-入栈工作完成以后,就是把下一个将要运行的任务的当前状况从该任务的栈中重新装入CPU 的寄存器,并开始下一个任务的运行。 任务切换过程增加了应用程序的额外负荷。,2.07 内核(Kernel),多任务系统中,内核负责管理各个任务,或者说为每个任务分配CPU时间,并且负责任务之间的通讯。内核提供的基本服务是任务切换。 内核本身也增加了应用程序的额外负荷,代码空间增加ROM 的用量,内核本身的数据结构增加了RAM的用量。但更主要的是,每个任务要有自己的栈空间,这一块吃起内存来是相当厉害的。内核本身对CPU 的占用时间一般在2 到5个百分点之间。,2.08 调度(Scheduler),调度(Scheduler),英文还有一词叫dispatcher,也是调度的意思。这是内核的主要职责之一,就是要决定该轮到哪个任务运行了。 多数实时内核是基于优先级调度法的。每个任务根据其重要程度的不同被赋予一定的优先级。基于优先级的调度法指,CPU总是让处在就绪态的优先级最高的任务先运行。 让高优先级任务掌握CPU 的使用权,有两种不同的情况,不可剥夺型内核 可剥夺型内核。,2.09 不可剥夺型内核 (Non-Preemptive Kernel),不可剥夺型内核要求每个任务自我放弃CPU 的所有权。不可剥夺型调度法也称作合作型多任务,各个任务彼此合作共享一个CPU。 不可剥夺型内核的一个优点是响应中断快。 在任务级,不可剥夺型内核允许使用不可重入函数。,不可剥夺型内核示例,不可剥夺型内核的最大缺陷在于其响应时间。高优先级的任务已经进入就绪态,但还不能运行,要等,也许要等很长时间,直到当前运行着的任务释放CPU。不可剥夺型内核的任务级响应时间是不确定的,不知道什么时候最高优先级的任务才能拿到CPU 的控制权,完全取决于应用程序什么时候释放CPU。 商业软件几乎没有不可剥夺型内核。,2.10 可剥夺型内核,当系统响应时间很重要时,要使用可剥夺型内核。因此,C/OS-以及绝大多数商业上销售的实时内核都是可剥夺型内核。 最高优先级的任务一旦就绪,总能得到CPU 的控制权。当一个运行着的任务使一个比它优先级高的任务进入了就绪态,当前任务的CPU 使用权就被剥夺了,或者说被挂起了,那个高优先级的任务立刻得到了CPU的控制权。,可剥夺型内核示例,使用可剥夺型内核,最高优先级的任务什么时候可以执行,可以得到CPU 的控制权是可知的。使用可剥夺型内核使得任务级响应时间得以最优化。 使用可剥夺型内核时,应用程序不应直接使用不可重入型函数。调用不可重入型函数时,要满足互斥条件,这一点可以用互斥型信号量来实现。,可重入型函数可以被一个以上的任务调用,而不必担心数据的破坏。 可重入型函数任何时候都可以被中断,一段时间以后又可以运行,而相应数据不会丢失。可重入型函数或者只使用局部变量,即变量保存在CPU寄存器中或堆栈中。如果使用全局变量,则要对全局变量予以保护。,2.11 可重入性(Reentrancy),可重入型函数,void strcpy(char *dest, char *src) while (*dest+ = *src+) ; *dest = NUL; ,不可重入型函数,int Temp; void swap(int *x, int *y) Temp = *x; *x = *y; *y = Temp; ,使用以下技术之一即可使Swap()函数具有可重入性: 把Temp 定义为局部变量 调用Swap()函数之前关中断,调动后再开中断 用信号量禁止该函数在使用过程中被再次调用,2.12 时间片轮番调度法,当两个或两个以上任务有同样优先级,内核允许一个任务运行事先确定的一段时间,叫做时间额度(quantum),然后切换给另一个任务。也叫做时间片调度。内核在满足以下条件时,把CPU控制权交给下一个任务就绪态的任务: 当前任务已无事可做 当前任务在时间片还没结束时已经完成了。 目前,C/OS-不支持时间片轮番调度法。应用程序中各任务的优先级必须互不相同。,2.13 任务优先级,每个任务都有其优先级。任务越重要,赋予的优先级应越高。,2.14 静态优先级,应用程序执行过程中诸任务优先级不变,则称之为静态优先级。在静态优先级系统中,诸任务以及它们的时间约束在程序编译时是已知的。,2.15动态优先级,应用程序执行过程中,任务的优先级是可变的,则称之为动态优先级。实时内核应当避免出现优先级反转问题。,2.16优先级反转,使用实时内核,优先级反转问题是实时系统中出现得最多的问题。 为防止发生优先级反转,内核能自动变换任务的优先级,这叫做优先级继承(Priority inheritance)。但C/OS-不支持优先级继承,一些商业内核有优先级继承功能。,2.17 任务优先级分配,给任务定优先级可不是件小事,因为实时系统相当复杂。许多系统中,并非所有的任务都至关重要。不重要的任务自然优先级可以低一些。实时系统大多综合了软实时和硬实时这两种需求。软实时系统只是要求任务执行得尽量快,并不要求在某一特定时间内完成。硬实时系统中,任务不但要执行无误,还要准时完成。 单调执行率调度法RMS(Rate Monotonic Scheduling)-用于分配任务优先级。这种方法基于哪个任务执行的次数最频繁,执行最频繁的任务优先级最高。,2.18 互斥条件,实现任务间通讯最简便到办法是使用共享数据结构。特别是当所有到任务都在一个单一地址空间下,能使用全程变量、指针、缓冲区、链表、循环缓冲区等,使用共享数据结构通讯就更为容易。虽然共享数据区法简化了任务间的信息交换,但是必须保证每个任务在处理共享数据时的排它性,以避免竞争和数据的破坏。与共享资源打交道时,使之满足互斥条件最一般的方法有:,关中断 使用测试并置位指令 禁止做任务切换 利用信号量,2.18.1 关中断和开中断,程序清单 关中断和开中断 Disable interrupts; /*关中断*/ Access the resource (read/write from/to variables); /*读/写变量*/ Reenable interrupts; /*重新允许中断*/,实际上C/OS-提供两个宏调用,允许用户在应用程序的C 代码中关中断然后再开中断:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL,这两个宏调用的使用法见程序: 程序清单2.4利用C/OS_ 宏调用关中断和开中断 void Function (void) OS_ENTER_CRITICAL(); . . /*在这里处理共享数据*/ . OS_EXIT_CRITICAL(); ,关中断的时间不能太长。因为它影响整个系统的中断响应时间,即中断延迟时间。当改变或复制某几个变量的值时,应想到用这种方法来做。这也是在中断服务子程序中处理共享变量或共享数据结构的唯一方法。在任何情况下,关中断的时间都要尽量短。 如果使用某种实时内核,一般地说,关中断的最长时间不超过内核本身的关中断时间,就不会影响系统中断延迟。,2.18.2 测试并置位,如果不使用实时内核,当两个任务共享一个资源时,一定要约定好,先测试某一全程变量,如果该变量是0,允许该任务与共享资源打交道。为防止另一任务也要使用该资源,前者只要简单地将全程变量置为1,这通常称作测试并置位(Test-And-Set),或称作TAS。TAS操作可能是微处理器的单独一条不会被中断的指令,或者是在程序中关中断做TAS操作再开中断。,程序清单2.5 利用测试并置位处理共享资源 Disable interrupts; 关中断 if (Access Variable is 0) 如果资源未占用,标志为0 Set variable to 1; 置资源不可用,标志为1 Reenable interrupts; 重开中断 Access the resource; 处理该资源 Disable interrupts; 关中断 Set the Access Variable back to 0; 清资源不可使用,标志为0 Reenable interrupts; 重新开中断 else 否则 Reenable interrupts; 开中断 /* You dont have access to the resource, try back later; */ /* 资源不可使用,以后再试; */ ,2.18.3 禁止,然后允许任务切换,如果任务不与中断服务子程序共享变量或数据结构,可以使用禁止、然后允许任务切换。 如下述程序清单所示,以C/OS-的使用为例,两个或两个以上的任务可以共享数据而不发生竞争。注意,此时虽然任务切换是禁止了,但中断还是开着的。如果这时中断来了,中断服务子程序会在这一临界区内立即执行。中断服务子程序结束时,尽管有优先级高的任务已经进入就绪态,内核还是返回到原来被中断了的任务。直到执行完给任务切换开锁函数OSSchedUnlock (),内核再看有没有优先级更高的任务被中断服务子程序激活而进入就绪态,如果有,则做任务切换。,程序清单2.6 用给任务切换上锁,然后开锁的方法实现数据共享. void Function (void) OSSchedLock(); . . /* You can access shared data in here (interrupts are recognized) */ . /*在这里处理共享数据(中断是开着的)*/ OSSchedUnlock(); ,2.18.4 信号量(Semaphores),信号量是60年代中期Edgser Dijkstra 发明的。信号量实际上是一种约定机制,在多任务内核中普遍使用。信号量用于: 控制共享资源的使用权(满足互斥条件) 标志某事件的发生 使两个任务的行为同步 信号像是一把钥匙,任务要运行下去,得先拿到这把钥匙。如果信号已被别的任务占用,该任务只得被挂起,直到信号被当前使用者释放。,信号是只有两个值的变量,信号量是计数式的。只取两个值的信号是只有两个值0 和1的量,因此也称之为信号量。计数式信号量的值可以是0 到255 或0 到65535,或0 到4294967295,取决于信号量规约机制使用的是8 位、16位还是32 位。 一般地说,对信号量只能实施三种操作:初始化INITIALIZE),也可称作建立(CREATE);等信号(WAIT)也可称作挂起(PEND);给信号(SIGNAL)或发信号(POST)。,信号量初始化时要给信号量赋初值,等待信号量的任务表(Waiting list)应清为空。,想要得到信号量的任务执行等待(WAIT)操作。 如果该信号量有效(即信号量值大于0),则信号量值减1,任务得以继续运行。 如果信号量的值为0,等待信号量的任务就被列入等待信号量任务表。,任务以发信号操作(SIGNAL)释放信号量。如果没有任务在等待信号量,信号量的值仅仅是简单地加1。如果有任务在等待该信号量,那么就会有一个任务进入就绪态,信号量的值也就不加1。 收到信号量的任务可能是以下两者之一 等待信号量任务中优先级最高的,或者是 最早开始等待信号量的那个任务,即按先进先出的原则(First In First Out ,FIFO),程序清单2.7 通过获得信号量处理共享数据 OS_EVENT *SharedDataSem; void Function (void) INT8U err; OSSemPend(SharedDataSem, 0, ,如果中断服务程序或当前任务激活了一个高优先级的任务,高优先级的任务立即开始执行。,当诸任务共享输入输出设备时,信号量特别有用。 如:多任务同时访问打印机。使用信号量并给信号量赋初值1(用二进制信号量)。要想使用打印机的任务,先要得到该资源的信号量。,用获取信号量来得到打印机使用权,每个任务都知道有个信号表示资源可不可以使用。要想使用该资源,要先得到这个信号。然而有些情况下,最好把信号量藏起来,各个任务在同某一资源打交道时,并不知道实际上是在申请得到一个信号量。例如,多任务共享一个RS-232C 外设接口,各任务要送命令给接口另一端的设备并接收该设备的回应。,在任务级看不到隐含的信号量,程序清单 2.8 隐含的信号量。 INT8U CommSendCmd(char *cmd, char *response, INT16U timeout) Acquire ports semaphore; Send command to device; Wait for response (with timeout); if (timed out) Release semaphore; return (error code); else Release semaphore; return (no error); ,计数式信号量用于某资源可以同时为几个任务所用。例如,用信号量管理缓冲区阵列(buffer pool),如图2.12 所示。缓冲区阵列中共有10 个缓冲区,任务通过调用申请缓冲区函数BufReq()向缓冲区管理方申请得到缓冲区使用权。当缓冲区使用权还不再需要时,通过调用释放缓冲区函数BufRel()将缓冲区还给管方。函数示意码如程序清单2.9所示,程序清单 2.9 用信号量管理缓冲区。 BUF *BufReq(void) BUF *ptr; Acquire a semaphore; Disable interrupts; ptr = BufFreeList; BufFreeList = ptr-BufNext; Enable interrupts; return (ptr); void BufRel(BUF *ptr) Disable interrupts; ptr-BufNext = BufFreeList; BufFreeList = ptr; Enable interrupts; Release semaphore; ,处理简单的共享变量也使用信号量则是多余的。请求和释放信号量的过程是要花相当的时间的。有时这种额外的负荷是不必要的。用户可能只需要关中断、开中断来处理简单共享变量,以提高效率。,2.19 死锁(或抱死)(Deadlock (or Deadly Embrace)),死锁也称作抱死,指两个任务无限期地互相等待对方控制着的资源。设任务T1 正独享资源R1,任务T2 在独享资源R2,而此时T1 又要独享R2,T2 也要独享R1,于是哪个任务都没法继续执行了,发生了死锁。最简单的防止发生死锁的方法是让每个任务都: 先得到全部需要的资源再做下一步的工作 用同样的顺序去申请多个资源 释放资源时使用相反的顺序,内核大多允许用户在申请信号量时定义等待超时,以此化解死锁。当等待时间超过了某一确定值,信号量还是无效状态,就会返回某种形式的出现超时错误的代码,这个出错代码告知该任务,不是得到了资源使用权,而是系统错误。死锁一般发生在大型多任务系统中,在嵌入式系统中不易出现。,2.20 同步,可以利用信号量使某任务与中断服务同步(或者是与另一个任务同步,这两个任务间没有数据交换)。,图2.13 用信号量使任务与中断服务同步,用来实现同步机制的信号量初始化成0,信号量用于这种类型同步的称作单向同步(unilateral rendezvous)。一个任务做I/O操作,然后等信号回应。当I/O 操作完成,中断服务程序(或另外一个任务)发出信号,该任务得到信号后继续往下执行。,如果内核支持计数式信号量,信号量的值表示尚未得到处理的事件数。请注意,可能会有一个以上的任务在等待同一事件的发生,则这种情况下内核会根据以下原则之一发信号给相应的任务: 发信号给等待事件发生的任务中优先级最高的任务,或者 发信号给最先开始等待事件发生的那个任务,两个任务可以用两个信号量同步它们的行为。如图2.14 所示。这叫做双向同步(bilateral rendezvous)。双向同步同单向同步类似,只是两个任务要相互同步。,图2.14 两个任务用信号量同步彼此的行为,程序清单2.10 双向同步 Task1() for (;) Perform operation; Signal task #2; (1) Wait for signal from task #2; (2) Continue operation; Task2() for (;) Perform operation; Signal task #1; (3) Wait for signal from task #1; (4) Continue operation; ,2.21 事件标志(Event Flags),当某任务要与多个事件同步时,要使用事件标志。若任务需要与任何事件之一发生同步,可称为独立型同步(即逻辑或关系)。任务也可以与若干事件都发生了同步,称之为关联型(逻辑与关系)。独立型及关联型同步如图2.15所示。,图2.15 独立型及关联型同步,可以用多个事件的组合发信号给多个任务。如图2.16所示,典型地,8 个、16个或32个事件可以组合在一起,取决于用的哪种内核。每个事件占一位(bit),以32位的情况为多。任务或中断服务可以给某一位置位或复位,当任务所需的事件都发生了,该任务继续执行,至于哪个任务该继续执行了,是在一组新的事件发生时断定的。也就是在事件位置位时做判断。,2.22 任务间的通讯(Intertask Communication),有时很需要任务间的或中断服务与任务间的通讯。这种信息传递称为任务间的通讯。任务间信息的传递有两个途径:通过全程变量或发消息给另一个任务。 用全程变量时,必须保证每个任务或中断服务程序独享该变量。中断服务中保证独享的唯一办法是关中断。如果两个任务共享某变量,各任务实现独享该变量的办法可以是关中断再开中断,或使用信号量。,问题:任务如何知道全局变量被修改? 解决方法: 以信号量方式向任务发信号 该任务以查询方式不断周期性地查询变量的值 使用邮箱或消息队列,2.23 消息邮箱(Message Mail boxes),通过内核服务可以给任务发送消息。典型的消息邮箱也称作交换消息,是用一个指针型变量,通过内核服务,一个任务或一个中断服务程序可以把一则消息(即一个指针)放到邮箱里去。同样,一个或多个任务可以通过内核服务接收这则消息。发送消息的任务和接收消息的任务约定,该指针指向的内容就是那则消息。,内核一般提供以下邮箱服务: 邮箱内消息的内容初始化,邮箱里最初可以有,也可以没有消息 将消息放入邮箱(POST) 等待有消息进入邮箱(PEND) 如果邮箱内有消息,就接受这则消息。如果邮箱里没有消息,则任务并不被挂起(ACCEPT),用返回代码表示调用结果,是收到了消息还是没有收到消息。 消息邮箱也可以当作只取两个值的信号量来用。,2.24 消息队列(Message Queue),消息队列实际上是邮箱阵列。,图2.18 消息队列,2.25 中断,中断是一种硬件机制,用于通知CPU有个异步事件发生了。中断一旦被识别,CPU保存部分(或全部)现场(Context)即部分或全部寄存器的值,跳转到专门的子程序,称为中断服务子程序(ISR)。中断服务子程序做事件处理,处理完成后,程序回到: 在前后台系统中,程序回到后台程序 对不可剥夺型内核而言,程序回到被中断了的任务 对可剥夺型内核而言,让进入就绪态的优先级最高的任务开始运行,图2.19 中断嵌套,2.26 中断延迟,所有实时系统在进入临界区代码段之前都要关中断,执行完临界代码之后再开中断。关中断的时间越长,中断延迟就越长。中断延迟由下面表达式给出。 中断延迟 = 关中断的最长时间 + 开始执行中断服务子程序的第一条指令的时间,2.27 中断响应,中断响应-从中断发生到开始执行用户的中断服务子程序代码来处理这个中断的时间。 中断响应时间包括开始处理这个中断前的全部开销。(保护现场),对前后台系统,保存寄存器以后立即执行用户代码,中断响应时间由下式给出。 中断响应时间 = 中断延迟 + 保存CPU内部寄存器的时间 对于不可剥夺型内核,微处理器保存内部寄存器以后,用户的中断服务子程序代码全立即得到执行。不可剥夺型内核的中断响应时间由下式给出。 中断响应时间 = 中断延迟 + 保存CPU内部寄存器的时间,对于可剥夺型内核,则要先调用一个特定的函数,该函数通知内核即将进行中断服务,使得内核可以跟踪中断的嵌套。对于 C/OS-说来,这个函数是OSIntEnter(),可剥夺型内核的中断响应时间由下式给出: 中断响应 中断延迟 + 保存CPU 内部寄存器的时间 + 内核的进入中断服务函数的执行时间 中断响应是系统在最坏情况下的响应中断的时间,2.28 中断恢复时间(Interrupt Recovery),中断恢复时间定义为微处理器返回到被中断了的程序代码所需要的时间。 前后台系统: 中断恢复时间 = 恢复CPU 内部寄存器值的时间 + 执行中断返回指令的时间 不可剥夺型内核: 中断恢复时间 = 恢复CPU 内部寄存器值的时间 + 执行中断返回指令的时间,可剥夺型内核在中断服务子程序的末尾,要调用一个由实时内核提供的函数。C/OS-中这个函数为OSIntExit(),用于判断中断是否脱离了所有的中断嵌套。 中断恢复时间 = 判定是否有优先级更高的任务进入了就绪态的时间 + 恢复那个优先级更高任务的CPU内部寄存器的时间 + 执行中断返回指令的时间,2.29 中断延迟、响应和恢复,图2.20 中断延迟、响应和恢复(前后台模式),图2.21 中断延迟、响应和恢复(不可剥夺型内核),图2.22 中断延迟、响应和恢复(可剥夺型内核),返回被中断的任务,运行因中断而进入就绪态的优先级最高的任务,2.30 中断处理时间,中断服务的处理时间应该尽可能的短,但是对处理时间并没有绝对的限制。 如果中断服务是在任何给定的时间开始,且中断服务程序代码是应用程序中最重要的代码,则中断服务需要多长时间就应该给它多长时间。 大多数情况下,中断服务子程序应识别中断来源,从叫中断的设备取得数据或状态,并通知真正做该事件处理的那个任务。,通知一个任务去做事件处理所花的时间比处理这个事件所花的时间还多,如何处理?,在中断服务子程序中做事件处理并在中断服务子程序中开中断,以允许优先级更高的中断打入并优先得到服务。,2.31 非屏蔽中断(NMI),中断服务对时间要求非常严格,在这种情况下可以使用非屏蔽中断,绝大多数微处理器有非屏蔽中断功能。 留做紧急处理用(如断电时保存重要的信息)。如果应用程序没有这方面的要求,非屏蔽中断可用于时间要求最苛刻的中断服务。 在非屏蔽中断的中断服务子程序中,不能使用内核提供的服务,因为非屏蔽中断是关不掉的,故不能在非屏蔽中断处理中处理临界区代码。,2.32 时钟节拍(Clock Tick),时钟节拍是特定的周期性中断。这个中断可以看作是系统心脏的脉动。中断之间的时间间隔取决于不同的应用,一般在10mS 到200mS 之间。时钟的节拍式中断使得内核可以将任务延时若干个整数时钟节拍,以及当任务等待事件发生时,提供等待超时的依据。时钟节拍率越快,系统的额外开销就越大。 各种实时内核都有将任务延时若干个时钟节拍的功能。然而这并不意味着延时的精度是1 个时钟节拍,只是在每个时钟节拍中断到来时对任务延时做一次裁决而已。,图2.25 将任务延迟一个时钟节拍(第一种情况),图2.26 将任务延迟一个时钟节拍(第二种情况),图2.27 将任务延迟一个时钟节拍(第三种情况),2.33 对存储器的需求,前后台系统:对存储器容量的需求仅仅取决于应用程序代码。 多任务内核:内核本身需要额外的代码空间(ROM)。内核的大小取决于多种因素,取决于内核的特性,从1K到100K字节都是可能的。 总代码量 = 应用程序代码 + 内核代码,因为每个任务都是独立运行的,必须给每个任务提供单独的栈空间(RAM)。 系统栈专门用于处理中断级代码。 每个任务所需的栈空间大小可以分别定义或要求相同。,如果内核不支持单独的中断用栈,总的RAM 需求由下列表达式给出。 RAM 总需求 = 应用程序的RAM需求 + (任务栈需求 + 最大中断嵌套栈需求) * 任务数 如果内核支持中断用栈分离,总RAM需求量由下列表达式给出。 RAM总需求 = 应用程序的RAM需求 + 内核数据区的RAM需求 + 各任务栈需求之总和 + 最多中断嵌套之栈需求,表2.2 实时系统小结,