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, 用于分配所请求大小的内存区
并返回一个指向该内存区的指针。假定空闲内存区是通过搜索一个空闲内存块的队列找
到的,一个信号量必须被用来保护这个非抢占多用户资源。分配内存的操作如下:
获得互斥信号量
搜索空闲内存块链表
释放互斥信号量
值得注意的是搜索一个足够大的空闲内存块的可能的冗长的时间是发生在调用任务
的上下文中。这是可以被高优先级的任务抢占的(除了信号量调用的这段执行时间)。
在一个标准的统一结构的实时内核中,内存分配例程操作如下:
进入内核