教学课件第5章嵌入式操作系统

上传人:新** 文档编号:568907887 上传时间:2024-07-27 格式:PPT 页数:167 大小:799KB
返回 下载 相关 举报
教学课件第5章嵌入式操作系统_第1页
第1页 / 共167页
教学课件第5章嵌入式操作系统_第2页
第2页 / 共167页
教学课件第5章嵌入式操作系统_第3页
第3页 / 共167页
教学课件第5章嵌入式操作系统_第4页
第4页 / 共167页
教学课件第5章嵌入式操作系统_第5页
第5页 / 共167页
点击查看更多>>
资源描述

《教学课件第5章嵌入式操作系统》由会员分享,可在线阅读,更多相关《教学课件第5章嵌入式操作系统(167页珍藏版)》请在金锄头文库上搜索。

1、第5章嵌入式操作系统 第5章嵌入式操作系统 5.1 引言引言5.2 嵌入式操作系统概述嵌入式操作系统概述5.3 操作系统的基本概念操作系统的基本概念5.4 C/OS-简介简介5.5 C/OS-内核结构内核结构5.6 C/OS-在在ARM上的移植上的移植5.7 基于基于C/OS-构建的构建的TCP/IP/PPP协议栈协议栈思考与练习题思考与练习题 第5章嵌入式操作系统 5.1 引引 言言 嵌入式系统采用的操作系统一般是实时操作系统(Real Time Operating System,RTOS),它是嵌入式应用软件的基础和开发平台。RTOS一般是一段嵌入在目标代码中的软件,用户的其他应用程序都建

2、立在它的基础上。 RTOS的引入,解决了嵌入式软件开发标准化的难题。随着嵌入式系统中软件比重不断上升、应用程序越来越大,对开发人员、应用程序接口、程序档案的组织管理成为一个大的课题。引入RTOS相当于引入了一种新的管理模式,对于开发单位和开发人员都是一个提高。基于RTOS开发出的程序,具有较高的可移植性,可以实现90%以上的设备独立,一些成熟的通用程序还可以作为专用库函数产品推向社会。 第5章嵌入式操作系统 嵌入式软件的函数化、产品化能够促进嵌入式软件模块的复用性,从而降低系统的研发成本。C/OS-是一个可裁减的、源代码公开的嵌入式实时操作系统。它提供任务调度、任务间的通信与同步、任务管理、时

3、间管理和内存管理等基本功能,现在已经应用于多个领域,非常适合实时操作系统的教学。 第5章嵌入式操作系统 5.2 嵌入式操作系统概述嵌入式操作系统概述 5.2.1 5.2.1 嵌入式实时操作系统嵌入式实时操作系统一般的,嵌入式操作系统是指支持嵌入式系统工作的操作系统,它在知识体系和技术结构上与通用操作系统没有太大区别。通用操作系统只注重平均性能,如对于整个系统来说,所有任务的平均响应时间是关键,并不关心单个任务的响应时间;而实时系统强调的是实时性,即系统的正确性不仅依赖于计算结果,也依赖于结果产生的时间。因此,实时系统是指一个能够在指定的或者确定的时间内,实现系统功能和对外部或内部、同步或异步事

4、件作出响应的系统。图5-1形象地体现了两者之间的关系。 第5章嵌入式操作系统 图5-1 实时操作系统与嵌入式操作系统的关系 第5章嵌入式操作系统 嵌入式实时操作系统是嵌入在系统目标代码中的软件,并在系统启动之后运行。用户的其他应用程序是运行在这个软件平台基础之上的多个任务。实时操作系统根据各个任务的要求,进行资源管理、任务调度、中断响应等。并且,在嵌入式实时操作系统中,每个任务根据重要性不同具有不同的优先级,系统根据各个任务的优先级来动态地切换各个任务,从而保证对实时性的要求。因此,嵌入式实时操作系统可以理解为一个标准内核,它将CPU时间、中断、定时器等资源都封装起来,留给用户标准的API接口

5、。在这个基础上,用户通过使用这些内核提供的API函数进行程序开发,最终完成各个任务的协调工作。 第5章嵌入式操作系统 5.2.2 典型的嵌入式操作系统典型的嵌入式操作系统 1 1VxWorksVxWorksVxWorks操作系统是美国WindRiver公司于1993年设计开发的一种嵌入式实时操作系统。VxWorks拥有良好的持续发展能力、高性能的内核以及友好的用户开发环境,是目前嵌入式系统领域中使用最广泛、市场占有率最高的实时操作系统。VxWorks支持多种处理器,如x86、i960、Sun Sparc、Power PC、Motorola MC68xxx、MIPS RX000、Strong A

6、RM等。VxWorks操作系统基于微内核结构,由400多个相对独立、短小精悍的目标模块组成,用户可以根据需要增加或删减适当模块来裁剪和配置系统,其链接器可按应用的需要来动态链接目标模块。大多数的VxWorks API是专有的,采用GNU的编译和调试器。 第5章嵌入式操作系统 VxWorks以其良好的可靠性和卓越的实时性被广泛地应用在通信、军事、航空、航天等高精尖技术及实时性要求极高的领域中,如通信卫星、军事演习、导弹制导和飞机导航等。尤其在美国的F-16、FA-18战斗机,B-12隐形轰炸机和爱国者导弹上,甚至美国JPL实验室研制的著名“索杰纳”火星探测器上也使用了VxWorks。 第5章嵌入

7、式操作系统 2 2Windows CEWindows CE Microsoft Windows CE是从整体上为有限资源的平台而设计的多线程、完整优先权、多任务的嵌入式操作系统。Windows CE主要针对小容量、移动式、智能化、32位、连接设备的模块化实时应用。高度模块化使得Windows CE能够对掌上设备、无线设备、专用工业控制器的用户电子设备等进行定制,并使得Windows CE能在多种处理器体系结构上运行,尤其适用于那些对内存占用空间具有一定限制的设备。Windows CE操作系统的基本内核至少需要200 KB的ROM。它能够支持Win32 API子集、多种用户界面硬件、多种串行和网

8、络通信技术、COM/OLE和其他进程间通信的先进方法。而且,Microsoft公司为Windows CE提供了Platform Builder和Embedded Visual Studio开发工具。 第5章嵌入式操作系统 Windows CE有5个主要的模块:(1) 内核模块:支持进程和线程处理及内存管理等基本服务。(2) 内核系统调用接口模块:允许应用软件访问操作系统提供的服务。(3) 文件系统模块:支持DOS等格式的文件系统。(4) 图形窗口和事件子系统模块:控制图形显示并提供Windows GUI界面。(5) 通信模块:允许与其他设备进行信息交换。 第5章嵌入式操作系统 需要指出的是,W

9、indows CE嵌入式操作系统不是一个硬实时操作系统,但它最大的优点是能够提供与PC机类似的图形用户界面和主要的应用程序。它的界面内容大多是在Windows里出现的标准部件,包括桌面、任务栏、窗口、图标和控件等。因此,只要是对PC机上的Windows比较熟悉的用户,都能很快学会使用基于Windows CE嵌入式操作系统的嵌入式设备。 第5章嵌入式操作系统 3 3pSOSpSOSpSOS是ISI(Intergrated Systems Inc.)公司研发的产品。ISI最早成立于1980年,pSOS在其成立后不久即被推出,是世界上最早的实时操作系统之一,也是最早进入中国市场的实时操作系统。ISI

10、公司于2000年被WindRiver公司兼并。 第5章嵌入式操作系统 pSOS是一个模块化、高性能、完全可扩展的实时操作系统,专为嵌入式微处理器设计,提供了一个完全的多任务环境,在定制的或是商业化的硬件上提供高性能和高可靠性,可以让开发者根据操作系统的功能和内存需求定制每一个应用所需的子系统。pSOS包含单处理器支持模块(pSOS)、多处理器支持模块(pSOSm)、文件管理器支持模块(PHILE)、TCP/IP通信包(PNA)、流式通信模块(OPEN)、图形界面、Java、HTTP等。开发者可以利用它来实现从简单的单个独立设备到复杂的、网络化的多处理器系统。 第5章嵌入式操作系统 4 4QNX

11、QNXQNX是加拿大QNX公司的产品。大多数RTOS都是从68 K的CPU上开发成熟,然后再移植到x86体系上的。而QNX是直接在x86体系上开发出来的,只是近几年才在68 K等CPU上使用。QNX是一个实时、可扩充的操作系统。它部分遵循POSIX相关标准,如POSIX.1b实时扩展。QNX提供了一个很小的微内核以及一些可选的配合进程;其内核仅提供4种服务:进程调度、进程间通信、底层网络通信和中断处理;其进程在独立的地址空间运行;所有其他的操作系统服务都实现为协作的用户进程。因此,QNX内核非常小巧(QNX4.x约为12 KB),而且运行速度极快。QNX灵活的结构可以使用户根据实际的需求,将系

12、统配置成微小的嵌入式操作系统或是包括几百个处理器的超级虚拟机操作系统。 第5章嵌入式操作系统 5 5Palm OS Palm OS 3COM公司的Palm OS在掌上电脑和PDA市场上占有很大的市场份额。Palm OS有开放的操作系统应用程序接口(API),开发商可以根据需要自行开发所需的应用程序。目前共有3500多个应用程序可以运行在Palm Pilot(Palm OS平台)上,其中大部分应用程序为其他厂商和个人开发,从而使Palm Pilot的功能得以不断增多。这些应用软件包括计算器、各种游戏、电子宠物、地理信息等。在开发环境方面,可以在Windows 95/98以上及Macintosh上

