1034嵌入式操作系统嵌入式操作系统是相对于其他PC操作系统而言的,是嵌入式系统设计的灵魂,它的出现大大提高了嵌入式系统开发的效率。在嵌入式开发中,操作系统不仅极大地减少了系统开发的工作总量,而且提高了嵌入式系统软件的可移植性。近10年来,嵌入式操作系统得到了飞速的发展,从支持8位微处理器到16位、32位甚至64位微处理器;从支持单一品种的微处理器芯片到支持多种品种的微处理器芯片;从只有内核到除了内核还提供其它功能模块,如文件系统、TCP/IP网络系统和窗口图形系统等。目前,实时嵌入式操作系统的种类繁多,大体上可分为两种——商用型和免费型。商用型的实时操作系统功能稳定、可靠,有完善的技术支持和售后服务,但往往价格昂贵;免费型的实时操作系统在价格方面则具有较大优势。本章从操作系统的基本概念出发,讲解了操作系统中进程、任务、内核、调度、通信、同步等概念及嵌入式实时操作系统相应特点。针对当前实际应用情况,详细讲解了最为流行,应用最为广泛的几款嵌入式实时操作系统的结构、特点及其移植方法。其中包括免费型的µC/OS-Ⅱ,µCLinux,以及商用型的WinCE。4.1操作系统的基本概念4.1.1操作系统的结构和功能为了满足嵌入式应用,嵌入式实时操作系统可以根据实际应用环境的要求对内核进行裁剪和重新配置。一般的,实时操作系统总是由以下几个重要部分组成:实时内核、网络组件、文件系统和图形用户接口等,其体系结构如图4.1所示。应用程序接口(API)基本模块内存管理文件管理任务调度内核扩展模块网络模块图形用户接口数据库接口驱动程序、硬件抽象层、板级支持包OS层中间层应用程序应用程序应用层硬件(Hardware)图4.1嵌入式实时操作系统的体系结构从图4-1中可以看出,除了任务管理、任务调度,操作系统还提供大量其他的服务。嵌入式操作系统相对于一般操作系统而言,常常只包括操作系统的内核(或微内核),其他诸如网络模块、图形用户接口、通信协议等模块,可以根据实际需求另外选择。大多数嵌入式操作系统一般必须提供多任务管理、内存管理和外围资源(如I/O设备、通信端口)管理等功能。1044.1.2多进程和多线程许多嵌入式系统并不是单纯的完成一种功能。例如,在一个电话机应答系统中,需要把记录通话信息和操作用户控制面板定义为不同的任务,因为它们不仅在逻辑上进行的是不同的操作,而且完成的速度也不同。这些不同的任务构成了应答机系统功能的各个部分,为了完成多个任务,满足组织程序结构的需要,引入了进程的概念。一个进程可以简单地认为是一个程序的唯一执行。进程是顺序执行的,而且CPU一次只能执行一个进程。但是,当确定了一个进程的完整状态后,就可以强制CPU停止执行当前进程而执行另一个进程。通过改变CPU中的程序计数器,使其指向新进程的代码,同时将新进程的数据移入寄存器和主存中,就可以实现进程的切换。这样,就能够使多个进程同时存在于CPU中。在嵌入式系统中,一个进程的常用形式是线程(如图4.2所示)。线程在CPU的寄存器中有各自不同的值集合,但是共存于一个主存储空间中。线程普遍应用于嵌入式系统中(即任务),这样可以避免存储管理单元的复杂,节约存储单元的消耗。操作系统核心(Kernel)进程线程线程进程图4.2进程与线程的关系4.1.3任务在嵌入式系统中,一个任务也称作一个线程,是一个程序,该程序在运行时可以认为CPU完全只属于该程序自己。在实时应用程序的设计过程中,要考虑如何将应用功能合理地划分为多个任务,让每个任务完成一定的功能,成为整个应用的一部分。每个任务都被赋予一定的优先级,有自己的一套CPU寄存器和栈空间(如图4.3所示)。每一个任务都有其优先级,任务越重要,赋予的优先级越高。就大多数内核而言,任务的优先级由用户决定。应用程序执行过程中诸任务优先级不变,则称之为静态优先级。在静态优先级系统中,诸任务以及它们的时间约束在程序编译时是已知的。相应的,应用程序执行过程中,如果任务的优先级是可变的,则称之为动态优先级。一般的,每一个任务都是一个无限的循环,可以处在如图4.4所示的五种状态之一。1)休眠态(Dormant):是指任务驻留在内存的程序空间中,并未被多任务内核所调度。2)就绪态(Ready):是指任务已经准备好,可以运行,但是由于该任务的优先级比正在运行的任务的优先级低,还是暂时不能运行。3)运行态(Running):是指任务获得了CPU的控制权,正在运行中。基于优先级调度的实时内核总是让处于就绪状态的优先级最高的任务运行。4)挂起态(Pending):也叫做等待事件态(Waiting),是指任务在等待某一事件的发生(如等待某外设的I/O操作、等待定时脉冲的到来、等待超时信号的到来以结束目前的等待,等等)。正在运行的任务由于调用了延时函数或等待某事件发生而将自身挂起,就处于挂起态。105SP...寄存器内容CPU寄存器CPU存储器StatusSPPriority...任务控制块任务1堆栈StatusSPPriority...任务控制块任务2堆栈StatusSPPriority...任务控制块任务n堆栈...图4.3多任务堆栈与CPU寄存器等待或挂起休眠就绪运行中断挂起等待消息收到消息挂起时间到中断中断结束任务调度任务被占先创建任务删除任务图4.4任务状态转换图5)被中断态(Interrupt):是指发生中断时,CPU提供相应的中断服务,原来正在运行的任务暂时不能运行,而进入了被中断状态。4.1.4任务切换任务切换(ContextSwitch)是指CPU寄存器内容切换。当多任务内核决定运行另外的任务时,它保存正在运行的任务和当前状态,即当前CPU寄存器中的全部内容;内核将这些内容保存在该任务的当前状态保存区,也就是该任务自己的栈区之中(这个过程称为“入栈”)。入栈工作完成后,把将要运106行的任务的当前状态从该任务的栈区中装入CPU寄存器(这个过程称为“出栈”),并开始这个任务的运行。这样,就完成了一次任务切换。任务切换过程增加了应用程序的额外负荷,CPU的内部寄存器越多,额外负荷就越重。任务切换所需要的时间取决于CPU有多少寄存器要入栈。4.1.5内核多任务系统中,内核负责管理各个任务,或者说为每个任务分配CPU空间,并且负责任务之间的通信。内核提供的基本服务是任务切换。之所以使用实时内核可以大大简化应用系统的设计,是因为实时内核允许将应用分成若干个任务,由实时内核来管理它们。内核本身也增加了应用程序的额外负荷,代码空间增加ROM的用量,内核本身的数据结构增加了RAM的用量。但更主要的是,每个任务要有自己的栈空间,这一块占用内存是是相当厉害的。内核本身对CPU的占用时间一般在2到5个百分点之间。单片机一般不能运行实时内核,因为单片机的RAM很有限。通过提供必不可缺少的系统服务,诸如信号量管理、邮箱、消息队列、延时等,实时内核使得CPU的利用更为有效。一般情况下,用户用实时内核设计过系统,将决不再想返回到前后台系统设计方式。4.1.6信号量信号量用于实现任务与任务之间、任务与中断处理程序之间的同步与互斥。根据用途,信号量一般分为三种:用于解决互斥问题的互斥信号量、用于解决同步问题的二值信号量和用于解决资源计数问题的计数信号量。其中互斥信号量比较特殊,可能会引起优先级反转问题。信号量可以让一定数量的任务同时访问共享资源。只有当所有希望使用某个资源的任务都使用同一个信号量时,信号量才能起到保护资源的作用,如果某个任务不使用该信号量而直接访问资源,那么信号量就不能保护资源。通常,信号量被设置成只允许最多一个任务在给定的时间内访问某个资源,这就是所谓的以二值模式使用信号量。在这种模式下,信号量的计数不是0就是1,这在互斥或同步共享数据的访问时是很有用的。用信号量保护的代码区有时称为“临界区”。当用做互斥时,信号量被初始化成1,表明目前没有任务进入“临界区”,但最多只有一个任务可以进入“临界区”。因此第一个试图进入“临界区”的任务将成功获得信号量,而所有其它的任务就必须等待。但目前在“临界区”中的任务离开“临界区”时,将释放信号量并允许第一个正在等待的任务进入“临界区”。这种二值的信号量称为互斥信号量。当二值信号量用于同步时,被初始化成0,表示同步的事件尚未发生。一个任务申请信号量以等待该同步事件的发生。另一个任务到达同步点时,释放信号量(将其值设置为1)表示同步事件已发生,以唤醒等待的任务。计数模式的信号量最常用的情况就是控制对多个共享资源的使用,这样的一个信号量允许许多个任务同时访问同一种资源的多个实例,因此信号量被初始化为n(非负整数)。n为该种共享资源的数目。4.1.7任务调度调度(Dispatcher),是内核的主要职责之一,就是决定该轮到哪个任务运行了。多数实时内核是基于优先级调度法的。每个任务根据其重要程度被赋予一定的优先级。基于优先级的调度法则,CPU总是让处在就绪态的优先级最高的任务先运行。然而,究竟何时让高优先级任务掌握CPU的使用权,有两种不同的情况,这要看用的是什么类型的内核,是非占先式的还是占先式的内核。1.非占先式内核非占先式(Non-Preemptive)内核要求每个任务自我放弃CPU的所有权。非占先式调度法也称作合作型多任务(CooperativeMultitasking),各个任务彼此合作共享一个CPU。异步事件还是由中断服务来处理。中断服务可以使一个高优先级的任务由挂起状态变为就绪状态。但中断服务以后控制权还是回107到原来被中断了的那个任务,直到该任务主动放弃CPU的使用权时,那个高优先级的任务才能获得CPU的使用权。非占先式内核的一个优点是响应中断快。在任务级,非占先式内核允许使用不可重入函数。每个任务都可以调用非可重入性函数,而不必担心其他任务可能正在使用该函数,从而造成数据的破坏。因为每个任务要运行到完成时才释放CPU的控制权。当然该不可重入型函数本身不得有放弃CPU控制权的企图。使用非占先式内核时,任务级响应时间比前后台系统快得多。此时的任务级响应时间取决于最长的任务执行时间。非占先式内核的另一个优点是,几乎不需要使用信号量保护共享数据。运行着的任务占有CPU,而不必担心被别的任务抢占。但这也不是绝对的,在某种情况下,信号量还是用得着的。处理共享I/O设备时仍需要使用互斥型信号量。例如,在打印机的使用上,仍需要满足互斥条件。图4.5示意非占先式内核的运行情况,任务在运行过程之中,[图4.5(1)]中断来了,如果此时中断是开着的,CPU由中断向量[图4.5(2)]进入中断服务子程序,中断服务子程序做事件处理[图4.5(3)],使一个有更高级的任务进入就绪态。中断服务完成以后,中断返回指令[图4.5(4)],使CPU回到原来被中断的任务,接着执行该任务的代码[图4.5(5)]直到该任务完成,调用一个内核服务函数以释放CPU控制权,由内核将控制权交给那个优先级更高的,并已进入就绪态的任务[图4.5(6)],这个优先级更高的任务才开始处理中断服务程序标识的事件[图4.5(7)]。(2)(4)ISR(3)(1)(5)(6)低优先级任务中断服务程序使高优先级任务就绪高优先级任务时间(7)低优先级服务释放CPU使用权图4.5非占先式内核非占先式内核的最大缺陷在于其响应时间。高优先级的任务已经进入就绪态,但还不能运行,也许要等很长时间,直到当前运行着的任务释放CPU。与前后系统一样,非占先式内核的任务级响应时间是不确定的,不知道什么时候最高优先级的任务才能拿到CPU的控制权,完全取决于应用程序什么时候释放CPU。总之,非占先式内核允许每个任务运行,直到该任务自愿放弃CPU的控制权。中断可以打断运行着的任务。中断服务完成以后将CPU控制权还给被中断了的任务。任务级响应时间要大大好于前后台系统,但仍是不可知的,商业软件几乎没有非占先式内核。1082.占先式内核当系统响应时间很重要时,要使用占先式(Preemptive)内核。因此,µC/OS-II以及绝大多数商业上销售的实时内核部是占先式内核。最高优先级的任务一旦就绪,总能得到CPU的控