摘要:实时操作系统已经由以前的嵌入式系统的统一实体的结构演变为最低层是一个实时内核的层次结构。多任务、抢占调度、快速上下文切换、低中断延时和快速灵活的通信机制是对现代实时内核的标准要求。第一部分描述了内核需求,第二部分提供了VxWorks内核的功能的细节,第三部分分析了VxWorks内核的优点,最后第四部分包含了内核性能的时间分析。
1. 实时操作系统的结构
在计算机的早期开发的操作系统的最原始的结构形式是一个统一的实体(monolithic)。在这样的系统中,提供的不同功能的模块,如处理器管理、内存管理、输入输出等,通常是独立的。然而他们在执行过程中并不考虑其他正在使用中的模块,各个模块都以相同的时间粒度运行。
由于现代实时环境需要许多不同的功能,以及在这样的环境中存在的并发活动所引起的异步性和非确定性,操作系统变得更加复杂。所以早期操作系统的统一结构的组织已经被更加精确的内部结构所淘汰。
层次结构的起点----内核
操作系统的最好的内部结构模型是一个层次性的结构,最低层是内核。这些层次可以看成为一个倒置的金字塔,每一层都建立在较低层的功能之上。内核仅包含一个操作系统执行的最重要的低层功能。正象一个统一结构的操作系统,内核提供了在高层软件与下层硬件之间的抽象层。然而,内核仅提供了构造操作系统其他部分所需的最小操作集。
对一个实时内核的要求
一个实时操作系统内核需满足许多特定的实时环境所提出的基本要求,这些包括:
多任务:由于真实世界的事件的异步性,能够运行许多并发进程或任务是很重要的。多任务提供了一个较好的对真实世界的匹配,因为它允许对应于许多外部事件的多线程执行。系统内核分配CPU给这些任务来获得并发性。
抢占调度:真实世界的事件具有继承的优先级,在分配CPU的时候要注意到这些优先级。基于优先级的抢占调度,任务都被指定了优先级,在能够执行的任务(没有被挂起或正在等待资源)中,优先级最高的任务被分配CPU资源。换句话说,当一个高优先级的任务变为可执行态,它会立即抢占当前正在运行的较低优先级的任务。
快速灵活的任务间的通信与同步:在一个实时系统中,可能有许多任务作为一个应用的一部分执行。系统必须提供这些任务间的快速且功能强大的通信机制。内核也要提供为了有效地共享不可抢占的资源或临界区所需的同步机制。
方便的任务与中断之间的通信:尽管真实世界的事件通常作为中断方式到来,但为了提供有效的排队、优先化和减少中断延时,我们通常希望在任务级处理相应的工作。所以需要杂任务级和中断级之间存在通信。
性能边界:一个实时内核必须提供最坏情况的性能优化,而非针对吞吐量的性能优化。我们更期望一个系统能够始终以50微秒执行一个函数,而不期望系统平均以10微秒执行该函数,但偶尔会以75微秒执行它。
特殊考虑:由于对实时内核的要求的增加,必须考虑对内核支持不断增加的复杂功能的要求。这包括多进程处理,Ada和对更新的、功能更强的处理器结构如RISC的支持。
拥有其它名字的内核
许多商用化的内核支持的功能远强于上面所列的要求。在这方面,他们不是真正的内核,而更象一个小的统一结构的操作系统。因为他们包含简单的内存分配、时钟管理、甚至一些输入输出系统调用的功能。
这种分类不仅仅是在语义上的争论,在这篇文章的后面章节将说明限制内核功能和优化这些功能的重要性。
2. VxWorks内核:Wind
VxWorks操作系统是一种功能最全的现在可以获得的独立于处理器的实时系统。然而,VxWorks是带有一个相当小的真正微内核的层次结构。内核仅提供多任务环境、进程间通信和同步功能。这些功能模块足够支持VxWorks在较高层次所提供的丰富的性能的要求。
通常内核操作对于用户是不可见的。应用程序为了实现需要内核参与的任务管理和同步使用一些系统调用,但这些调用的处理对于调用任务是不可见的。应用程序仅链接恰当的VxWorks例程(通常使用VxWorks的动态链接功能),就象调用子程序一样发出系统调用。这种接口不象有些系统需要一个笨拙的跳转表接口,用户需要通过一个整数来指定一个内核功能调用。
多任务
内核的基本功能是提供一个多任务环境。多任务使得许多程序在表面上表现为并发执行,而事实上内核是根据基本的调度算法使他们分段执行。每个明显独立的程序被成为一个任务。每个任务拥有自己的上下文,其中包含在内核调度使该任务执行的时候它所看到的CPU环境和系统资源。
任务状态
内核维护系统中的每个任务的当前状态。状态迁移发生在应用程序调用内核功能服务的时候。下面定义了wind内核状态:
就绪态----一个任务当前除了CPU不等待任何资源
阻塞态----一个任务由于某些资源不可获得而被阻塞
延迟态----一个任务睡眠一段时间
挂起态----主要用于调试的一个辅助状态,挂起禁止任务的执行
任务被创建以后进入挂起态,需要通过特定的操作使被创建的任务进入就绪态,这一操作执行速度很快,使应用程序能够提前创建任务,并以一种快捷的方式激活该任务。
调度控制
多任务需要一个调度算法分配CPU给就绪的任务。在VxWorks中默认的调度算法是基于优先级的抢占调度,但应用程序也可以选择使用时间片轮转调度。
基于优先级抢占调度:基于优先级的抢占调度,每个任务被指定一个优先级,内核分配CPU给处于就绪态的优先级最高的任务。调度采用抢占的方式,是因为当一个优先级高于当前任务的任务变为就绪态时,内核将立即保存当前任务的上文,并切换到高优先级任务的上文。VxWorks有从0到255共256个优先级。在创建的时候任务被指定一个优先级,在任务运行的过程中可以动态地修改优先级以便跟踪真实世界的事件优先级。外部中断被指定优先于任何任务的优先级,这样能够在任何时候抢占一个任务。
时间片轮转:基于优先级抢占调度可以扩充时间片轮转调度。时间片轮转调度允许在相同优先级的处于就绪态的任务公平地共享CPU。没有时间片轮转调度,当有多个任务在同一优先级共享处理器时,一个任务可能独占CPU,不会被阻塞直到被一个更高优先级的任务抢占,而不给同一优先级的其他任务运行的机会。如果时间片轮转被使能,执行任务的时间计数器在每个时钟滴答递增。当指定的时间片耗尽,计数器会被清零,该任务被放在同一优先级任务队列的队尾。加入特定优先级组的新任务被放在该组任务的队尾,并将运行计数器初始化为零。
基本的任务函数
用于状态控制的基本任务函数包括一个任务的创建、删除、挂起和唤醒。一个任务也可以使自己睡眠一个特定的时间间隔不去运行。
许多其他任务例程提供由任务上下文获得的状态信息。这些例程包括访问一个任务当前处理器寄存器控制。
任务删除问题
Wind内核提供防止任务被意外删除的机制。通常,一个执行在临界区或访问临界资源的任务要被特别保护。我们设想下面的情况:一个任务获得一些数据结构的互斥访问权,当它正在临界区内执行时被另一个任务删除。由于任务无法完成对临界区的操作,该数据结构可能还处于被破坏或不一致的状态。而且,假想任务没有机会释放该资源,那麽现在其他任何任务现在就不能获得该资源,资源被冻结了。
任何要删除或终止一个设定了删除保护的任务的任务将被阻塞。当被保护的任务完成临界区操作以后,它将取消删除保护以使自己可以被删除,从而解阻塞删除任务。
正如上面所展示的,任务删除保护通常伴有互斥操作。
这样,为了方便性和效率,互斥信号量包含了删除保护选项。(参见"互斥信号量")
任务间通信
为了提供完整的多任务系统的功能,wind内核提供了一套丰富的任务间通信与同步的机制。这些通信功能使一个应用中各个独立的任务协调他们的活动。
共享地址空间
Wind内核的任务间通信机制的基础是所有任务所在的共享地址空间。通过共享地址空间,任务能够使用共享数据结构的指针自由地通信。管道不需要映射一块内存区到两个互相通信任务的寻址空间。
不幸的是,共享地址空间具有上述优点的同时,带来了未被保护内存的重入访问的危险。UNIX操作系统通过隔离进程提供这样的保护,但同时带来了对于实时操作系统来说巨大的性能损失。
互斥操作
当一个共享地址空间简化了数据交换,通过互斥访问避免资源竞争就变为必要的了。用来获得一个资源的互斥访问的许多机制仅在这些互斥所作用的范围上存在差别。实现互斥的方法包括禁止中断、禁止任务抢占和通过信号量进行资源锁定。
中断禁止:最强的互斥方法是屏蔽中断。这样的锁定保证了对CPU的互斥访问。这种方法当然能够解决互斥的问题,但它对于实时是不恰当的,因为它在锁定期间阻止系统响应外部事件。长的中断延时对于要求有确定的响应时间的应用来说是不可接受的。
抢占禁止:禁止抢占提供了强制性较弱的互斥方式。当前任务运行的过程中不允许其他任务抢占,而中断服务程序可以执行。这也可能引起较差的实时响应,就象被禁止中断一样,被阻塞的任务会有相当长时间的抢占延时,就绪态的高优先级的任务可能会在能够执行前被强制等待一段不可接受的时间。为避免这种情况,在可能的情况下尽量使用信号量实现互斥。
互斥信号量:信号量是用于锁定共享资源访问的基本方式。不象禁止中断或抢占,信号量限制了互斥操作仅作用于相关的资源。一个信号量被创建来保护资源。VxWorks的信号量遵循Dijkstra的P()和V()操作模式。
当一个任务请求信号量,P(),根据在发出调用时信号量的置位或清零的状态,会发生两种情况。如果信号量处于置位态,信号量会被清零,并且任务立即继续执行。如果信号量处于清零态,任务会被阻塞来等待信号量。
当一个任务释放信号量,V(),会发生几种情况。如果信号量已经处于置位态,释放信号量不会产生任何影响。如果信号量处于清零态且没有任务等待该信号量,信号量只是被简单地置位。如果信号量处于清零态且有一个或多个任务等待该信号量,最高优先级的任务被解阻塞,信号量仍为清零态。
通过将一些资源与信号量关联,能够实现互斥操作。当一个任务要操作资源,它必须首先获得信号量。只要任务拥有信号量,所有其他的任务由于请求该信号量而被阻塞。当一个任务使用完该资源,它释放信号量,允许等待该信号量的另一个任务访问该资源。
Wind内核提供了二值信号量来解决互斥操作所引起的问题。这些问题包括资源拥有者的删除保护,由资源竞争引起的优先级逆转。
删除保护----互斥引起的一个问题会涉及到任务删除。在由信号量保护的临界区中,需要防止执行任务被意外地删除。删除一个在临界区执行的任务是灾难性的。资源会被破坏,保护资源的信号量会变为不可获得,从而该资源不可被访问。通常删除保护是与互斥操作共同提供的。由于这个原因,互斥信号量通常提供选项来隐含地提供前面提到的任务删除保护的机制。
优先级逆转/优先级继承----优先级逆转发生在一个高优先级的任务被强制等待一段不确定的时间以便一个较低优先级的任务完成执行。考虑下面的假设:
T1,T2和T3分别是高、中、低优先级的任务。T3通过拥有信号量而获得相关的资源。当T1抢占T3,为竞争使用该资源而请求相同的信号量的时候,它被阻塞。如果我们假设T1仅被阻塞到T3使用完该资源为止,情况并不是很糟。毕竟资源是不可被抢占的。然而,低优先级的任务并不能避免被中优先级的任务抢占,一个抢占的任务如T2将阻止T3完成对资源的操作。这种情况可能会持续阻塞T1等待一段不可确定的时间。这种情况成为优先级逆转,因为尽管系统是基于优先级的调度,但却使一个高优先级的任务等待一个低优先级的任务完成执行。
互斥信号量有一个选项允许实现优先级继承的算法。优先级继承通过在T1被阻塞期间提升T3的优先级到T1解决了优先级逆转引起的问题。这防止了T3,间接地防止T1,被T2抢占。通俗地说,优先级继承协议使一个拥有资源的任务以等待该资源的任务中优先级最高的任务的优先级执行。当执行完成,任务释放该资源并返回到它正常的或标准的优先级。因此,继承优先级的任务避免了被任何中间优先级的任务抢占。
同步
信号量另一种通常的用法是用于任务间的同步机制。在这种情况下,信号量代表一个任务所等待的条件或事件。最初,信号量是在清零态。一个任务或中断通过置位该信号量来指示一个事件的发生。等待该信号量的任务将被阻塞直到事件发生、该信号量被置位。一旦被解阻塞,任务就执行恰当的事件处理程序。信号量在任务同步中的应用对于将中断服务程序从冗长的事件处理中解放出来以缩短中断响应时间是很有用的。
消息队列
消息队列提供了在任务与中断服务程序或其他任务间交换变长消息的一种较低层的机制。这种机制在功能上类似于管道,但有较少的开销。
管道、套接字、远程过程调用和更多
许多高层的VxWorks机制提供任务间通信的更高层的抽象,包括管道、TCP/IP套接字、远程过程调用和更多。为了保持裁减内核为仅包含足够支持高层功能的一个最小函数集的设计目标,这些特性都是基于上面描述的内核同步方式的。
3. 内核设计的优点
Wind内核的一个重要的设计特性是最小的抢占延时。其他的主要设计的优点包括史无前例的可配置性,对不可预见的应用需求的可扩展性,在各种微处理器应用开发中的移植性。
最小的抢占延时
正如前面所讨论的,禁止抢占是获得代码临界资源互斥操作的通常手段。这种技巧的不期望的负面影响是高的抢占延时,这可以通过尽量使用信号量实现互斥和保持临界区尽量紧凑被减小。但即使广泛地使用信号量也不能解决所有的可能导致抢占延时的根源。内核本身就是一个导致抢占延时的根源。为了理解其原因,我们必须更好地理解内核所需的互斥操作。
内核级和任务级
在任何多任务系统中,大量的应用是发生在一个或多个任务的上下文。然而,有些CPU时间片不在任何任务的上下文。这些时间片发生在内核改变内部队列或决定任务调度。在这些时间片中,CPU在内核级执行,而非任务级。
为了内核安全地操作它的内部的数据结构,必须有互斥操作。内核级没有相关的任务上下文,内核不能使用信号量保护内部链表。内核使用工作延期作为实现互斥的方式。当有内核参与时,中断服务程序调用的函数不是被直接激活,而是被放在内核的工作队列中。内核完成这些请求的执行而清空内核工作队列。
当内核正在执行已经被请求服务时系统将不响应到达内核的函数调用。可以简单地认为内核状态类似于禁止抢占。如前面所讨论的,抢占延时在实时系统中是不期望有的,因为它增加了对于会引起应用任务重新调度的事件的响应时间。
尽管操作系统在内核级(此时禁止抢占)完全避免消耗时间是不可能的,但减少这些时间是很重要的。这是减少由内核执行的函数的数量的主要原因,也是不采用统一结构的系统设计方式的原因。例如,有一种流行的实时操作系统的每个函数都是在内核级执行。这意味着当一个低优先级的任务在执行分配内存、获得任务信息的函数时所有高优先级的任务被禁止抢占。
一个最小的内核
已经说明了一个最小内核的优点和构造高层操作系统功能的必要功能,我们使用这些操作原语来执行一个传统的内核级功能,而在VxWorks中作为任务级功能执行,内存管理。
在这个例子中,考虑用户可调用的子例程malloc,用于分配所请求大小的内存区并返回一个指向该内存区的指针。假定空闲内存区是通过搜索一个空闲内存块的队列找到的,一个信号量必须被用来保护这个非抢占多用户资源。分配内存的操作如下:
获得互斥信号量
搜索空闲内存块链表
释放互斥信号量
值得注意的是搜索一个足够大的空闲内存块的可能的冗长的时间是发生在调用任务的上下文中。这是可以被高优先级的任务抢占的(除了信号量调用的这段执行时间)。
在一个标准的统一结构的实时内核中,内存分配例程操作如下:
进入内核
搜索空闲内存块链表
退出内核
整个内存分配发生在内核级,任务抢占被禁止如果高优先级的任务在此时变为就绪态,它必须等待直到内核为低优先级的任务完成内存分配。有些操作系统甚至在这段时间禁止中断。
任务级操作系统服务
Wind River System的实时操作系统,VxWorks,显示了这样设计的一个最小内核是能够满足需求的。VxWorks是现在能够获得的独立于任何处理器的、拥有相当小内核的、功能完全的层次结构的实时操作系统。
VxWorks在内核之上提供了大量的功能。它包括内存管理,一个完整的BSD4.3网络包,TCP/IP,网络文件系统(NFS),远程过程调用(RPC),UNIX兼容的链接加载模块,C语言的解释界面,各种类型的定时器,性能监测组件,调试工具,额外的通信工具如管道、信号和套接字,I/O和文件系统,和许多功能例程。这些都不是运行在内核级,所以不会禁止中断或任务抢占。
可配置性
实时应用有多种内核需求。没有哪个内核有一个用来满足每种需求的很好的设计折衷。然而,一个内核可以通过配置来调整特定的性能特性,裁减实时系统来最好地适应一个应用的要求。不可预见的内核配置性以用户可选择的内核排队算法的形式提供给应用。
排队策略
VxWorks中的排队库是独立于使用他们的内核队列功能而执行的,这样提供了将来增加新的排队方式的灵活性。在VxWorks中有各种内核队列。就绪队列是一个按优先级索引的所有等待调度的任务队列。滴答队列用于定时功能。信号量是一个等待信号量的被阻塞任务的链表。活动队列是一个系统中所有任务的一个先进先出(FIFO)的链表。这些队列中的每个队列都需要一个不同的排队算法。这些算法不是被内嵌在内核中,而是被抽取到一个自治的、可转换的排队库中。这种灵活的组织形式是满足特殊的配置需求的基础。
可扩展性
支持不可预见的内核扩展的能力与已有功能的可配置性是同样重要的。简单的内核接口和互斥方法使内核级功能扩展相当容易;在某些情况下,应用可以仅利用内核钩子函数来实现特定的扩展。
内部钩子函数
为了不修改内核而能够向系统增加额外的任务相关的功能,VxWorks提供了任务创建、切换和删除的钩子函数。这些允许在任务被创建、上下文切换和任务被删除的时候额外的例程被调用执行。这些钩子函数可以利用任务上下文中的空闲区创建wind内核的任务特性。
未来考虑
有许多系统函数现在变得越来越重要,而且会影响到内核设计时的抢占延时。尽管涉及这些问题一个完整的讨论超出了本文的范围,但值得简单地提一下。
RISC/CISC
设计一个独立于CPU的操作系统一直是一个挑战。随着新的RSIC(精简指令集)处理器变得很流行,这些难度也加大了。为了在RISC环境下有效地执行,内核和操作系统需要有执行不同策略的灵活性。
例如,考虑在任务切换时内核执行的例程。在CISC(复杂指令集,如680x0或80x86)CPU,内核为每个任务存储一套完整的寄存器,在运行任务的时候将这些寄存器换入换出。在一个RISC机器上,这样是不合理的,因为涉及到太多的寄存器。所以内核需要一个更精密复杂的策略,如为任务缓存寄存器,允许应用指定一些寄存器给特殊的任务。
移植性
为了使wind内核在他们出现的结构上能够运行,需要有一个可移植的内核版本。这使移植是可行的,但不是最优化的。
多处理
支持紧耦合的多处理需求要求实时内核的内部功能包含,在理想情况下,在远端请求内核调用,如从一个处理器到另一个处理器。这就要涉及到信号量调用(为处理器间同步)和任务调用(为了控制另一个CPU上的任务)。这种复杂性无疑会增加内核级功能调用的开销,但是许多服务如对象标识可以在任务级执行。在多处理系统中保持一个最小内核的优点是处理器之间的互锁可以有较好的时间粒度。大的内核将在内核级消耗额外的时间,仅能获得粗糙的互锁时间精度。
Ada
Ada语言为实时系统设计者提供了象聚会机制这样的任务原语。异常处理、任务终止、终止替换和聚会都将潜在地影响内核设计。这些操作可以由前面讨论的任务和同步机制构造,为了保持减小抢占延时的设计目标,许多工作能够在任务级执行。
4. 实时内核的重要尺度
许多性能特性被用来比较以有的实时内核,这些包括:
快速的任务上下文切换----由于实时系统的多任务的特性,系统能够快速地从一个任务切换到另一个任务是很重要的。在分时系统中,如UNIX,上下文切换是在ms级。Wind内核执行原始上下文切换只用17us。
最小的同步开销----因为同步是实现资源互斥访问的基本方法,这些操作所引起的开销最小化是很重要的。在VxWorks中,请求和释放二值信号量仅用8us。
最小的中断延时----因为外部世界来的事件通常以中断的形式到来,操作系统快速的处理这些中断是很重要的。内核在操作一些临界数据结构的时候必须禁止中断。为了减小中断延时,必须使这些时间最小化。Wind内核的中断延时小于10us。
抢占延时对性能指标的影响
当许多的实时解决方案被提交给应用工程师时,性能指标对于评估供应商的产品变得越来越重要。不象上下文切换和中断延时,抢占延时很难测量。所以它很少在说明中被提及。但是考虑到当内核通常禁止上下文切换会长达数百微秒,而声称一个50us的固定长度(与任务个数无关)的上下文切换时间是毫无意义的。除了很难测量外,抢占延时可能会削弱许多性能指标的有效性。
Wind内核通过减小内核的大小来尽量减小抢占延时。包含繁多功能的内核必将引起长的抢占延时。
5. 结论
为了满足对实时内核日益增加的要求,如新的调度算法、多处理、Ada和RISC结构,wind内核始终以灵活性和可配置性为设计目标。它所提供的优秀的运行性能将成为实时应用需求新标准。
1. 实时操作系统的结构
在计算机的早期开发的操作系统的最原始的结构形式是一个统一的实体(monolithic)。在这样的系统中,提供的不同功能的模块,如处理器管理、内存管理、输入输出等,通常是独立的。然而他们在执行过程中并不考虑其他正在使用中的模块,各个模块都以相同的时间粒度运行。
由于现代实时环境需要许多不同的功能,以及在这样的环境中存在的并发活动所引起的异步性和非确定性,操作系统变得更加复杂。所以早期操作系统的统一结构的组织已经被更加精确的内部结构所淘汰。
层次结构的起点----内核
操作系统的最好的内部结构模型是一个层次性的结构,最低层是内核。这些层次可以看成为一个倒置的金字塔,每一层都建立在较低层的功能之上。内核仅包含一个操作系统执行的最重要的低层功能。正象一个统一结构的操作系统,内核提供了在高层软件与下层硬件之间的抽象层。然而,内核仅提供了构造操作系统其他部分所需的最小操作集。
对一个实时内核的要求
一个实时操作系统内核需满足许多特定的实时环境所提出的基本要求,这些包括:
多任务:由于真实世界的事件的异步性,能够运行许多并发进程或任务是很重要的。多任务提供了一个较好的对真实世界的匹配,因为它允许对应于许多外部事件的多线程执行。系统内核分配CPU给这些任务来获得并发性。
抢占调度:真实世界的事件具有继承的优先级,在分配CPU的时候要注意到这些优先级。基于优先级的抢占调度,任务都被指定了优先级,在能够执行的任务(没有被挂起或正在等待资源)中,优先级最高的任务被分配CPU资源。换句话说,当一个高优先级的任务变为可执行态,它会立即抢占当前正在运行的较低优先级的任务。
快速灵活的任务间的通信与同步:在一个实时系统中,可能有许多任务作为一个应用的一部分执行。系统必须提供这些任务间的快速且功能强大的通信机制。内核也要提供为了有效地共享不可抢占的资源或临界区所需的同步机制。
方便的任务与中断之间的通信:尽管真实世界的事件通常作为中断方式到来,但为了提供有效的排队、优先化和减少中断延时,我们通常希望在任务级处理相应的工作。所以需要杂任务级和中断级之间存在通信。
性能边界:一个实时内核必须提供最坏情况的性能优化,而非针对吞吐量的性能优化。我们更期望一个系统能够始终以50微秒执行一个函数,而不期望系统平均以10微秒执行该函数,但偶尔会以75微秒执行它。
特殊考虑:由于对实时内核的要求的增加,必须考虑对内核支持不断增加的复杂功能的要求。这包括多进程处理,Ada和对更新的、功能更强的处理器结构如RISC的支持。
拥有其它名字的内核
许多商用化的内核支持的功能远强于上面所列的要求。在这方面,他们不是真正的内核,而更象一个小的统一结构的操作系统。因为他们包含简单的内存分配、时钟管理、甚至一些输入输出系统调用的功能。
这种分类不仅仅是在语义上的争论,在这篇文章的后面章节将说明限制内核功能和优化这些功能的重要性。
2. VxWorks内核:Wind
VxWorks操作系统是一种功能最全的现在可以获得的独立于处理器的实时系统。然而,VxWorks是带有一个相当小的真正微内核的层次结构。内核仅提供多任务环境、进程间通信和同步功能。这些功能模块足够支持VxWorks在较高层次所提供的丰富的性能的要求。
通常内核操作对于用户是不可见的。应用程序为了实现需要内核参与的任务管理和同步使用一些系统调用,但这些调用的处理对于调用任务是不可见的。应用程序仅链接恰当的VxWorks例程(通常使用VxWorks的动态链接功能),就象调用子程序一样发出系统调用。这种接口不象有些系统需要一个笨拙的跳转表接口,用户需要通过一个整数来指定一个内核功能调用。
多任务
内核的基本功能是提供一个多任务环境。多任务使得许多程序在表面上表现为并发执行,而事实上内核是根据基本的调度算法使他们分段执行。每个明显独立的程序被成为一个任务。每个任务拥有自己的上下文,其中包含在内核调度使该任务执行的时候它所看到的CPU环境和系统资源。
任务状态
内核维护系统中的每个任务的当前状态。状态迁移发生在应用程序调用内核功能服务的时候。下面定义了wind内核状态:
就绪态----一个任务当前除了CPU不等待任何资源
阻塞态----一个任务由于某些资源不可获得而被阻塞
延迟态----一个任务睡眠一段时间
挂起态----主要用于调试的一个辅助状态,挂起禁止任务的执行
任务被创建以后进入挂起态,需要通过特定的操作使被创建的任务进入就绪态,这一操作执行速度很快,使应用程序能够提前创建任务,并以一种快捷的方式激活该任务。
调度控制
多任务需要一个调度算法分配CPU给就绪的任务。在VxWorks中默认的调度算法是基于优先级的抢占调度,但应用程序也可以选择使用时间片轮转调度。
基于优先级抢占调度:基于优先级的抢占调度,每个任务被指定一个优先级,内核分配CPU给处于就绪态的优先级最高的任务。调度采用抢占的方式,是因为当一个优先级高于当前任务的任务变为就绪态时,内核将立即保存当前任务的上文,并切换到高优先级任务的上文。VxWorks有从0到255共256个优先级。在创建的时候任务被指定一个优先级,在任务运行的过程中可以动态地修改优先级以便跟踪真实世界的事件优先级。外部中断被指定优先于任何任务的优先级,这样能够在任何时候抢占一个任务。
时间片轮转:基于优先级抢占调度可以扩充时间片轮转调度。时间片轮转调度允许在相同优先级的处于就绪态的任务公平地共享CPU。没有时间片轮转调度,当有多个任务在同一优先级共享处理器时,一个任务可能独占CPU,不会被阻塞直到被一个更高优先级的任务抢占,而不给同一优先级的其他任务运行的机会。如果时间片轮转被使能,执行任务的时间计数器在每个时钟滴答递增。当指定的时间片耗尽,计数器会被清零,该任务被放在同一优先级任务队列的队尾。加入特定优先级组的新任务被放在该组任务的队尾,并将运行计数器初始化为零。
基本的任务函数
用于状态控制的基本任务函数包括一个任务的创建、删除、挂起和唤醒。一个任务也可以使自己睡眠一个特定的时间间隔不去运行。
许多其他任务例程提供由任务上下文获得的状态信息。这些例程包括访问一个任务当前处理器寄存器控制。
任务删除问题
Wind内核提供防止任务被意外删除的机制。通常,一个执行在临界区或访问临界资源的任务要被特别保护。我们设想下面的情况:一个任务获得一些数据结构的互斥访问权,当它正在临界区内执行时被另一个任务删除。由于任务无法完成对临界区的操作,该数据结构可能还处于被破坏或不一致的状态。而且,假想任务没有机会释放该资源,那麽现在其他任何任务现在就不能获得该资源,资源被冻结了。
任何要删除或终止一个设定了删除保护的任务的任务将被阻塞。当被保护的任务完成临界区操作以后,它将取消删除保护以使自己可以被删除,从而解阻塞删除任务。
正如上面所展示的,任务删除保护通常伴有互斥操作。
这样,为了方便性和效率,互斥信号量包含了删除保护选项。(参见"互斥信号量")
任务间通信
为了提供完整的多任务系统的功能,wind内核提供了一套丰富的任务间通信与同步的机制。这些通信功能使一个应用中各个独立的任务协调他们的活动。
共享地址空间
Wind内核的任务间通信机制的基础是所有任务所在的共享地址空间。通过共享地址空间,任务能够使用共享数据结构的指针自由地通信。管道不需要映射一块内存区到两个互相通信任务的寻址空间。
不幸的是,共享地址空间具有上述优点的同时,带来了未被保护内存的重入访问的危险。UNIX操作系统通过隔离进程提供这样的保护,但同时带来了对于实时操作系统来说巨大的性能损失。
互斥操作
当一个共享地址空间简化了数据交换,通过互斥访问避免资源竞争就变为必要的了。用来获得一个资源的互斥访问的许多机制仅在这些互斥所作用的范围上存在差别。实现互斥的方法包括禁止中断、禁止任务抢占和通过信号量进行资源锁定。
中断禁止:最强的互斥方法是屏蔽中断。这样的锁定保证了对CPU的互斥访问。这种方法当然能够解决互斥的问题,但它对于实时是不恰当的,因为它在锁定期间阻止系统响应外部事件。长的中断延时对于要求有确定的响应时间的应用来说是不可接受的。
抢占禁止:禁止抢占提供了强制性较弱的互斥方式。当前任务运行的过程中不允许其他任务抢占,而中断服务程序可以执行。这也可能引起较差的实时响应,就象被禁止中断一样,被阻塞的任务会有相当长时间的抢占延时,就绪态的高优先级的任务可能会在能够执行前被强制等待一段不可接受的时间。为避免这种情况,在可能的情况下尽量使用信号量实现互斥。
互斥信号量:信号量是用于锁定共享资源访问的基本方式。不象禁止中断或抢占,信号量限制了互斥操作仅作用于相关的资源。一个信号量被创建来保护资源。VxWorks的信号量遵循Dijkstra的P()和V()操作模式。
当一个任务请求信号量,P(),根据在发出调用时信号量的置位或清零的状态,会发生两种情况。如果信号量处于置位态,信号量会被清零,并且任务立即继续执行。如果信号量处于清零态,任务会被阻塞来等待信号量。
当一个任务释放信号量,V(),会发生几种情况。如果信号量已经处于置位态,释放信号量不会产生任何影响。如果信号量处于清零态且没有任务等待该信号量,信号量只是被简单地置位。如果信号量处于清零态且有一个或多个任务等待该信号量,最高优先级的任务被解阻塞,信号量仍为清零态。
通过将一些资源与信号量关联,能够实现互斥操作。当一个任务要操作资源,它必须首先获得信号量。只要任务拥有信号量,所有其他的任务由于请求该信号量而被阻塞。当一个任务使用完该资源,它释放信号量,允许等待该信号量的另一个任务访问该资源。
Wind内核提供了二值信号量来解决互斥操作所引起的问题。这些问题包括资源拥有者的删除保护,由资源竞争引起的优先级逆转。
删除保护----互斥引起的一个问题会涉及到任务删除。在由信号量保护的临界区中,需要防止执行任务被意外地删除。删除一个在临界区执行的任务是灾难性的。资源会被破坏,保护资源的信号量会变为不可获得,从而该资源不可被访问。通常删除保护是与互斥操作共同提供的。由于这个原因,互斥信号量通常提供选项来隐含地提供前面提到的任务删除保护的机制。
优先级逆转/优先级继承----优先级逆转发生在一个高优先级的任务被强制等待一段不确定的时间以便一个较低优先级的任务完成执行。考虑下面的假设:
T1,T2和T3分别是高、中、低优先级的任务。T3通过拥有信号量而获得相关的资源。当T1抢占T3,为竞争使用该资源而请求相同的信号量的时候,它被阻塞。如果我们假设T1仅被阻塞到T3使用完该资源为止,情况并不是很糟。毕竟资源是不可被抢占的。然而,低优先级的任务并不能避免被中优先级的任务抢占,一个抢占的任务如T2将阻止T3完成对资源的操作。这种情况可能会持续阻塞T1等待一段不可确定的时间。这种情况成为优先级逆转,因为尽管系统是基于优先级的调度,但却使一个高优先级的任务等待一个低优先级的任务完成执行。
互斥信号量有一个选项允许实现优先级继承的算法。优先级继承通过在T1被阻塞期间提升T3的优先级到T1解决了优先级逆转引起的问题。这防止了T3,间接地防止T1,被T2抢占。通俗地说,优先级继承协议使一个拥有资源的任务以等待该资源的任务中优先级最高的任务的优先级执行。当执行完成,任务释放该资源并返回到它正常的或标准的优先级。因此,继承优先级的任务避免了被任何中间优先级的任务抢占。
同步
信号量另一种通常的用法是用于任务间的同步机制。在这种情况下,信号量代表一个任务所等待的条件或事件。最初,信号量是在清零态。一个任务或中断通过置位该信号量来指示一个事件的发生。等待该信号量的任务将被阻塞直到事件发生、该信号量被置位。一旦被解阻塞,任务就执行恰当的事件处理程序。信号量在任务同步中的应用对于将中断服务程序从冗长的事件处理中解放出来以缩短中断响应时间是很有用的。
消息队列
消息队列提供了在任务与中断服务程序或其他任务间交换变长消息的一种较低层的机制。这种机制在功能上类似于管道,但有较少的开销。
管道、套接字、远程过程调用和更多
许多高层的VxWorks机制提供任务间通信的更高层的抽象,包括管道、TCP/IP套接字、远程过程调用和更多。为了保持裁减内核为仅包含足够支持高层功能的一个最小函数集的设计目标,这些特性都是基于上面描述的内核同步方式的。
3. 内核设计的优点
Wind内核的一个重要的设计特性是最小的抢占延时。其他的主要设计的优点包括史无前例的可配置性,对不可预见的应用需求的可扩展性,在各种微处理器应用开发中的移植性。
最小的抢占延时
正如前面所讨论的,禁止抢占是获得代码临界资源互斥操作的通常手段。这种技巧的不期望的负面影响是高的抢占延时,这可以通过尽量使用信号量实现互斥和保持临界区尽量紧凑被减小。但即使广泛地使用信号量也不能解决所有的可能导致抢占延时的根源。内核本身就是一个导致抢占延时的根源。为了理解其原因,我们必须更好地理解内核所需的互斥操作。
内核级和任务级
在任何多任务系统中,大量的应用是发生在一个或多个任务的上下文。然而,有些CPU时间片不在任何任务的上下文。这些时间片发生在内核改变内部队列或决定任务调度。在这些时间片中,CPU在内核级执行,而非任务级。
为了内核安全地操作它的内部的数据结构,必须有互斥操作。内核级没有相关的任务上下文,内核不能使用信号量保护内部链表。内核使用工作延期作为实现互斥的方式。当有内核参与时,中断服务程序调用的函数不是被直接激活,而是被放在内核的工作队列中。内核完成这些请求的执行而清空内核工作队列。
当内核正在执行已经被请求服务时系统将不响应到达内核的函数调用。可以简单地认为内核状态类似于禁止抢占。如前面所讨论的,抢占延时在实时系统中是不期望有的,因为它增加了对于会引起应用任务重新调度的事件的响应时间。
尽管操作系统在内核级(此时禁止抢占)完全避免消耗时间是不可能的,但减少这些时间是很重要的。这是减少由内核执行的函数的数量的主要原因,也是不采用统一结构的系统设计方式的原因。例如,有一种流行的实时操作系统的每个函数都是在内核级执行。这意味着当一个低优先级的任务在执行分配内存、获得任务信息的函数时所有高优先级的任务被禁止抢占。
一个最小的内核
已经说明了一个最小内核的优点和构造高层操作系统功能的必要功能,我们使用这些操作原语来执行一个传统的内核级功能,而在VxWorks中作为任务级功能执行,内存管理。
在这个例子中,考虑用户可调用的子例程malloc,用于分配所请求大小的内存区并返回一个指向该内存区的指针。假定空闲内存区是通过搜索一个空闲内存块的队列找到的,一个信号量必须被用来保护这个非抢占多用户资源。分配内存的操作如下:
获得互斥信号量
搜索空闲内存块链表
释放互斥信号量
值得注意的是搜索一个足够大的空闲内存块的可能的冗长的时间是发生在调用任务的上下文中。这是可以被高优先级的任务抢占的(除了信号量调用的这段执行时间)。
在一个标准的统一结构的实时内核中,内存分配例程操作如下:
进入内核
搜索空闲内存块链表
退出内核
整个内存分配发生在内核级,任务抢占被禁止如果高优先级的任务在此时变为就绪态,它必须等待直到内核为低优先级的任务完成内存分配。有些操作系统甚至在这段时间禁止中断。
任务级操作系统服务
Wind River System的实时操作系统,VxWorks,显示了这样设计的一个最小内核是能够满足需求的。VxWorks是现在能够获得的独立于任何处理器的、拥有相当小内核的、功能完全的层次结构的实时操作系统。
VxWorks在内核之上提供了大量的功能。它包括内存管理,一个完整的BSD4.3网络包,TCP/IP,网络文件系统(NFS),远程过程调用(RPC),UNIX兼容的链接加载模块,C语言的解释界面,各种类型的定时器,性能监测组件,调试工具,额外的通信工具如管道、信号和套接字,I/O和文件系统,和许多功能例程。这些都不是运行在内核级,所以不会禁止中断或任务抢占。
可配置性
实时应用有多种内核需求。没有哪个内核有一个用来满足每种需求的很好的设计折衷。然而,一个内核可以通过配置来调整特定的性能特性,裁减实时系统来最好地适应一个应用的要求。不可预见的内核配置性以用户可选择的内核排队算法的形式提供给应用。
排队策略
VxWorks中的排队库是独立于使用他们的内核队列功能而执行的,这样提供了将来增加新的排队方式的灵活性。在VxWorks中有各种内核队列。就绪队列是一个按优先级索引的所有等待调度的任务队列。滴答队列用于定时功能。信号量是一个等待信号量的被阻塞任务的链表。活动队列是一个系统中所有任务的一个先进先出(FIFO)的链表。这些队列中的每个队列都需要一个不同的排队算法。这些算法不是被内嵌在内核中,而是被抽取到一个自治的、可转换的排队库中。这种灵活的组织形式是满足特殊的配置需求的基础。
可扩展性
支持不可预见的内核扩展的能力与已有功能的可配置性是同样重要的。简单的内核接口和互斥方法使内核级功能扩展相当容易;在某些情况下,应用可以仅利用内核钩子函数来实现特定的扩展。
内部钩子函数
为了不修改内核而能够向系统增加额外的任务相关的功能,VxWorks提供了任务创建、切换和删除的钩子函数。这些允许在任务被创建、上下文切换和任务被删除的时候额外的例程被调用执行。这些钩子函数可以利用任务上下文中的空闲区创建wind内核的任务特性。
未来考虑
有许多系统函数现在变得越来越重要,而且会影响到内核设计时的抢占延时。尽管涉及这些问题一个完整的讨论超出了本文的范围,但值得简单地提一下。
RISC/CISC
设计一个独立于CPU的操作系统一直是一个挑战。随着新的RSIC(精简指令集)处理器变得很流行,这些难度也加大了。为了在RISC环境下有效地执行,内核和操作系统需要有执行不同策略的灵活性。
例如,考虑在任务切换时内核执行的例程。在CISC(复杂指令集,如680x0或80x86)CPU,内核为每个任务存储一套完整的寄存器,在运行任务的时候将这些寄存器换入换出。在一个RISC机器上,这样是不合理的,因为涉及到太多的寄存器。所以内核需要一个更精密复杂的策略,如为任务缓存寄存器,允许应用指定一些寄存器给特殊的任务。
移植性
为了使wind内核在他们出现的结构上能够运行,需要有一个可移植的内核版本。这使移植是可行的,但不是最优化的。
多处理
支持紧耦合的多处理需求要求实时内核的内部功能包含,在理想情况下,在远端请求内核调用,如从一个处理器到另一个处理器。这就要涉及到信号量调用(为处理器间同步)和任务调用(为了控制另一个CPU上的任务)。这种复杂性无疑会增加内核级功能调用的开销,但是许多服务如对象标识可以在任务级执行。在多处理系统中保持一个最小内核的优点是处理器之间的互锁可以有较好的时间粒度。大的内核将在内核级消耗额外的时间,仅能获得粗糙的互锁时间精度。
Ada
Ada语言为实时系统设计者提供了象聚会机制这样的任务原语。异常处理、任务终止、终止替换和聚会都将潜在地影响内核设计。这些操作可以由前面讨论的任务和同步机制构造,为了保持减小抢占延时的设计目标,许多工作能够在任务级执行。
4. 实时内核的重要尺度
许多性能特性被用来比较以有的实时内核,这些包括:
快速的任务上下文切换----由于实时系统的多任务的特性,系统能够快速地从一个任务切换到另一个任务是很重要的。在分时系统中,如UNIX,上下文切换是在ms级。Wind内核执行原始上下文切换只用17us。
最小的同步开销----因为同步是实现资源互斥访问的基本方法,这些操作所引起的开销最小化是很重要的。在VxWorks中,请求和释放二值信号量仅用8us。
最小的中断延时----因为外部世界来的事件通常以中断的形式到来,操作系统快速的处理这些中断是很重要的。内核在操作一些临界数据结构的时候必须禁止中断。为了减小中断延时,必须使这些时间最小化。Wind内核的中断延时小于10us。
抢占延时对性能指标的影响
当许多的实时解决方案被提交给应用工程师时,性能指标对于评估供应商的产品变得越来越重要。不象上下文切换和中断延时,抢占延时很难测量。所以它很少在说明中被提及。但是考虑到当内核通常禁止上下文切换会长达数百微秒,而声称一个50us的固定长度(与任务个数无关)的上下文切换时间是毫无意义的。除了很难测量外,抢占延时可能会削弱许多性能指标的有效性。
Wind内核通过减小内核的大小来尽量减小抢占延时。包含繁多功能的内核必将引起长的抢占延时。
5. 结论
为了满足对实时内核日益增加的要求,如新的调度算法、多处理、Ada和RISC结构,wind内核始终以灵活性和可配置性为设计目标。它所提供的优秀的运行性能将成为实时应用需求新标准。
评论
发表评论