13、安装Palm Pilot Desktop。此外,Palm Pilot可以与流行的PC平台上的应用程序(如Word、Excel等)进行数据交换。 第5章嵌入式操作系统 6 6嵌入式嵌入式LinuxLinuxLinux是一种免费的、源代码完全开放的、符合POSIX标准规范的操作系统。随着Linux的迅速发展,嵌入式Linux现在已经有许多版本,包括硬实时的嵌入式Linux(如新墨西哥工学院的RT-Linux堪萨斯大学的KURT-Linux)和一般的嵌入式Linux版本(如(CLinux、PocketLinux等)。其中,RT-Linux通过把Linux任务优先级设为最低,而所有实时任务的优先级都高

14、于它,最终达到既兼容通常的Linux任务又保证强实时性能的目的。 第5章嵌入式操作系统 另外一种常用的嵌入式Linux(CLinux是针对没有MMU的处理器而开发的,已被广泛使用在ColdFire、ARM、MIPS、SPARC、SuperH等没有MMU的微处理器上。虽然(CLinux的内核比Linux2.0内核小得多,但它保留了Linux操作系统稳定性好、网络能力优异以及对文件系统的支持等主要优点。(CLinux与标准Linux最大的区别在于内存管理。标准Linux是针对有MMU的处理器而设计的,它将虚拟地址送到MMU,然后把虚拟地址映射为物理地址;通过赋予每个任务不同的虚拟物理地址转换映射,

15、支持不同任务之间的保护。第5章嵌入式操作系统 而(CLinux是针对没有MMU的处理器,不能使用处理器的虚拟内存管理技术,它对内存的访问是直接的,即它对地址的访问不需要经过MMU,而是直接送到地址线上输出;所有程序中访问的地址都是实际的物理地址;对内存空间不提供保护,各个进程实际上共享一个运行空间。在实现上,(CLinux专为嵌入式系统做了许多小型化的工作。 第5章嵌入式操作系统 7. (C/OS-7. (C/OS-(C/OS-是一个完整的,源码公开的,可移植、固化、裁剪的占先式实时多任务内核,主要面向中小型嵌入式系统,具有执行效率高、占用空间小、可移植性强、实时性能优良和可扩展性强等特点。(

16、C/OS-结构小巧,最小内核可编译至2 KB(虽然这样的内核没有太大的实用性),即使包含全部功能,编译后也仅有610 KB,因而非常适用于小型控制系统。(C/OS-具有良好的兼容性,如系统本身不支持文件系统,但是如果需要,也可以自行加入文件系统的内容。此外,(C/OS-是用ANSI的C语言编写的,包含一小部分汇编语言代码,使之可供不同架构的微处理器使用。至今,从8位到16位,(C/OS-已在超过49种不同架构的微处理器上成功移植。 第5章嵌入式操作系统 (C/OS-是基于实时内核(C/OS的,和(C/OS版本V1.11(C/OS的最终版)是向上兼容的。目前,世界上已有很多人在各个领域中使用(C

17、/OS及(C/OS-,这些领域包括:照相机行业(如数码相机)、航空业、高端音响、医疗器械、电子乐器、发动机控制、网络设备、高速公路电话系统、自动提款及工业机器人等。更因为(C/OS-完全公开源代码,所以国内外很多高等院校都将其用于实时系统教学。 第5章嵌入式操作系统 8国内著名的嵌入式实时操作系统国内著名的嵌入式实时操作系统 (1) Delta OS:Delta OS是全中文的嵌入式实时操作系统,提供强实时和嵌入式多任务的内核。Delta OS的特点是任务响应时间快速、确定,不随任务负载大小改变;绝大部分的代码由C语言编写,具有很好的移植性。它适用于内在要求较大、可靠性要求较高的嵌入式系统。D

18、elta OS主要包括:嵌入式实时内核DeltaCORE、嵌入式TCP/IP组件DeltaNET、嵌入式文件系统DeltaFILE以及嵌入式图形用户接口DeltaGUI等。同时,它还提供了一整套的嵌入式开发套件LamdaTOOL。Delta OS是国内嵌入式领域不可多得的一整套嵌入式开发应用解决方案,已成功应用于通信、网络、信息家电等多个领域。 第5章嵌入式操作系统 (2) Hopen OS:Hopen OS由一个体积很小的内核以及一些可以根据需要自行定制的系统模块组成。其核心Hopen Kernel的规模一般为10 KB左右,占用空间小,并具有实时、多任务、多线程的系统特征。(3) HBOS

19、:HBOS系统是浙江大学自主研制开发的全中文实时操作系统。它具有实时、多任务等特征,能提供浏览器、网络通信核图形窗口等服务,还可供进行一定的定制或二次开发,并能为应用软件开发提供API接口支持。HBOS系统可用于信息家电、智能设备和仪器仪表等领域开发应用。在HBOS系统平台下,已经成功地开发出机顶盒和数据采集等系统。 第5章嵌入式操作系统 5.3 操作系统的基本概念操作系统的基本概念 5.3.1 5.3.1 多进程和多线程多进程和多线程许多嵌入式系统并不是单纯地完成一种功能。例如,在一个电话应答机系统中,需要把记录通话信息和操作用户控制面板定义为不同的任务,因为它们不仅在逻辑上进行的是不同的操

20、作,而且完成的速度也不同。这些不同的任务构成了应答机系统功能的各个部分,为了完成多个任务而组织程序结构的需要,引入了进程的概念。 第5章嵌入式操作系统 一个进程可以简单地认为是一个程序的唯一执行。进程是顺序执行的,而且CPU一次只能执行一个进程。但是,当确定了一个进程的完整状态后,就可以强制CPU停止执行当前进程而执行另一个进程。通过改变CPU中的程序计数器,使其指向新进程的代码,同时将新进程的数据移入寄存器和主存中,就可以实现进程的切换。这样,就能够使多个进程同时存在于CPU中。在嵌入式系统中,一个进程的常用形式是线程。线程在CPU的寄存器中有各自不同的值集合,但是共存于一个主存储空间中。线

21、程普遍应用于嵌入式系统中(即任务),这样可以避免存储管理单元的复杂,节约存储管理单元的消耗。 第5章嵌入式操作系统 5.3.2 5.3.2 任务任务在嵌入式系统中,一个任务也称作一个线程,是一个程序,该程序在运行时可以认为CPU完全只属于该程序自己。在实时应用程序的设计过程中,要考虑如何将应用功能合理地划分为多个任务,让每个任务完成一定的功能,成为整个应用的一部分。每个任务都被赋予一定的优先级,有自己的一套CPU寄存器和栈空间(如图5-2所示)。 第5章嵌入式操作系统 图5-2 多任务堆栈与CPU寄存器 第5章嵌入式操作系统 一般的,每一个任务都是一个无限的循环,可以处在以下五种状态之一:(1

22、) 休眠态(Dormant):是指任务驻留在内存的程序空间中,并未被多任务内核所调度。(2) 就绪态(Ready):是指任务已经准备好,可以运行,但是由于该任务的优先级比正在运行的任务的优先级低,还暂时不能运行。(3) 运行态(Running):是指任务获得了CPU的控制权,正在运行中。基于优先级调度的实时内核总是让处于就绪态的优先级最高的任务运行。 第5章嵌入式操作系统 (4) 挂起态(Pending):也叫作等待事件态(waiting),是指任务在等待某一事件的发生(如等待某外设的I/O操作、等待定时脉冲的到来、等待超时信号的到来以结束目前的等待,等等)。正在运行的任务由于调用了延时函数或

23、等待某事件发生而将自身挂起,就处于挂起态。(5) 被中断态(Interrupt):是指发生中断时,CPU提供相应的中断服务,原来正在运行的任务暂不能运行,而进入了被中断状态。 第5章嵌入式操作系统 5.3.3 5.3.3 任务切换任务切换任务切换(Context Switch)是指CPU寄存器内容切换。当多任务内核决定运行另外的任务时,它保存正在运行的任务的当前状态,即当前CPU寄存器中的全部内容;内核将这些内容保存在该任务的当前状态保存区,也就是该任务自己的栈区之中(这个过程称为“入栈”)。入栈工作完成后,把将要运行的任务的当前状态从该任务的栈中装入CPU寄存器(这个过程称为“出栈”),并开

24、始这个任务的运行。这样,就完成了一次任务切换。任务切换过程增加了应用程序的额外负荷,CPU的内部寄存器越多,额外负荷就越重。任务切换所需要的时间取决于CPU有多少寄存器要入栈。 第5章嵌入式操作系统 5.3.4 5.3.4 内核内核多任务系统中,内核负责管理各个任务,为每个任务分配CPU的使用时间,并且负责任务间的通信。内核提供的基本服务是任务切换,通过提供必不可少的系统服务,诸如信号量管理、邮箱、消息队列及时间延时等,使得CPU的利用更为有效。此外,实时内核允许将应用程序划分成若干个任务并对它们进行管理(如任务切换、调度、任务间的同步和通信,等等),因而使用实时内核可以大大简化应用系统的设计

25、。 第5章嵌入式操作系统 但是,内核本身也增加了应用程序的额外负荷,因为内核提供的服务需要一定的执行时间。额外负荷的多少取决于用户调用这类服务的频率。在设计得较好的应用系统中,内核占用2%5%的CPU负荷。再有,内核是加在用户应用程序中的软件,因而会增加ROM(程序代码空间)的用量,而内核本身的数据结构还会增加RAM(数据空间)的用量。更主要的是,每个任务都要有自己的栈空间,这会占用相当多的内存(由任务的数量决定)。单片机一般不能运行实时内核,就是因为单片机的RAM非常有限。 第5章嵌入式操作系统 5.3.5 任务调度任务调度 1 1非占先式内核非占先式内核非占先式内核(non-preempt

26、ive kernel)中各个任务彼此合作,共享CPU。在一个任务的运行过程中,除了中断,不能在该任务未运行完时抢占该任务的CPU控制权。中断服务可使一个高优先级的任务由挂起态变为就绪态,但中断服务以后,CPU的使用权交回给原来被中断了的任务,直到该任务主动释放CPU的控制权,一个新的高优先级的任务才能运行。图5-3表示非占先式内核的运行情况。 第5章嵌入式操作系统 图5-3 非占先式内核 第5章嵌入式操作系统 图5-3中,1:任务在运行过程中被中断。2:若此时中断开着,则CPU进入中断服务子程序(ISR)。3:ISR做事件处理,使一个更高优先级的任务进入就绪态。4:中断服务完成后,使CPU回到

27、原来被中断的任务。5:继续执行该任务。6:直到该任务完成,释放CPU的使用权给其他任务。7:看到有高优先级的任务处于就绪态,内核做任务切换,高优先级的任务才开始处理ISR标志的事件。 第5章嵌入式操作系统 非占先式内核的优点包括:(1) 响应中断快。(2) 可以使用不可重入函数。由于任务运行过程中不会被其他任务抢占,该任务使用的子函数不会被重入,因此不必担心其他任务正在使用该函数而造成数据破坏。(3) 共享数据方便。 第5章嵌入式操作系统 2 2占先式内核占先式内核当系统响应时间很重要时,须使用占先式内核。在占先式内核中,最高优先级的任务一旦就绪,便能得到CPU的使用权。当一个运行着的任务使一

28、个比它优先级高的任务进入就绪态时,当前任务被挂起,那个高优先级的任务立刻得到CPU的使用权开始运行。如果是中断服务子程序使一个高优先级的任务进入就绪态,则当中断完成时,被中断的任务被挂起,优先级高的任务开始运行。占先式内核的执行过程如图5-4所示。 第5章嵌入式操作系统 图5-4 占先式内核 第5章嵌入式操作系统 图5-4中,1:任务在运行过程中被中断。2:若此时中断开着,则CPU进入中断服务子程序(ISR)。3:ISR做事件处理,使一个更高优先级的任务进入就绪态。当ISR完成时,进入内核提供的一种服务(内核提供的一个函数被调用)。4:这个函数识别出有一个高优先级的任务(更重要的任务)进入就绪

29、态,内核做任务切换。5:执行高优先级的任务直到该任务完成,而不再运行原来被中断了的任务。6:内核看到原来的低优先级的任务要运行,进行另一次任务切换。7:被中断了的任务继续运行,直到该任务完成。 第5章嵌入式操作系统 5.3.6 5.3.6 任务间的通信与同步任务间的通信与同步在多任务的实时系统中,一项工作可能需要多个任务或多个任务与多个中断处理程序共同完成。那么,它们之间必须协调工作、互相配合,必要时还要交换信息。实时内核提供了任务间的通信与同步机制以解决这个问题。1 1任务间的通信任务间的通信多任务实时系统中,任务间或中断服务与任务间常常需要交换信息,这种信息传递称为任务间的通信(inter

30、 task communication)。任务间的通信有两个途径:共享数据结构和消息机制。 第5章嵌入式操作系统 1) 共享数据结构实现任务间通信的最简单方法是使用共享数据结构,尤其是多个任务在同一地址空间下的情形。共享数据结构的类型可以是全局变量、指针、缓冲区等。在使用共享数据结构时,必须保证共享数据结构使用的排它性,即保证每个任务或中断服务子程序独享该数据结构。否则,会导致竞争或对数据时效的破坏。因此,在使用共享数据结构时,必须实现存取的互斥机制。实现对共享数据结构操作的互斥常常采用以下方法:开/关中断、禁止任务切换以及信号量(semaphore)机制等。 第5章嵌入式操作系统 (1) 开

31、/关中断。开/关中断实现数据共享保护是指在进行共享数据结构的访问时先进行关中断操作,在访问完成后再开中断。这种方法简单、易实现,是中断服务子程序中共享数据结构的唯一方法。但是,如果关中断的时间太长,则可能影响整个实时系统的中断响应时间和中断延迟时间。 第5章嵌入式操作系统 (2) 禁止任务切换。禁止任务切换是指在进行共享数据的操作前,先禁止任务切换,操作完成后再允许任务切换。这种方式虽然实现了共享数据的互斥,但是实时系统的多任务切换在此时被禁止了,应尽量少使用。需要注意的是,尽管禁止任务切换,但任务进行共享数据操作时,中断服务子程序此时仍然可以抢占CPU的使用权。因此,这种方式只适合任务间的共

32、享数据结构的互斥。 第5章嵌入式操作系统 (3) 信号量。在多任务实时操作系统中,信号量也被广泛用来进行任务间的通信和同步。但是,信号量的使用应该有所节制,不能让所有的互斥处理都使用信号量机制实现,因为信号量机制是有一定系统开销的。对于简单的数据共享,如果处理时间很短,使用开/关中断实现而不需要使用信号量。只有涉及系统消耗比较大的共享数据操作时,才考虑使用信号量,因为如果此时使用开/关中断,就可能会影响系统的中断响应时间。 第5章嵌入式操作系统 2) 消息机制(1) 消息邮箱。消息通常是内存空间的一个数据结构,通常是一个指针型变量。一个任务或一个中断服务子程序通过内核服务,可以把一则消息放到邮

33、箱里去;同样的,一个或多个任务通过内核服务可以接收这则消息。每个邮箱都有相应的正在等待的任务列表。要得到消息的任务如果发现邮箱是空的,就被挂起,并被放入到该邮箱的等待消息的任务列表中,直到接收到消息。通常,内核允许设定等待超时,如果等待时间已到仍没有收到消息,任务就进入就绪态并返回等待超时的出错信息。如果消息放入邮箱中,则内核或者把消息传递给等待消息的任务列表中优先级最高的任务(基于优先级),或者把消息传给最先开始等待消息的任务(基于先进先出)。 第5章嵌入式操作系统 (C/OS-II只支持基于优先级的分配算法,内核一般提供以下邮箱服务: 邮箱内消息内容的初始化; 将消息放入邮箱(POST);

34、 等待消息进入邮箱(PEND); 从邮箱中得到消息。 第5章嵌入式操作系统 (2) 消息队列。消息队列实际上是邮箱阵列,在消息队列中允许存放多个消息。对消息队列的操作和对消息邮箱的操作基本相同。通常,内核中提供的消息队列服务包括: 消息队列初始化; 放一则消息到队列中去(POST); 等待一则消息的到来(PEND); 从队列中等到消息。 第5章嵌入式操作系统 2 2任务间同步任务间同步任务间的同步是指异步环境下的一组并发执行任务因各自的执行结果互为对方的执行条件,因而任务之间需要互发信号,以使各任务按一定的速度执行。任务同步也常常使用信号量。与任务间的通信不同,信号量的使用不再作为一种互斥机制

35、,而是代表某个特定的事件是否发生。任务的同步分为单向同步和多向同步。 第5章嵌入式操作系统 (1) 单向同步。如图5-5所示,图中用一面旗帜或称作一个标志来表示信号量。这个标志表示某一事件的发生(不再是保证互斥条件),用来实现同步机制的信号量初始化为0。这种类型的同步称作单向同步(unilateral rendezvous)。图中,一个任务在等待(PEND)某个事件发生时,查看该事件的信号量是否非0;另一个任务或中断服务子程序在进行操作时,当该事件发生后,将该信号量设置为1;等待该事件的任务查询到信号量的变化,代表该事件已发生,任务得以继续自身的运行。 第5章嵌入式操作系统 图5-5 用信号量

36、使任务与中断服务(或任务)单向同步 第5章嵌入式操作系统 (2) 双向同步。两个任务可以用两个信号量同步它们的行为,如图5-6所示。这种同步称为双向同步(bilateral rendezvous)。双向同步与单向同步类似,但是双向同步不可能在任务与ISR之间实施,因为ISR运行时不可能等待一个信号量。 第5章嵌入式操作系统 图5-6 两个任务用两个信号量双向同步 第5章嵌入式操作系统 5.3.7 5.3.7 操作系统的结构和功能操作系统的结构和功能为了满足嵌入式应用,嵌入式实时操作系统可以根据实际应用环境的要求对内核进行裁剪和重新配置。一般的,实时操作系统总是由以下几个重要部分组成:实时内核、

37、网络组件、文件系统和图形用户接口等,其体系结构如图5-7所示。 第5章嵌入式操作系统 图5-7 嵌入式实时操作系统的体系结构 第5章嵌入式操作系统 5.4.1 C/OS-5.4.1 C/OS-概述概述C/OS-读作“micro C O S 2”,即“微控制器操作系统版本2”。C/OS-是一个免费的、源代码公开的嵌入式实时多任务内核,是专门为嵌入式应用设计的RTOS,提供了实时系统所需的基本功能。C/OS-的全部功能的核心部分代码只占用8.3 KB,用户还可以针对自己的实际系统对C/OS-进行裁剪(最少可达2.7 KB)。C/OS-只提供了诸如任务调度、任务管理、时间管理、内存管理、中断管理和任

38、务间的同步与通信等实时内核的基本功能,没有提供输入/输出管理、文件系统、图形用户接口及网络组件之类的额外服务。但是,由于C/OS-的可移植性和开源性,用户可以根据实际应用添加所需要的服务。 5.4 C/OS-简介简介 第5章嵌入式操作系统 C/OS-是在PC机上开发的,C编译器使用的是Borland C/C+ 3.1版。而PC机是大家最熟悉的开发环境,因此在PC机上学习和使用C/OS-非常方便。此外,C/OS-作为一个源代码公开的嵌入式实时内核,对开发者学习和使用实时操作系统提供了极大的帮助。许多开发者已成功地把C/OS-应用于自己的嵌入式系统中,从而使得C/OS-获得了快速的发展。从最早的C

39、OS,以及后来的C/OS和C/OS- V2.00,到现在的C/OS- V2.52,该内核已经有十余年的发展历史,在诸多领域得到了广泛应用。许多行业中C/OS-成功应用的实例,也进一步说明了该内核的实用性和可靠性。 第5章嵌入式操作系统 5.4.2 C/OS-5.4.2 C/OS-的特点的特点1 1源代码公开源代码公开2可移植性可移植性(portable) 3可固化可固化(ROMable) 4可裁剪可裁剪(scalable) 5占先式占先式(preemptive) 6多任务多任务 7可确定性可确定性 8任务栈任务栈 9系统服务系统服务 10中断管理中断管理 11稳定性与可靠性稳定性与可靠性 第5

40、章嵌入式操作系统 5.4.3 5.4.3 C/OS-C/OS-的软件体系结构的软件体系结构C/OS-的软件体系结构以及与硬件的关系如图5-8所示,其软件体系主要包括以下4个部分:(1) 应用软件层:在应用程序中使用C/OS-时,用户开发设计的应用代码。(2) 与应用相关的配置代码:与应用软件相关的、C/OS-的配置代码。包括两个头文件,这两个头文件分别定义了与应用相关的控制参数和所有相关的头文件。 第5章嵌入式操作系统 (3) 与处理器无关的核心代码:包括与处理器无关的10个源代码文件和1个头文件。其中,10个源代码文件分别实现了C/OS-内核结构,即内核管理、事件管理、消息邮箱管理、内存管理

41、、互斥型信号量管理、消息队列管理、信号量管理、任务管理、定时管理和内核管理。(4) 与处理器相关的设置代码:与处理器相关的源代码,包括1个头文件、1个汇编文件和一个C文件。在不同处理器上移植C/OS-时,需要根据处理器的类型对这部分代码重新编写。可以在 C/OS-的网站www.C/OS-.com中查找移植范例,也可以阅读处理器的移植代码进行编译。 第5章嵌入式操作系统 图5-8 C/OS-软件体系结构 第5章嵌入式操作系统 5.5 C/OS-内核结构内核结构 5.5.1 5.5.1 临界段临界段代码的临界段critical sections)是指处理时不可分割的代码。一旦这部分代码开始执行,就

42、不允许任何中断进入。与其他内核一样,C/OS-为了处理临界段代码,也需要关中断,处理完毕后,再开中断。关中断能够使C/OS-避免有其他任务或中断服务同时进入临界段代码。但是,关中断的时间会影响用户系统对实时事件的响应特性,它是实时内核开发商提供的最重要的指标之一。C/OS-努力使关中断时间降至最短,但在具体使用C/OS-时,关中断的时间很大程度上取决于微处理器的结构以及编译器所生成的代码质量。 第5章嵌入式操作系统 微处理器一般都具有关中断/开中断指令,用户使用的C语言编译器必须具有能够在C中直接实现关中断/开中断操作的机制。有些C编译器允许在用户的C源代码中插入汇编语言的语句,即通过插入微处

43、理器指令来实现关中断/开中断的操作;而有些编译器把从C语言中关中断/开中断的操作放在语言扩展部分,从C语言中直接关中断/开中断。C/OS-通过定义两个宏(macros)OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL(),来实现关中断和开中断的操作,从而避免了不同C编译器厂商选择不同的方法处理关中断和开中断。 第5章嵌入式操作系统 OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()总是成对使用的,它们把临界段代码封装起来,实现对应用程序中的临界段代码的保护,具体用法如以下代码所示。.OS_ENTER_CRITICAL()/* C/OS-II临界

44、段代码 */OS_EXIT_CRITICAL() ;. 第5章嵌入式操作系统 在使用OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()时要特别小心,如果在调用一些如OSTimeDel()之类的功能函数之前关中断会导致应用程序崩溃(死机)。这是因为任务被挂起一段时间直到挂起时间到,但由于中断被关掉了,就无法得到时钟节拍中断服务(即一直处于挂起状态)。显然,所有的挂起类(PEND)调用都有这类问题。作为一条普遍使用的规则是:调用C/OS-II的功能函数时,中断应当总是开着的。 第5章嵌入式操作系统 5.5.2 5.5.2 任务任务在C/OS-II中,任务通常是一个无限的循

45、环。任务就像其他C函数一样,有返回值类型和参数,但它绝不返回任何数据,因此返回参数类型必须定义成void。当任务开始执行时,会给用户代码传递一个形式参数。这个参数是一个指向void的指针,以允许用户应用程序向该任务传递任何类型的参数。任务的函数结构必须是以下两种形式之一: 第5章嵌入式操作系统 (1) 执行无限循环的任务。void YourTask(void *pdata)任务初始化代码;for( ; ;) 用户代码;/*调用C/OS-II的功能函数,如下列函数之一 */OSMboxPend() ;OSQPend() ;OSSemPend() ; OSFlagPend() ; OSTaskSu

46、spend(OS_PRIO_SELF) ; 第5章嵌入式操作系统 OSTimeDly() ;OSTimeDlyHMSM() ;.用户代码; 第5章嵌入式操作系统 (2) 执行一次后自我删除的任务。void YourTask(void *pdata)用户代码;OSTaskDel(OS_PRIO_SELF) ; 第5章嵌入式操作系统 对于后一种执行自我删除的任务来说,任务代码并非真的删除了,而是C/OS-简单地不再理会这个任务了。这个任务的代码也不会再运行,而且也绝不会返回。 C/OS-可以管理多达64个任务,但建议保留4个最高优先级和4个最低优先级的任务,供以后C/OS-的版本使用。目前C/OS

47、-使用了2个优先级别:OS_LOWEST_PRIO(空闲任务)和OS_LOWEST_PRIO-1(统计任务)。必须给每个任务赋予不同的优先级,优先级号可以为0OS_LOWEST_PRIO-2。优先级号越低,任务的优先级越高。 第5章嵌入式操作系统 5.5.3 5.5.3 任务控制块任务控制块OS_TCBOS_TCBC/OS-对任务的管理是通过任务控制块TCB(Task Control Blocks)进行的。任务控制块是一个数据结构(OS_TCB),全部存放在RAM中。在创建任务时,该任务的TCB被赋值;当任务的CPU使用权被剥夺时,C/OS-用OS_TCB来保存该任务的状态(即当前CPU寄存器

48、的值);当任务再次被调度,重新得到CPU使用权时,能够从任务控制块中恢复该任务的执行状态,确保任务从当时被中断的那一点继续执行。OS_TCB的结构如下所示: 第5章嵌入式操作系统 typedef struct os_tcb OS_STK *OSTCBStkPtr;/* 指向当前任务堆栈栈顶的指针 */#if OS_TASK_CREAT_EXT_EN /* OS_TASK_CREAT_EXT_EN为1时下列数据有效 */void *OSTCBExtPtr;/* 指向用户定义的任务控制块扩展 */OS_STK *OSTCBStkBottom;/* 指向任务堆栈栈底的指针 */INT32U OSTC

49、BStkSize; /* 堆栈中可容纳的指针元数目 */INT16U OSTCBOpt;/* 指向任务堆栈栈底的指针 */ INT16U OSTCBId;/* 存储任务的识别码 */#endif struct os_tcb *OSTCBNext;/* 指向任务OS_TCB双向链表中后一个元素 */ struct os_tcb *OSTCBPrev;/* 指向任务OS_TCB双向链表中前一个元素 */ 第5章嵌入式操作系统 #if (OS_Q_EN0)&(OS_MBOX_QS0)|(OS_MBOX_EN0)|(OS_SEM_EN0)|(OS_MUTEX_EN0) OS_EVENT *OSTCBE

50、ventPtr; /* 指向事件控制块的指针 */#endif#if (OS_Q_EN0)&(OS_MBOX_QS0)|(OS_MBOX_EN0) void *OSTCBMsg; /* 指向传递给任务的消息的指针*/#endif#if OS_TASK_DEL_EN0 OS_FLAG_NODE*OSTCBFlagNode;/* 指向事件标志节点的指针*/#endif 第5章嵌入式操作系统 INT16U OSTCBDly; /* 用于设置任务延时或等待的最多时钟节拍数 */INT8U OSTCBStat; /* 任务的状态字 */INT8U OSTCBPrio; /* 任务的优先级 */INT8U

51、 OSTCBX, OSTCBY, OSTCBBitX, OSTCBBitY;/*加速任务进入就绪态的过程*/ OS_TCB 第5章嵌入式操作系统 5.5.4 5.5.4 任务调度任务调度 C/OS-是占先式实时内核,优先级最高的任务一旦进入就绪态,立即拥有CPU的控制权并开始运行。C/OS-的调度器(scheduler)就是用来查找准备就绪的优先级最高的任务并进行任务切换。任务级的调度是由OSSched()函数完成的,中断级的调度是由OSIntExt()函数完成的。在进行 C/OS-任务调度(task scheduling)时,首先调用OSSched()函数,它先判断要进行任务切换的条件,如果

52、条件允许进行任务调度,则调用OS_TASK_SW()。OS_TASK_SW()是宏调用,用来实现任务切换,它先将当前任务的CPU寄存器的值保存到该任务的堆栈中,然后获得最高优先级任务的堆栈指针,并从中恢复该任务的CPU寄存器的值,使之继续执行,这时就完成了一次任务切换。 第5章嵌入式操作系统 5.5.5 5.5.5 任务管理任务管理C/OS-提供大量的API函数实现对任务的管理,图5-9所示是C/OS-控制下的任务状态转换图。 第5章嵌入式操作系统 图5-9 任务的状态转换 第5章嵌入式操作系统 1 1建立任务建立任务(OSTaskCreat()(OSTaskCreat()和和OSTaskCr

53、eatExt()OSTaskCreatExt() C/OS-要管理用户的任务,就必须先建立任务。通过将任务的地址和其他参数传递给以下两个函数之一来建立任务:OSTaskCreat()和OSTaskCreatExt()。其中,OSTaskCreat()与C/OS向下兼容;OSTaskCreatExt()是OSTaskCreat()的扩展,提供一些附加的功能。用这两个函数中的任何一个都可以建立任务。任务可以在多任务调度开始前建立,也可以在其他任务的执行过程中建立。但是,在main()函数内开始多任务调度(OSStart()前,必须至少建立一个任务,而且任务不能由中断服务程序(ISR)建立。 第5章

54、嵌入式操作系统 在调用了任务建立函数后,C/OS-内核会首先从TCB空闲列表内申请一个空的TCB指针;然后根据用户给出的参数初始化任务堆栈,并在内部的任务就绪表中标记该任务为就绪状态;最后返回。这样就创建了一个任务。 第5章嵌入式操作系统 2 2任务堆栈任务堆栈在C/OS-中,每个任务都有自己的堆栈空间。堆栈必须声明为OS_STK类型,并且由连续的内存空间组成。可以静态分配堆栈空间(在编译时分配),也可以动态分配堆栈空间(在运行时分配),这两种声明方式都应放置在函数外面。任务所需堆栈的容量由应用程序确定。在确定堆栈容量时,必须考虑到任务调用的所有函数的嵌套情况、任务调用的所有函数为局部变量分配

55、的所有内存的数目,以及所有可能的中断服务子程序嵌套对堆栈的需求。此外,堆栈必须能够保存CPU所有的寄存器。 第5章嵌入式操作系统 C/OS-II提供了堆栈检验函数OSTaskStkChk(),用来确定任务实际需要的堆栈空间的大小。这样能够避免为任务分配过多的堆栈空间,从而减少应用程序代码所需的RAM(内存)数量。调用堆栈检验函数后,所得到的只是一个大致的堆栈使用情况,并不能说明堆栈使用的全部实际情况。为了适应系统以后的升级和扩展,应该多分配10%100%的堆栈空间。 第5章嵌入式操作系统 3 3删除任务删除任务(OSTaskDel()(OSTaskDel()删除任务是指任务将返回并处于休眠状态

56、,任务的代码不再被C/OS-II调用,而并不是说任务的代码被删除了。通过调用OSTaskDel()可以完成删除任务的功能。调用OSTaskDel()后,先进行条件判断,当所有的条件都满足后,就会从所有可能的C/OS-II的数据结构中去除任务的任务控制块OS_TCB,这样就不会被其他的任务或中断服务子程序置于就绪态,即任务被置于休眠状态。 第5章嵌入式操作系统 4 4 挂挂 起起 任任 务务 与与 恢恢 复复 任任 务务 (OSTaskSuspend()(OSTaskSuspend()和和 OSTaskResume()OSTaskResume()通过调用OSTaskSuspend()函数可以将指

57、定的任务挂起。 任务可以挂起自己或其他任务。但是,如果任务在被挂起的同时还在等待延迟时间到,则需要对任务取消挂起操作,并且要继续等待延迟时间到,任务才能转入就绪态。在C/OS-中,只能通过调用OSTaskResume()函数才能恢复被挂起的任务。OSTaskResume()用于将指定的已经挂起的任务恢复为就绪态。但是,该函数并不要求和OSTaskSuspend()函数成对使用。 第5章嵌入式操作系统 5.5.6 5.5.6 中断服务中断服务中断是一种硬件机制,用于通知CPU“有一个异步事件发生了”。中断一旦被识别,CPU保存部分或全部寄存器的值,跳转到专门的子程序,称为中断服务子程序(ISR)

58、。在C/OS-中,中断服务子程序要用汇编语言编写。如果用户使用的C语言编译器支持内嵌汇编,则可以直接将中断服务子程序代码放在C语言的程序文件中。中断服务子程序的结构如下所示:用户中断服务子程序: 第5章嵌入式操作系统 保存全部CPU寄存器;调用OSIntEnter()或OSIntNesting 直接加1;执行用户代码做中断服务;调用OSIntExit();恢复所有CPU寄存器;执行中断返回指令; 第5章嵌入式操作系统 在C/OS-中,中断服务使正在执行的任务暂时挂起,并在进入中断前将任务执行现场保存到任务堆栈中;然后,中断服务子程序进行事件处理;当处理完成后中断返回时,程序让进入就绪态的优先级

59、最高的任务在中断嵌套全部退出后立即运行。C/OS-的中断服务过程如图5-10所示。 第5章嵌入式操作系统 图5-10 C/OS-的中断服务 第5章嵌入式操作系统 5.5.7 5.5.7 时钟节拍与时间管理时钟节拍与时间管理时钟节拍(clock tick)是特定的周期性中断。这个中断就像是系统心脏的脉动,而中断之间的时间间隔取决于不同的应用,一般为10200 ms。时钟的节拍式中断使得内核可以将任务延时若干个整数时钟节拍,以及当任务等待事件发生时,提供等待超时。时钟节拍率越快,系统的额外开销就越大。C/OS-的节拍率为10100 Hz,时钟节拍的实际频率取决于应用程序的精度。时钟节拍源可以是专门

60、的硬件定时器,也可以是来自交流电源的信号。 第5章嵌入式操作系统 1 1任务延时函数任务延时函数(OSTimeDly()(OSTimeDly()函数OSTimeDly()被任务调用后会将任务延时一段特定的时间,这段时间的长短由指定的时钟节拍的数目确定。当任务调用OSTimeDly()后,一旦规定的时间到或者其他任务通过调用OSTimeDlyResume()取消了延时,该任务就会立即进入就绪态。需要注意的是,调用该函数会使C/OS-进行一次任务调度,从而执行优先级最高的就绪态任务。只有当该任务在所有就绪任务中的优先级最高时,它才会立即运行。 第5章嵌入式操作系统 2 2按时、分、秒、毫秒延时函数

61、按时、分、秒、毫秒延时函数(OSTimeDlyHMSM()(OSTimeDlyHMSM()前面提到,在使用OSTimeDly()时,应用程序必须知道延时时间对应的时钟节拍数目。如果使用OSTimeDlyHMSM()函数,就可以按时(h)、分(min)、秒(s)、毫秒(ms)来方便地定义时间。与OSTimeDly()一样,调用OSTimeDlyHMSM()函数也会使C/OS-进行一次任务调度,并且执行下一个优先级最高的就绪态任务。在实际的应用中,应避免使任务延时过长的时间,因为从任务中获得一些反馈行为(如减少计数器、清除LED等)经常是很有意义的;但是,如果确实需要长时间的延时,C /OS-可以

62、将任务延时长达256 h(接近11天)。 第5章嵌入式操作系统 3 3恢复延时任务函数恢复延时任务函数(OSTimeDlyResume()(OSTimeDlyResume()C/OS-允许处于延时期的任务不等待延时期满,而通过其他任务取消延时时间使自己处于就绪态。通过指定要恢复的任务的优先级来调用OSTimeDlyResume()函数就可以实现这一功能。此外,OSTimeDlyResume()函数也可以唤醒正在等待事件的任务。在这种情况下,等待事件发生的任务会把它当作等待超时处理,从而终止等待事件。 第5章嵌入式操作系统 4 4两个系统时间函数两个系统时间函数(OSTimeGet()(OSTi

63、meGet()和和OSTimeSet()OSTimeSet()无论时钟节拍何时发生,C/OS-都会将一个32位的计数器加1。这个计数器在调用OSStart()初始化多任务时,被置为0;并且在4 294 967 295个节拍执行完一遍时,会重新从0开始计数。可以通过调用OSTimeGet()函数,获得该计数器的当前值;通过调用OSTimeSet(),改变该计数器的值。 第5章嵌入式操作系统 5.5.8 5.5.8 任务间同步与通信的管理任务间同步与通信的管理和其他多任务操作系统一样,C/OS-通过向任务发信号来使不同的任务相互同步或相互通信。在C/OS-中,任务或中断服务子程序使用事件控制块EC

64、B(Event Control Block)来向其他任务发信号。而这里的信号是指某个事件,如信号量、邮箱或消息队列。C/OS-提供一系列的功能函数来实现对这些信号的操作与管理。 第5章嵌入式操作系统 1 1事件控制块事件控制块ECBECB事件控制块ECB是用于实现信号量管理、消息邮箱管理及消息队列管理等功能函数的基本数据结构。C/OS-通过定义OS_EVENT数据结构来确定一个事件控制块ECB的所有信息:Typedef struct INT8U OSEventType; /* 事件类型 */INT8U OSEventGrp; /* 等待任务所在的组 */INT16U OSEventCnt; /

65、* 计数器(当事件是信号量时) */void*OSEventPtr;/* 指向消息或消息队列的指针 */INT8U OSEventTblOS_EVENT_TBL_SIZE; /* 等待任务列表 */ OS_EVENT; 第5章嵌入式操作系统 C/OS-提供以下几种功能函数来实现对事件控制块ECB的操作:(1) OS_EventWaitListInit():初始化一个事件控制块。当 要 建 立 一 个 信 号 量 、 邮 箱 或 消 息 队 列 时 , 可 通 过 调 用 OS_EventWaitListInit()来对事件控制块中的等待任务列表进行初始化。该函数初始化一个空的等待任务列表,当初

66、始化完成时,表中没有任何等待事件的任务。第5章嵌入式操作系统 (2) OS_EventTaskRdy():使一个任务进入就绪态。函数OS_EventTaskRdy()用于使一个任务进入就绪态。当某个事件发生了,要将等待该事件的任务列表中的最高优先级的任务置于就绪态,这个操作是通过调用函数OS_EventTaskRdy()实现的。也就是说,该函数从等待任务队列中使最高优先级任务脱离等待状态,并把该任务置于就绪态。 第5章嵌入式操作系统 (3) OS_EventTaskWait():使一个任务进入等待某事件发生状态。当某个任务需要等待一个事件的发生时,通过调用函数OS_EventTaskWait(

67、),将当前任务从就绪任务表中删除,并放到相应事件的事件控制块ECB的等待任务列表中。(4) OS_EventTO():由于等待超时而将任务置为就绪态。如果在预先指定的时限内任务等待的事件没有发生,则通过调用OS_EventTO(),可以将等待超时的任务的状态置为就绪态。该函数负责从事件控制块中的等待任务列表中将任务删除,并把它置为就绪态,然后从任务控制块中将指向事件控制块的指针删除。 第5章嵌入式操作系统 2 2信号量管理信号量管理使用信号量可以在任务间传递信息,实现任务与任务或中断服务子程序的同步。C/OS-中的信号量由两部分组成:一部分是16位的无符号整型信号量的计数值(065 535);

68、另一部分是由等待该信号量的任务组成的等待任务列表。C/OS-提供了以下6个函数对信号量进行操作: 第5章嵌入式操作系统 (1) OSSemCreat():建立一个信号量。在使用一个信号量之前,首先必须建立该信号量。可以调用函数OSSemCreat()来建立信号量,并对信号量赋予一个取值范围在065 535的初始计数值。如果信号量用来表示一个或者多个事件发生,则该信号量的初始值通常赋为0;如果信号量用来对共享资源进行访问,则该信号量的初始值应赋为1;如果信号量用来表示允许任务访问n个相同的资源,则该信号量的初始值应赋为n,并把该信号量作为一个可计数的信号量使用。(2) OSSemDel():删除

69、一个信号量。函数OSSemDel()用来删除一个信号量。需要注意的是,在删除一个信号量时,必须首先删除使用该信号量的所有任务。 第5章嵌入式操作系统 (3) OSSemPend():等待一个信号量。通过调用函数OSSemPend()可以实现等待一个信号量,并对信号量进行减1操作。如果信号量是有效的(即信号量的计数值非0),则信号量的计数值递减,函数OSSemPend()将“无错”代码(OS_NO_ERR)返回给它的调用函数;如果信号量无效(计数值为0),且调用它的函数不是中断服务子程序,则调用OSSemPend()函数的任务进入挂起态,等待另一个任务(或中断服务子程序)发出该信号量。(4) O

70、SSemPost():发送一个信号量。函数OSSemPost()用于发送一个信号量,并对信号量进行加1操作。 第5章嵌入式操作系统 (5) OSSemAccept(): 无 等 待 地 请 求 一 个 信 号 量 。 函 数 OSSemAccept()完成的功能是:当一个任务请求一个信号量时,如果该信号量暂时无效,则让该任务简单地返回,而不是进入睡眠状态。(6) OSSemQuery():查询一个信号量的当前状态。在应用程序中,可以调用函数OSSemQuery()来随时查询一个信号量的当前状态。在调用该函数前,必须先定义一个指向数据结构OS_SEM_DATA的指针pdata,用该指针来存储信号

71、量的有关信息。 第5章嵌入式操作系统 3 3消息邮箱管理消息邮箱管理消息邮箱(简称邮箱)是C/OS-中的一种通信机制。在使用消息邮箱时,通常先定义一个指针型的变量,该指针指向一个包含了消息内容的特定数据结构。发送消息的任务或中断服务子程序把这个指针型的变量送往邮箱,接收消息的任务从邮箱中取出该指针变量,从而实现任务间或中断服务子程序与任务间的信息交换。C/OS-提供6种对消息邮箱的操作,它们通过以下函数实现: 第5章嵌入式操作系统 (1) OSMboxCreate():建立一个邮箱。使用邮箱之前,必须先建立邮箱。通过调用函数OSMboxCreate()可以创建一个邮箱,并且指定其初始值。这个初

72、始值一般是NULL,但也可以使其在最开始就包含一条消息。如果使用邮箱的目的是为了通知一个事件的发生(即只发送一条消息),则要初始化该邮箱为空(即NULL),因为在开始时事件很有可能还没发生;如果用邮箱共享某些资源,则要初始化该邮箱为一个非空的指针,在这种情况下,邮箱被当成一个二值信号量使用。 第5章嵌入式操作系统 (2) OSMboxDel():删除一个邮箱。函数OSMboxDel()用来删除一个邮箱。使用该函数时要特别注意,多个任务可能还在试图操作已经删除的邮箱。因此,在删除邮箱之前,必须首先删除可能操作该邮箱的所有任务。(3) OSMboxPend():等待邮箱中的消息。通过调用可以实现等

73、待一则消息发送到邮箱中的功能。如果邮箱中有消息(非NULL指针),则从该邮箱中取出该消息,返回给调用该函数的任务,并将NULL指针存入邮箱中;如果邮箱为空,则调用该函数的任务进入挂起态,等待另一个任务(或中断服务子程序)通过邮箱发送消息或者等待超时。 第5章嵌入式操作系统 (4) OSMboxPost():向邮箱发送一则消息。向邮箱发送一则消息可以通过调用函数OSMboxPost()来实现。该函数除了发送消息外,还会检查是否有任务在等待该邮箱中的消息,如果有,就会将其唤醒并进行一次任务切换。但是,如果从中断服务子程序中调用OSMboxPost(),则不会发生任务切换。(5) OSMboxPos

74、tOpt():向邮箱发送一则消息。可以使用一个功能更强的函数OSMboxPostOpt()向邮箱中发送消息。该函数是C/OS-新增加的函数,可以替代OSMboxPost()。此外,函数OSMboxPostOpt()可以向等待邮箱的所有任务发送消息(广播)。 第5章嵌入式操作系统 (6) OSMboxAccept():无等待地从邮箱中得到一则消息。当需要无等待地从邮箱中获得消息时,可以调用函数OSMboxAccept()来实现。如果调用了该函数,即使邮箱为空,应用程序也可以从邮箱中得到消息,而不必使任务进入挂起态。调用函数OSMboxAccept()的任务必须检查其返回值,如果返回值是NULL,

75、则说明邮箱是空的,没有可用的消息;如果该值是非NULL值,则说明邮箱中有消息可用。中断服务子程序在试图得到一则消息时,应该使用函数OSMboxAccept(),而不能使用函数OSMboxPend()。(7) OSMboxQuery():查询一个邮箱的状态。函数OSMboxQuery()使得应用程序可以随时查询一个邮箱的当前状态。 第5章嵌入式操作系统 4 4消息队列管理消息队列管理消息队列(简称队列)是C/OS-的另一种通信机制。它可以使一个任务或者中断服务子程序向另一个任务发送以指针定义的变量。针对不同的应用,每个指针指向的包含了消息的数据结构的类型也有所不同。C/OS-提供了9个对消息队列

76、进行操作的函数:OSQCreate()、OSQPDel()、OSQPend()、OSQPost()、OSQPostFront()、OSQPostOpt()、OSQAccept()、OSQFlush()和OSQQuery()。其中,除了OSQPost()、OSQPostFront()和OSQFlush(),其他几个函数的操作特点都与消息邮箱管理的功能函数类似。这里重点介绍一下这3个函数。 第5章嵌入式操作系统 (1) OSQPost():向消息队列发送一则消息(FIFO)。在向消息队列发送一则消息时,需要注意插入的新消息在队列中的位置。如果调用函数OSQPost(),则使用指针变量 .OSQIn

77、(指向消息队列中插入下一条消息的位置的指针)作为指向下一个插入消息的单元指针。新消息在消息队列中的位置满足先入先出(FIFO)的原则。第5章嵌入式操作系统 (2) OSQPostFront():向消息队列发送一则消息(LIFO)。函数OSQPostFront()与OSQPost()类似,只是在插入新的消息到消息队列中时,使用 .OSQOut(指向消息队列中下一条取出消息的位置的指针)而不是 .OSQIn,作为指向下一个插入消息的单元指针。因此,新消息在消息队列中的位置满足后入先出(LIFO)的原则。 (3) OSQFlush():清空消息队列。函数OSQFlush()允许清空一个消息队列中的所

78、有消息,从而使该队列可以重新开始使用。 第5章嵌入式操作系统 5.5.9 5.5.9 内存管理内存管理为了避免直接使用ANSI C中的malloc()和free()两个函数动态分配/释放内存所造成的内存碎片以及执行时间不确定等缺点,在C/OS-中,操作系统把连续的大块内存按分区来管理。每个分区中包含整数个大小相同的内存块。利用这种机制,C/OS-对malloc()和free()函数进行了改进,使得它们可以分配和释放固定大小的内存块。而且,这样使得malloc()和free()函数的执行时间是确定的。C/OS-对内存的管理主要是通过内存控制块(MCB,Memery Control Blocks)

79、和4个功能函数来实现的。 第5章嵌入式操作系统 1 1内存控制块内存控制块为了便于管理内存,C/OS-使用内存控制块来跟踪每一个内存分区,并对系统中的每个内存分区都建立它自己的内存控制块。内存控制块的数据结构如下所示:Typedef structvoid*OSMemAddr;/* 指向内存分区起始地址的指针 */void*OSMemFreeList; /* 指向下一个空余内存控制块或下一个空余内存块的指针 */INT32U OSMemBlkSize; /* 内存分区中内存块的大小 */ INT32U OSMemNBlks; /* 内存分区中总的内存块数量 */INT32U OSMemNFree

80、; /* 内存分区中当前可以获得的空余内存块数量 */ OS_MEM; 第5章嵌入式操作系统 2 2建立内存分区建立内存分区(OSMemCreate()(OSMemCreate()在使用一个分区之前,必须调用函数OSMemCreate()先建立该内存分区。如果函数OSMemCreate()操作失败,则它将返回一个NULL指针;否则,它将返回一个指向内存控制块的指针。对内存管理的其他操作,如OSMemGet()、OSMemPut()及OSMemQuery()等,都需要通过该指针进行。 第5章嵌入式操作系统 3 3分配内存块分配内存块(OSMemGet()(OSMemGet()当调度某任务执行时,

81、必须先从已建立的内存分区中为该任务申请一个内存块。应用程序通过调用函数OSMemGet()来从内存分区中申请一个内存块。显然,应用程序必须知道内存块的大小,并且在使用时不能超过其容量。当应用程序不再使用这个内存块后,必须及时将其释放,重新放回到相应的内存分区中。 第5章嵌入式操作系统 4 4释放内存块释放内存块(OSMemPut()(OSMemPut()函数OSMemPut()用来将应用程序不再使用的一个内存块释放并放回到相应的内存分区中。需要注意的是,OSMemPut()并不知道该内存块是属于哪个内存分区。5 5查询内存分区的状态查询内存分区的状态(OSMemQuery()(OSMemQue

82、ry()C/OS-提供OSMemQuery()函数来查询一个特定内存分区的状态。通过调用该函数,可以知道特定内存分区中内存块的大小、可用内存块数目以及已经使用的内存块数目等信息。所有这些信息都存放在OS_MEM_DATAS数据结构中。 第5章嵌入式操作系统 5.5.10 C/OS-5.5.10 C/OS-的初始化的初始化C/OS-在调用其他内核服务之前,首先要调用系统初始化函数OSInit()来对系统进行初始化。OSInit()初始化C/OS-所有变量和数据结构,并建立空闲任务OS_TaskIdle()。空闲任务OS_TaskIdle()总是处于就绪态,它的优先级总是设成最低,即OS_LOWE

83、ST_PRIO。如果统计任务允许OS_TASK_STAT_EN和OS_TASK_CREAT_EXT_EN都设为1,则空闲任务OS_TaskIdle()还要统计任务OS_TaskStat()并且使其进入就绪态。统计任务的优先级总是设为OS_LOWEST_PRIO-1。此外,C/OS-还初始化了5个空的数据结构缓冲区。每个缓冲区都是单向链表,允许C/OS-从缓冲区迅速取得或释放一个缓冲区中的元素。 第5章嵌入式操作系统 5.5.11 C/OS-5.5.11 C/OS-的启动的启动通过调用OSStart()能够实现C/OS-的多任务的启动。但是,在启动C/OS-之前,必须至少建立一个应用任务,其过程

84、如下所示:voidmain(void) OSInit();/* 初始化C/OS- */./* 通过调用OSTaskCreat()或OSTaskCreatExt()创建至少一个应用任务 */.OSStart();/* 开始多任务调度。OSStart()永远不会返回 */ 第5章嵌入式操作系统 5.6 C/OS-在在ARM上的移植上的移植 5.6.1 C/OS-5.6.1 C/OS-的移植条件的移植条件(1) 处理器的C编译器能够产生可重入代码。C/OS-是一个多任务实时内核,一段代码(如一个函数)可能被多个任务调用,代码的可重入性是保证多任务正确执行的基础。可重入代码是指可以被多个任务调用而数据

85、不会被破坏的一段代码。也就是说,可重入代码在执行过程中如果被中断,能够在中断结束后继续正确运行,不会因为在代码中断时被其他任务重新调用而破坏代码中的数据。可重入代码或者只使用局部变量,即变量保存在CPU寄存器中或堆栈中;或者使用全局变量,则要对全局变量予以保护。下面两个例子可以说明可重入代码和不可重入代码的区别。 第5章嵌入式操作系统 voidtemp (int *x, int *y)t temp;temp = *x;*x = *y; *y = temp; int tempvoidtemp (int *x, int *y)temp = *x;*x = *y; *y = temp; 第5章嵌入式

86、操作系统 (2) 处理器支持中断并能产生定时中断(通常在10100 Hz之间)。 C/OS-通过处理器产生的定时器中断来实现多任务之间的调度。 而在ARM7TDMI的处理器上可以产生定时器中断。(3) 用C语言可以在程序中开/关中断。在第2章介绍过,ARM处理器中包含一个CPSR寄存器,该寄存器包括一个全局的中断禁止位,控制它就可以打开或关闭中断。在C/OS-中,可以通过OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()两个宏来控制处理器的相应位进行开/关中断的操作。 第5章嵌入式操作系统 (4) 处理器支持能够容纳一定量(几千字节)数据的存储硬件堆栈。 对于一些只有

87、10根地址线的8位控制器,芯片最多可访问1 KB的存储单元,在这样的条件下,移植C/OS-是比较困难的。 第5章嵌入式操作系统 (5) 处理器有将堆栈指针和其他CPU寄存器的内容读出并存储到堆栈或内存中的指令。C/OS-进行任务调度时,首先将当前任务的CPU寄存器存放到该任务的堆栈中,然后再从另一个新任务的堆栈中恢复其原来的寄存器的值,使之继续运行。所以,寄存器的入栈/出栈操作是C/OS-多任务调度的基础。在ARM处理器中,汇编指令stmfd可将所有寄存器压栈,指令ldmfd可将所有的寄存器出栈。 第5章嵌入式操作系统 5.6.2 C/OS-5.6.2 C/OS-的移植步骤的移植步骤移植C/O

88、S-主要完成以下两部分工作。1 1设置与处理器和编译器相关的代码设置与处理器和编译器相关的代码 OS_CPU.H包括了用#define语句定义的、与处理器相关的常数、宏以及类型。因此,所有需要完成的基本配置和定义全部集中在此头文件中。OS_CPU.H的大体结构如下列程序所示: 第5章嵌入式操作系统 /* 数据类型(与编译器有关) */typedef unsigned char BOOLEAN;typedef unsigned char INT8U; /*无符号8位整数*/typedef signed char INT8S; /*有符号8位整数*/typedef unsigned int INT

89、16U;/*无符号16位整数*/typedef signed int INT16S;/*有符号16位整数*/typedef unsigned long INT32U;/*无符号32位整数*/typedef signed long INT32S;/*有符号32位整数*/typedef float FP32;/*单精度浮点数*/ 第5章嵌入式操作系统 typedef double FP64;/*双精度浮点数*/typedef unsigned int OS_STK;/*堆栈入口宽度为16位*/typedef unsigned short OS_CPU_SR; /*定义CPU状态寄存器宽度为16位*

90、/* 与处理器有关的代码 */#define OS_ENTER_CRITICAL() cpu_sr = INTS_OFF(); #define OS_EXIT_CRITICAL() if(cpu_sr = 0) INTS_ON(); #define OS_STK_GROWH1/* 定义堆栈方向:1=向下递减,0=向上递增 */#define OS_TASK_SW() ? /*定义任务切换宏 */ 第5章嵌入式操作系统 (1) 与编译器相关的数据类型。为了确保C/OS-的可移植性,其程序代码不使用C语言中的short、int及long等数据类型,因为它们是与编译器相关的。不同的微处理器有不同的字

91、长,因此C/OS-的移植包括了一系列的数据类型定义,这样定义的数据结构既是可移植的,又很直观。例如INT16U表示16位无符号整型数。对于像ARM这样的32位处理器,INT16U表示unsigned short型;而对于16位处理器,则表示unsigned int型。此外,用户必须将任务堆栈的数据类型告诉C/OS-。这是通过OS_STK声明恰当的数据类型来实现的。我们使用的处理器上的堆栈是16位的,所以将OS_STK声明为无符号整型数据类型。当建立任务时,所有的任务堆栈都必须用OS_STK作为堆栈的数据类型。 第5章嵌入式操作系统 (2) 定 义 OS_ENTER_CRITICAL()和 OS

92、_EXIT_CRITICAL()。在5.5节中分析过,与所有的实时内核一样,C/OS-在访问代码的临界段时首先要关中断,并在访问完毕后重新允许中断。这使得C/OS-能够保护临界段代码免受多任务或中断服务子程序的破坏。通常每个处理器都会提供一定的汇编指令来开/关中断,因此用户使用的C编译器必须有一定的机制支持直接从C语言中执行这些操作。但是,有些编译器允许在C源代码中插入行汇编语句,很容易实现开/关中断的操作;而有些编译器提供语言扩展功能,可以直接从C语言中开/关中断。为了隐藏编译器厂商提供的不同实现方法以增加可移植性,C/OS-定义了两个宏来开/关中断,即OS_ENTER_CRITICAL()

93、和OS_EXIT_CRITICAL()。 第5章嵌入式操作系统 在ARM处理器中,开/关中断是通过改变当前程序状态寄存器CPSR中的相应控制来实现的。由于使用了软中断,将CPSR保存到SPSR中,因此软中断退出时会将SPSR恢复到CPSR中。因此,程序只要改变SPSR中相应的控制位就可以实现开/关中断的操作。在S3C44B0X上改变这些位是通过嵌入汇编实现的,具体代码如下: INTS_OFFmrsr0, cpsr; 当前CPSRmovr1,r0; 复制屏蔽orrr1,r1,#0xC0 ; 屏蔽中断位msrcpsr, r1; 关中断andr0,r0,#0x80 ; 从初始CPSR返回到中断位mo

94、vpc,lr; 返回 第5章嵌入式操作系统 INTS_ONmrsr0, cpsr; 当前CPSRbicr0,r0,#0cC0; 屏蔽中断msrcpsr, r0; 开中断movpc,lr; 返回 第5章嵌入式操作系统 (3) 定义堆栈增长方向OS_STK_GROWTH。C/OS-在结构常量OS_STK_GROWTH中指定堆栈的增长方向。绝大多数微处理器和微控制器的堆栈是从上向下递减的,但是有些处理器使用的是相反的方式。C/OS-被设计成对两种情况都可以处理: 置OS_STK_GROWTH为0,表示堆栈从下(低地址)向上(高地址)递增。 置OS_STK_GROWTH为1,表示堆栈从上(高地址)向下

95、(低地址)递减。 第5章嵌入式操作系统 (4) 定义OS_TASK_SW()宏。在C/OS-中,处于就绪态任务的堆栈结构看起来就像刚刚发生过中断一样,所有的寄存器都保存在堆栈中。也就是说,C/OS-要运行处于就绪态的任务就必须要从任务堆栈中恢复处理器所有的寄存器,并且执行中断返回指令。为了实现任务调度,可以通过执行OS_TASK_SW()模仿中断的产生。OS_TASK_SW()是C/OS-从低优先级任务切换到高优先级任务时被调用的。任务切换只是简单地将处理器的寄存器保存到将被挂起的任务的堆栈中,并从堆栈中恢复要运行的更高优先级的任务。可以采用以下两种方式定义OS_TASK_SW():如果处理器

96、支持软中断,则中断服务子程序或指令陷阱使OS_TASK_SW()的中断向量地址指向汇编语言函数OSCtxSw();否则直接在OS_TASK_SW()中调用OSCtxSw()函数。 第5章嵌入式操作系统 2 2用用C C语言编写语言编写1010个与操作系统相关的函数个与操作系统相关的函数(OS_CPU_C.C(OS_CPU_C.C)这10个函数包括:OSTaskStkInit()、OSTaskCreatHook()、OSTaskDelHook()、OSTaskSwHook()、OSTaskIdleHook()、OSTaskStatHook()、OSTaskTickHook()、OSTaskHoo

97、kBegin()、OSTaskHookEnd()和OSTaskInitHook()。在这些函数中,唯一必须移植的函数是OSTaskStkInit(),其他9个Hook函数必须声明,但不一定要包含代码。 第5章嵌入式操作系统 (1) OSTaskStkInit()。函数OSTaskStkInit()在任务创建时(OSTaskCreat()或OSTaskCreatExt()被调用,作用是初始化任务的堆栈结构。这样,堆栈看起来就像中断刚发生过一样,所有寄存器都保存在堆栈中。在ARM微处理器上,任务堆栈空间由高至低依次保存着PC、LR、R12R0、CPSR及SPSR。OSTaskStkInit()初始

98、化后的堆栈内容如图5-11所示。堆栈初始化结束后,返回新的堆栈栈顶指针。 第5章嵌入式操作系统 图5-11 堆栈初始化后的内容 第5章嵌入式操作系统 (2) Hook函数。其余的9个Hook函数又称为钩子函数,主要用来扩展C/OS-的功能。这些Hook函数可以不包含任何代码,但必须被声明。在Hook函数内部,允许用户添加相应的代码来实现一些特定的功能,从而进一步扩展C/OS-的功能。 第5章嵌入式操作系统 3 3 用用 汇汇 编编 语语 言言 编编 写写 4 4个个 与与 处处 理理 器器 相相 关关 的的 函函 数数 (OS_CPU_A.ASM)(OS_CPU_A.ASM)C/OS-在移植过

99、程中要求用户编写4个简单的汇编语言函数,包括:OSStartHighRdy()、OSCtxSw()、OSIntCtxSw()和OSTickISR()。如果C编译器支持插入行汇编代码,就可以将所有与处理器相关的代码放到OS_CPU_C.C文件中,而不必再建立单独的汇编语言文件。 第5章嵌入式操作系统 (1) OSStartHighRdy():运行就绪态的优先级最高的任务。C/OS-的多任务启动函数OSStart()通过调用函数OSStartHighRdy()使得处于就绪态的、优先级最高的任务开始运行。函数OSStartHighRdy()负责从最高优先级任务的TCB控制块中获得该任务的堆栈指针SP

100、,并通过SP依次将CPU现场恢复。这时,系统将控制权交给用户创建的任务进程,直到该任务被阻塞或被其他更高优先级的任务抢占CPU。该函数仅仅在多任务启动时被执行一次,用来启动最高优先级的任务。函数OSStartHighRdy()的示意性代码如下列程序所示(用户应将它转换成汇编语言代码,因为它涉及到将处理器寄存器保存到堆栈的操作): 第5章嵌入式操作系统 VoidOSStartHighRdy(void)调用用户定义的OSTaskSwHook();OSRunning = TRUE;得到将要恢复运行任务的堆栈指针;Stack pointer = OSTCBHighRdy - OSTCBStkPtr;从

101、新任务堆栈中恢复处理器的所有寄存器;执行中断返回指令; 第5章嵌入式操作系统 (2) OSCtxSw():任务级的任务切换。任务级的任务切换时通过执行软中断指令;或者依据处理器的不同,执行TRAP(陷阱)指令来实现。而中断服务子程序、陷阱或异常处理的向量地址必须指向OSCtxSw()。函数OSCtxSw()由OS_TASK_SW()宏调用,而OS_TASK_SW()由函数OSSched()调用,函数OSSched()负责任务之间的调度。函数OSCtxSw()被调用后,先将当前任务的CPU现场保存到该任务的堆栈中;然后获得最高优先级任务的堆栈指针,并从该堆栈中恢复此任务的CPU现场,使之继续执行

102、。这样函数OSCtxSw()就完成了一次任务级的任务切换。其示意性代码如下列程序所示(这些代码必须用汇编语言编写,因为用户不能直接在C语言中访问CPU寄存器): 第5章嵌入式操作系统 VoidOSCtxSw(void)保存处理器的寄存器;在当前任务的任务控制块中保存当前任务的堆栈指针;OSTCBCur - OSTCBStkPtr = Stack pointer;OSTaskSwHook();OSTCBCur = OSTCBHighRdy;OSPrioCur = OSPrioHighRdy;得到将要重新开始运行的任务的堆栈指针;Stack pointer = OSTCBHighRdy - OST

103、CBStkPtr;从新任务的任务堆栈中恢复处理器所有寄存器的值;执行中断返回指令; 第5章嵌入式操作系统 (3) OSIntCtxSw():中断级的任务切换。OSIntExit()通过调用函数OSIntCtxSw(),在ISR中执行任务切换功能。因为中断可能会使更高优先级的任务进入就绪态,所以为了让更高优先级的任务能够立即运行,在中断服务子程序退出前,函数OSIntExit()会调用OSIntCtxSw()做任务切换,从而保证系统的实时性。函数OSIntCtxSw()和OSCtxSw()都是用来实现任务切换功能的,其区别在于不需要在函数OSIntCtxSw()中保存CPU的寄存器,因为在调用O

104、SIntCtxSw()之前已经发生了中断,在中断服务子程序中已经将CPU的寄存器保存到了被中断的任务的堆栈中。函数OSIntCtxSw()的示意性代码如下列程序所示(因为在C语言中不能直接访问CPU寄存器,所以这些代码必须用汇编语言编写): 第5章嵌入式操作系统 VoidOSIntCtxSw(void)调用用户定义的OSTaskSwHook();OSTCBCur = OSTCBHighRdy;OSPrioCur = OSPrioHighRdy; 得到将要重新执行的任务的堆栈指针;Stack pointer = OSTCBHighRdy - OSTCBStkPtr;从新任务的任务堆栈中恢复处理器

105、所有寄存器的值;执行中断返回指令; 第5章嵌入式操作系统 (4) OSTickISR():时钟节拍中断服务。C/OS-要求用户提供一个周期性的时钟源,从而实现时间的延迟和超时功能。为了完成该任务,必须在开始多任务后,即调用OSStart()后,启动时钟节拍中断。但是,由于OSStart()不会返回,因此用户无法实现这一操作。为了解决这个问题,可以在OSStart()运行后,C/OS-启动运行的第一个任务中初始化节拍中断服务函数OSTickISR()。 第5章嵌入式操作系统 函数OSTickISR()首先将CPU寄存器的值保存在被中断任务的堆栈中,之后调用OSIntEnter();然后,OSTi

106、ckISR()调用OSTimeTick(),检查所有处于延时等待状态的任务,判断是否有延时结束就绪的任务;最后,OSTickISR()调用OSIntExit(),如果中断使其他更高优先级的任务就绪且当前中断为中断嵌套的最后一层,那么OSIntExit()将进行任务切换。函数OSTickISR()的示意性代码如下列程序所示(因为在C语言中不能直接访问CPU寄存器,所以这些代码必须用汇编语言编写): 第5章嵌入式操作系统 VoidOSTickISR(void)保存处理器的寄存器;调用OSIntEnter()或者OSIntNesting+;if(OSIntNesting = 1)OSTCBCur -

107、 OSTCBStkPtr = Stack pointer;给产生中断的设备清中断;重新允许中断(可选);OSTimeTick();OSIntExit();恢复处理器寄存器;执行中断返回指令; 第5章嵌入式操作系统 5.6.3 5.6.3 测试移植代码测试移植代码当为处理器做完C/OS-的移植后,还需要测试移植的C/OS-是否正常工作。应该首先不加任何应用代码来测试移植好的C/OS-,即应该首先测试内核自身的运行状况。接着可以在C/OS-操作系统中建立应用程序,通过观察程序执行的结果来检测移植是否成功。通常采用以下4个步骤测试移植代码:(1) 确保C编译器、汇编编译器及链接器正常工作。(2) 测

108、试函数OSTaskStkInit()和OSStartHighRdy()。(3) 测试函数OSCtxSw()。(4) 测试函数OSIntCtxSw()和OSTickISR()。 第5章嵌入式操作系统 5.7 基于基于C/OS-构建的构建的TCP/IP/PPP协议栈协议栈 5.7.1 5.7.1 嵌入式协议栈概述嵌入式协议栈概述嵌入式协议栈的运行必须基于嵌入式操作系统平台的支持,但并不是说协议栈必须依赖于嵌入式操作系统的API。实际上,许多嵌入式协议栈做到了相对于操作系统的最大独立性,可以与大多数嵌入式操作系统集成运行。 第5章嵌入式操作系统 但是,协议栈以及附带的上层接口和下层驱动程序会给嵌入式

109、系统设计设置其他的约束。例如,TCP/IP协议栈必须有一个相对的准确时间源,以便进行时间管理(处理各种发生在栈中的超时和定时行为)。此外,协议栈还必须包含一个资源管理系统。这个系统可能是一个标准的动态存储管理系统,也可能是为速度预先分配包缓存的客户系统。目前,嵌入式协议栈的提供有两种方式:一种是独立的第三方协议栈产品;另一种是嵌入式操作系统提供商提供协议栈产品。后一种方法在开发时相对要简单些,而且能够提供较强的软件可使用性。 第5章嵌入式操作系统 5.7.2 5.7.2 选择协议栈选择协议栈建立基于网络的嵌入式系统需要根据具体的应用环境和使用的网络技术来选择适合的网络协议栈。例如,如果开发的嵌

110、入式产品是基于网络的打印机,且能够通过Novell服务器存取,那么就必须采用Netware打印协议的协议栈;如果设计的打印机要从尽可能多的网络访问,则最好选择TCP/IP协议栈,因为TCP/IP是应用最广泛的协议;如果设计的打印机同时支持TCP/IP和Netware协议,那么打印机就既可以通过TCP/IP存取,也可以通过Netware局域网络存取。 第5章嵌入式操作系统 为嵌入式系统选择网络协议栈需要从以下三方面进行考虑:(1) 网络协议占用的内存:对于一个网络协议栈组件,占用的内存主要表现在两个方面,一个是协议栈代码段占用的存储器的大小;另一个是数据段占用的存储器的大小。其中,数据段包括普通

111、数据段和堆栈段对存储器的占用。选择协议栈时,需要对协议栈占用的内存进行估计。例如,对于常用的TCP/IP协议栈,不同的商家提供的产品不同。有的用于PC机的协议栈产品不一定适合嵌入式系统应用,因为PC机使用的协议栈可能不考虑对内存的占用,而嵌入式系统对内存的要求比较苛刻。通常厂家会提供这一指标,如interniche的TCP/IP协议栈占用内存大约50 KB左右。 第5章嵌入式操作系统 (2) 硬件资源:硬件资源主要指的是与网络有关的部件,如通信控制器、物理层接口等。在选择网络组件时,需要考虑这些硬件资源的成本因素。有时还需要考虑集成和分离两种设计方法的实现成本等因素。(3) 协议开销:协议开销

112、指的是CPU运行协议栈产生的开销。添加网络支持可能会导致系统实时响应的延迟。有时还需要对嵌入式处理器进行升级,从而导致系统成本增加。一般情况下,嵌入式协议栈与嵌入式操作系统是集成在一起的,购买集成的嵌入式协议栈时,商家可以提供集成系统的综合指标。 第5章嵌入式操作系统 5.7.3 5.7.3 嵌入式嵌入式TCP/IPTCP/IP协议栈协议栈1 1嵌入式嵌入式TCP/IPTCP/IP协议栈概述协议栈概述TCP/IP协议是一项应用广泛的协议标准,利用它可以互联所有的计算机和网络,通过绝大多数传输媒体,几乎可以与运行在所有操作系统上的软件进行通信。嵌入式系统通过以太网、电力线和电话线等载体可以实现与

113、Internet互联;也可以利用无线接入技术解决基础电缆不到位的问题。 第5章嵌入式操作系统 如果是与局域网连接,则只需要为嵌入式设备配备以太网卡和IP地址即可。如果是利用电话线路,则设备可以使用电话用户的ID,通过PPP协议与TCP/IP进行互联,在这里,PPP协议运载IP数据包。通过TCP/IP网络协议栈,几乎可以从世界上任何地方来访问或控制这些实现了互联的嵌入式装置。 第5章嵌入式操作系统 一般情况下,TCP/IP协议栈是通过选择能够支持TCP/IP的RTOS来实现的。例如,QNX公司的Neutrino RTOS,ATI公司的Nucleus Plus,Windrier公司的VxWorks

114、等操作系统都提供了TCP/IP协议栈,而且它们提供的协议栈大部分都是可以剪裁的。嵌入式TCP/IP协议栈省去了诸如接口间转发软件、全套Internet服务工具以及支持电子邮件的工具等几个协议,这些软件工具在嵌入式装置中很少使用。虽然大多数TCP或UDP的应用可以在任何协议栈上运行,但是比较小型的嵌入式TCP/IP栈对于如何配置系统有一定局限,也只适用于一定的软插座。例如,QNX公司的协议栈是模块化的,如果存储器的容量有限,可以在需要使用时进行动态安装。此外,由于Neutrino和核心之间不需要机器语言的连接,因此可对系统进行部分更新,可以远程对系统进行更新或重新编程,而不需要进行引导。 第5章

115、嵌入式操作系统 嵌入式协议栈的一些指标和接口可能与普通的协议栈不同,这体现在以下几个方面:(1) 嵌入式协议栈的API可能与普通的协议栈不同。普通嵌入式的接口是标准的,如winsock、BSD socket等,标准化的优点是可实现应用软件的兼容性,但是带来的问题是为了实现标准化的接口必然使用了大量的代码,不仅效率低,而且处理器和存储器的开销大。当然,也有许多厂商将标准的协议栈接口移植到嵌入式系统中。于是,建立在Berkeley socket上的协议栈也称为嵌入式的。但是,Berkeley栈带有许多台式机所需要的特性,而这些特性对于嵌入式的应用是不需要的。总之,建立在专用socket基础上的协议

116、栈效率比较高,但是它提供的API与通用插座协议栈API有所不同。 第5章嵌入式操作系统 (2) 嵌入式协议栈的可裁剪性。普通协议栈使用协议栈的全集,只是因为普通计算机的资源丰富。对于资源有限的嵌入式系统来说,可裁减性非常重要。例如,TCP/IP协议中,UDP和TCP属于传输层协议。TCP是面向连接的、传输可靠的协议。为了保证到达数据的准确无误,TCP采用校验和的方式来检查数据是否有错误或有丢失,如果发现存在问题则要求重发;再者,TCP协议在应用层上保证到达数据的前后次序无误,接收数据的节点负责恢复数据的顺序。而UDP是无连接的、不能保证可靠传输的协议,UDP仅仅把校验和作为选项,也不保证数据的

117、顺序,不存在重发的必要,所以它的效率比较高。因此,这两种协议有不同的用途。如果通信系统通道对于可靠性的要求不太高,但是对实时性和效率要求比较高,则可以选用UDP。UDP比较适合于传输媒体本身十分可靠的情况,此时不需要采用TCP协议那样的服务程序,从而减少了系统的负担。 第5章嵌入式操作系统 (3) 嵌入式TCP/IP协议栈的平台兼容性。通常,普通协议栈与操作系统的结合比较紧密,协议栈的实现依赖于操作系统提供的服务,移植起来一般比较困难。但是,嵌入式协议栈设计一般对操作系统的依赖性不大,便于移植,许多商用的嵌入式TCP/IP协议栈支持多种操作系统平台,或者需要很少的移植代码。(4) 嵌入式协议栈

118、的效率较高。这主要体现在:占用的代码空间小、需要的数据存储器小、代码的效率高,从而可以减少对处理器的处理速度的要求。 第5章嵌入式操作系统 2 2嵌入式嵌入式TCP/IPTCP/IP协议栈的几种形式协议栈的几种形式(1) 应用于DSP的协议栈。嵌入式协议栈可以选用以DSP为 基 础 的 TCP/IP协 议 栈 。 例 如 , eDevice公 司 提 供 的SmartStack协议栈,就是在Analog Devices公司的AD1218x DSP芯片的基础上实现的。基于DSP建立协议栈时,还可以把调制解调器的软件和TCP/IP协议的软件协议栈同时放在一个芯片上。 第5章嵌入式操作系统 (2)

119、基于硬件实现的协议栈。大多数TCP/IP协议栈使用软件实现。但是,由于目前TCP/IP技术已经非常成熟,于是许多厂商将TCP/IP协议栈用硬件方法予以实现,从而可以提高效率、降低成本。例如,采用iReady的芯片或芯核,虽然使用的是4位微处理器,但也能够和Internet实现直接连接。(3) 普通的协议栈。大多数大型的嵌入式设备使用普通的TCP/IP协议栈。普通TCP/IP协议栈的大部分代码利用软件实现,系统采用普通的嵌入式处理器。IP协议下面的支持软件主要有两种:对于以太网,利用以太网卡和网卡的驱动程序实现链路层协议;对于远程网络如电话线,使用调制解调器,支持软件采用PPP协议。 第5章嵌入

120、式操作系统 (4) 代理协议栈。有些应用产品不需要采用TCP/IP的全集,可以让设备通过运行在网关上的代理来间接地接入TCP/IP网络。例如,通过Internet来控制家庭中的照明灯,或用来控制温度自动启动装置(如自动火警报警系统)。这些装置的功能有限、成本有限,为它们分别安装TCP/IP协议栈所增加的成本费用是难以接受的。因此,可以在照明灯和网关之间建立一个协议代理,让网关将信息经过翻译再传输给TCP/IP,使照明灯和Internet之间实现桥接。第5章嵌入式操作系统 运行协议代理的网关有多种形式,它可以是本地LAN中的一个装置、也可以是ISP或某个端口所支持的一种服务,还可以是目标服务器中

121、的一项功能机构。网关所起的作用是:在适当的时刻识别代理所提供的业务信息,并将此信息导向代理服务器,然后由代理服务器将业务信息转换成TCP/IP的业务信息,再送入到数据流中。 第5章嵌入式操作系统 5.7.4 基于基于C/OS-的的TCP/IP/PPP协议栈协议栈 1 1网络缓冲器网络缓冲器(Network Buffers)(Network Buffers) C/ip使用的核心资源采用了一种基于网络或者内存缓冲器(memory buffer)的策略。BSD使用的是一个内存缓冲器:标准数据在接口处被传送到一个缓冲器链表中,然后经排队等待通过协议层,最终到达套接字层。C/ip修改了内存缓冲器,并重新

122、命名它为网络缓冲器。因为系统只有有限的RAM空间,而且必须确保总是有能够使用的存储空间,所以在C/ip中创建了大小确定的网络缓冲器链表,并将其作为一个资源池来进行管理。网络缓冲器模块提供了以下功能:支持网络缓冲器的分配、排队以及各种服务,如初始化、添加一个新的网络缓冲器、从链表中删除一个网络缓冲器等。在这个协议栈中,通过应用接口的串行中断处理机制来使用这些网络缓冲器。 第5章嵌入式操作系统 对任何特殊的应用,合理选择网络缓冲器的大小对获取最佳操作性能都是非常重要的。C/ip设置网络缓冲器大小采用了以下规则:首先,设置网络缓冲区的大小,使得每一个单个的网络缓冲器在平均意义上能够存放每一个数据包;

123、如果应用程序是与许多小的数据包(很少超过100字节)通信,例如一个每次只返回一行数据的远程登录(telnet)应用,那么,设置网络缓冲器的大小约为50个字节;如果应用程序是进行批传输,那么设置网络缓冲器的大小能够处理一个完整的TCP包,从而能够简化连接。 第5章嵌入式操作系统 2 2点对点协议点对点协议(Point to Point Protocol(Point to Point Protocol,PPP)PPP)PPP由许多不同的协议组成,这些协议处理的操作包括连接测试、压缩和用户认证等。从本质上来说,PPP从串行端口接收网络缓冲器数据包链,然后过滤出换码序列,通过解压缩后最终将一个IP数据

124、包传递给IP协议。在大多数系统中,PPP是作为一个数据包驱动器来实现的,因此它适合以太网数据包驱动器的接口(但需要管理另外一套缓冲器)。 第5章嵌入式操作系统 3 3TCP/IPTCP/IP协议协议与其他所有的嵌入式TCP/IP协议栈一样,C/ip 的TCP的代码是基于BSD的。但是,与BSD最大的区别在于对定时器的实现和对信号量的使用。KA9Q是为单任务环境的DOS系统编写的,因此它使用复查(callback)功能来使应用程序使用协议栈。要想使用较为复杂的模块调用方式,则必须为TCP的控制块(TCP Control Block,TCB)建立信号量,从而能够实现同步读、写和连接,并通过互斥型信

125、号量来保护临界段代码。但是,这种设计无法唤醒那些正在等待读或写的多任务,而这个功能在连接已经关闭的情况下却非常有用。 第5章嵌入式操作系统 在Unix中,处理TCP定时器的一般方法是让程序以200 ms和500 ms的时间间隔轮流检测所有的TCB。轮流检测意味着CPU将要浪费时间去检测那些不需要被服务的TCB。但是,这种方法消耗的时间和数量众多的TCB是成比例的(如线性比例)。C/ip使用了Linux类型的定时器。通过预分配定时器结构,将其插入到一个规则链接的列表中。这个列表被系统时钟中断循环检测。这种定时器的设计方案在TCB数量较少时工作得比较好;当TCB数量增多时(几十个),从列表中插入或

126、删除定时器消耗的时间将会以非线性的方式增长,远远超过了循环检测所有TCB的时间。 第5章嵌入式操作系统 C/ip的IP模块非常基本,只处理一些简单的例程而不处理IP碎片。C/ip改变了套接字形式的网络地址的使用。当处理任何类型的网络地址(处理IPv6)时,套接字地址结构的使用非常普遍,因此,C/ip采用这种方法来简单地处理IP地址。 第5章嵌入式操作系统 4 4C/ipC/ip的功能的功能C/ip主要实现了以下功能:(1) 在PPP协议中使用PAP协议进行用户认证,使用VJ协议进行数据压缩。(2) 动态IP技术。(3) 优化了简单请求/应答交换。(4) 用定制的间隔进行TCP存活检测。C/ip

127、目前不支持以下功能:(1) CHAP协议的用户认证(协议栈中实现了这部分代码但是没有使用这项功能)。(2) TCP延时确认技术。 第5章嵌入式操作系统 思考与练习题思考与练习题 1什么是嵌入式操作系统?请列举几个典型的嵌入式操作系统,并简述其主要特点。2非占先式内核与占先式内核的主要区别是什么?3任务之间的通信方式有哪几种?每一种方式的特点是什么?4采用哪些方法可以实现对共享数据结构的互斥操作?5任务之间的同步方式有哪几种?每种方式的特点是什么?6简要说明操作系统的结构组成。第5章嵌入式操作系统 7试解释下列术语的含义:任务 任务切换 任务调度 临界段 任务控制块(TCB) 事件控制块(ECB) 8C/OS-中的任务有哪些状态?9试列举C/OS-中两种典型的任务结构。10C/OS-中进行任务管理常用的函数有哪些?11简述C/OS-中的中断服务过程。12简述C/OS-的启动过程。13移植C/OS-需要满足哪些条件?14请简要描述C/OS-的移植步骤。15简述基于C/OS-的TCP/IP/PPP协议栈(即C/ip)的特点。

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 建筑/环境 > 施工组织

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号