嵌入式操作系统设计中的若干问题

1 什么是嵌入式操作系统
随着计算机技术的发展,嵌入式应用已经成为计算机应用领域的一个重要组成部分。所谓嵌入式应用,是指应用系统不是以计算机为主,而是把计算机技术做为应用系统的一个组成部分嵌入到应用系统中,使其具有更高的性能。例如:程控交换机、手持式电话等,计算机的嵌入式应用领域是十分广阔的。
为了适应嵌入式应用的复杂性和多样性,并缩短其开发周期,嵌入式操作系统应运而生。嵌入式操作系统是相对于一般操作系统而言,通常指的是操作系统的核,严格地说是微内核。嵌入式操作系统为了适应被嵌入系统的要求具备了一般操作系统最基本的功能,如任务调度、同步机制、中断处理,但是没有一般操作系统的用户界面,如Shell。嵌入式操作系统是以库的形式提供给用户,用户可以通过操作系统的API(Application Programming Interface)使用嵌入式操作系统。

2 操作系统的几种结构

    单一操作系统
    操作系统由许多模块组成,这些模块之间可以相互调用。在这样的操作系统中通常有二种工作模式,即系统模式和用户模式。在系统模式下可以执行任何操作,而在用户模式下有些操作是受限制的,例如I/O操作和一些特殊指令。运行在用户模式的应用程序可以通过系统调用进入系统模式,完成操作后再返回用户模式,但是这样的操作系统很难维护和调试。 层次结构操作系统
    层次结构操作系统通常可以分为用户程序、I/O管理、进程通信、存储管理4层。程序可以在各层运行,这样的操作系统提供了"环"管理机制,即第n环的程序无权修改第n-1环的数据,从而提高了操作系统的安全性。 客户/服务器方式操作系统
    这种操作系统只有一个很小的内核,以完成进程间通信等基本功能,而把许多其它功能作为服务进程,运行于用户模式,用户程序作为客户进程。其工作方式是客户进程发出请求,服务进程响应请求,操作系统仅完成它们之间的通信。 嵌入式操作系统
    一般的操作系统不仅向用户提供开发应用程序的各种API,而且,还必须以命令行的形式或是图形的形式提供一个界面。但是嵌入式操作系统只有API而没有通常意义下的界面,亦即只有一个核心。在核心里只有操作系统的一些基本功能,如任务(线程)调度、存储管理、同步机制、中断管理、API等,而这些功能又可以根据不同的应用系统裁剪和扩充,以便以最小的代码量满足嵌入式系统的需求。

3 设计嵌入式操作系统应考虑的问题
确切地说,嵌入式设计不是一种技术,而是一种概念,一种设计哲学,即把计算机相关技术嵌入到各种应用系统中去,嵌入的方式可以是软件嵌入或硬件嵌入。在这种概念下,一个好的嵌入式设计应该满足以下几点:

    使用相关计算机的技术使被嵌入的系统获得很好的性能价格比; 能很好地与被嵌入系统相匹配; 在满足被嵌入系统功能的前提下尽可能简单,避免导致大的系统开销。

从某种意义上说,操作系统是计算机的一个扩展,它赋予了计算机更多的功能;从另一个角度看,操作系统也是计算机系统的资源管理者。因此,在嵌入式操作系统中,内核的最小构成也应包含下列各部分。
(1)任务调度
在当前的嵌入式应用中,特别是对一个复杂的嵌入式应用而言,用单任务来实现是不可能的,而只能采用多任务实现,因此任务调度策略的优劣是至关重要的。
虽然有各种各样的任务调度方法,但概括起来可以分为优先级调度、轮转调度、时间片调度3种。
优先级调度又可以分为2种:优先级抢占和优先级不抢占。优先级抢占是指当有高的优先级进程被激活后,则立即终止当前运行的进程,使其抢占CPU运行。优先级不抢占是指当有高的优先级进程被激活后,并不立即终止当前运行的进程使其抢占CPU,而是将其放到进程就绪队列中,在当前运行的进程结束后,从进程就绪队列中选择优先级最高的进程运行。轮转调度是进程管理者按照一个固定的时间间隔让就绪进程轮流运行。时间片调度是根据每个进程各自的实际情况在不同数量的单位时间的时间片内运行。
在一般的操作系统中,固定采用其中的1种或几种方法进行任务调度。例如在Unix和Linux中,采用了优先级不抢占、轮转和时间片3种方法,而在实时操作系统中,优先级抢占则是必不可少的。
无论是在一般的操作系统中还是在嵌入式操作系统中,任务调度都是操作系统的核心。在嵌入式操作系统设计中,很难明确地说哪一种方法比其它方法更好,而要根据被嵌入的实际系统的需求进行优化选择。由于线程的切换速度快于进程的切换速度,因此,在实时嵌入式应用中,多采用单进程多线程调度来提高实时性。在嵌入式操作系统中一般都配有上述3种调度算法的库函数,以满足不同用户的不同需求。在用户开发完自己的应用系统后,没有使用到的代码是不会被连接到最终系统中的,这一点是与一般的操作系统非常重要的区别。一般的操作系统则是将所有有关代码都加载到计算机系统中,而不论应用时是否会使用这些代码。
在实时嵌入式应用中还有一种调度方法,即期限(deadline)调度法。这种调度方法是设法保证每一个进程(或线程)能在它的期限之前被调度执行完,这种方法目前正在研究之中。
(2)存储管理
一般操作系统的存储管理非常复杂,虚拟管理被广泛地使用,简单地说虚拟存储器管理就是通过操作系统对内存和外存的管理,可以使应用程序获得比计算机实际内存更大的编址空间,使每一个应用程序都可以享有一个独立的虚拟存储空间。程序访问的地址是虚拟地址,操作系统通过段、页管理完成虚拟地址到物理地址的转换和页面的换入/换出,同时管理和维护各应用程序之间共享的存储空间。这样各应用程序被分隔在各自的地址空间运行,不致相互干扰。
嵌入式操作系统的存储管理通常比较简单。在具体的嵌入式应用中,进程(或线程)的数量和各自可能使用的内存容量是可以在开发时预测的,因此嵌入式操作系统通常采用静态内存分配。尤其是在实时嵌入式应用中,如果采用虚拟存储技术,因为页交换的时间不可预测,所以是不可取的。对于动态内存分配通常的做法也是从缓冲区中动态分配一块固定大小的内存,在使用完毕后就释放。一般地说,嵌入式操作系统的存储管理没有垃圾收集的功能。在一个复杂的应用系统中,可能会有几种情况的组合,应视具体情况处理。例如,在一个有多个处理器,且既有硬实时应用,又有软实时应用和非实时应用的嵌入式系统中,设计时可以在硬实时部分采用静态内存分配,软实时部分采用动态内存分配,而在非实时部分采用虚拟存储技术,并且使这三种应用分别运行在不同的处理器上。
在内存分配的策略上,嵌入式操作系统强烈地依赖于实际的被嵌入系统。一个通用嵌入式操作系统可以提供几种分配策略,而用户在开发自己的实际系统时可以选择,这就再次强调了前文提到的嵌入式操作系统的可裁剪性。
存储管理的另一个重要特性就是内存保护。在一般的操作系统中,每个应用程序都有自己的地址空间,不能任意访问其它应用程序的地址空间。这样,当一个应用程序崩溃时,不会对其它程序产生影响。尽管存储器芯片价格已经很便宜,但因受应用环境的限制,不能大量使用存储器,这时嵌入式操作系统的代码量就受到严格限制。例如有的嵌入式操作系统只有几KB,使其在处理内存保护方面就非常薄弱,这样的嵌入式操作系统一般应用于一些即使系统崩溃,也不致造成重大损失的领域,例如手持式电话。但某些嵌入式应用则对内存保护有非常严格的要求,例如在武器系统中,就要求嵌入式操作系统具有内存保护的功能。
(3)中断
计算机系统接受事件有二种方法:查询和中断。在多任务操作系统中,由于采用查询方式处理事件或I/O请求会消耗大量的系统资源—— CPU时间。因此无论在一般的操作系统还是嵌入式操作系统中都采用中断方式来处理事件或I/O请求。
在操作系统中,中断是同中断处理程序联系在一起的。以I/O操作为例,一般的过程是:任务A发出I/O请求后被挂起→操作系统切换到其它任务运行→I/O设备完成相应的操作并发出中断请求→操作系统调用相应的中断处理程序→解挂任务A。
在嵌入式操作系统中,对中断处理十分重视,可以说多数嵌入式操作系统都是事件驱动的。在嵌入式操作系统中,中断处理程序引发的任务切换如下所示:
任务A运行→响应中断并执行中断处理程序,切换到任务B→任务B运行退出后,切换到任务A→任务A继续运行
在中断处理程序(ISR)中仅执行一些必要的状态转换,对于事件的真正处理则利用中断任务(如上述的任务B)完成。这样使得中断处理程序的运行时间尽可能短,以便系统可以处理其它中断事件。同时,在ISR中不能执行有关信号量的操作。因为ISR具有最高优先级,如果在ISR中执行了信号量操作而被挂起,则整个系统将会死锁。
在中断处理上,一般的操作系统与嵌入式操作系统的不同之处是现场保护。一般的操作系统的中断现场保护是由操作系统来完成的,在中断处理完成之后,也由操作系统恢复现场。在嵌入式操作系统中,有时由于受到代码量的限制,中断现场的保护往往由中断处理程序来完成。在中断处理程序的入口要保护在中断处理程序中用到的寄存器,在中断处理完成后恢复。这样一方面减少了代码量,另一方面提高了中断响应时间,但是却损失了系统的安全性,同时也增加了调试的难度。这是在嵌入式操作系统的设计中应该予以关注的问题。
(4)操作系统与用户的接口
操作系统提供给用户使用的有二类接口。一类是人机界面,无论是视窗形式还是命令行形式,这个接口确切地说并不能做为操作系统的一部分,而仅仅是操作系统的一个外壳,这个界面是为了方便用户使用操作系统,而这个接口在嵌入式操作系统中是不存在的。这里要讨论的是另一个接口,操作系统提供给用户开发自己的应用程序接口(API),也就是系统调用。无论是一般的操作系统还是嵌入式操作系统都应具有这个接口。每一个操作系统提供的系统调用的功能和种类都不同,当然,对于一个操作系统来说,它提供的系统调用越多,则功能越强,对于应用程序的开发,也就越能提供高效而简单的支持,同时也会减少应用程序的维护量。相反,一个操作系统的系统调用越少越单一,那么应用程序相对就要做更多的工作,应用程序也就越复杂。为了适应不断复杂的应用程序开发的需求,操作系统中设计的系统调用也就越来越多,越来越复杂,功能越来越强大。但是这一规律并不适用于嵌入式操作系统,嵌入式操作系统的应用领域非常广,简单的可以应用在调制解调器上,复杂的可以应用在卫星地面通信接收站。这就决定了嵌入式操作系统所提供的系统调用的数量和功能是因应用不同而不同的。尽管前文提到的可裁剪性是嵌入式操作系统的一个非常重要的特性,但是任何一个嵌入式操作系统都不可能从具有各种完善功能、代码达几百KB的操作系统,裁剪到只具有实时调度和信号量操作的几KB代码。所以嵌入式操作系统只能面向实际的被嵌入系统的具体需求,确定系统调用,以便达到在提供最有效的系统调用的同时具有最小的代码量。
最后,在系统调用的形式上要提到POSIX。由于各个操作系统提供自己的系统调用,其类型、功能和调用格式各不相同,这样给应用程序的移植带来了很大困难。POSIX标准的提出正是试图解决这一问题。POSIX试图定义一些标准的系统调用接口和功能,尽管各个操作系统的实现方式各不相同。POSIX是以类Unix为基础开发的,同时,它试图将实时和非实时的情况统一化,这样就丧失了一定的效率和增加了代码量,所以有些操作系统在提供POSIX兼容的系统调用的同时,也提供了非POSIX兼容的系统调用。

4 结论
以上从构成一个操作系统的几个最重要的组成部分讨论了嵌入式操作系统与一般的操作系统的相同与不同之处。当然,构成一个完整的操作系统,还需要其它组成部分,如时钟、同步/互斥、进程间通信等。从以上分析可以看出,要设计一个好的嵌入式操作系统,必须充分考虑被嵌入的系统,要根据实际的应用来设计、选择操作系统。虽然嵌入式操作系统是可裁剪的,但这种裁剪也是有限的。
嵌入式系统正在蓬勃发展,存在着无限的商机,在这一领域,机遇与挑战并存。嵌入式操作系统的设计将在竞争中起决定性作用。