嵌入式系统以及如何构造一个嵌入式系统概览
时间:2007-02-17 来源:PHP爱好者
大多数的Linux系统运行于PC平台,但是,Linux也可在嵌入式系统中可靠的工作。此篇论文讲述了嵌入式系统的概况,以及论证了有关Linux应用于商业嵌入式系统的问题。
嵌入式系统 ---- 比摩西还老
那些用以控制设备的计算机,或叫嵌入式系统,差不多同计算机本身一样早地出现在我们的周围。
在通信领域中,这些嵌入式系统早在20世纪60年代后期就被用来控制电话的电子式机械交换并被称为“存储程控控制“系统“计算机”一词在那时尚不常见;所谓的存储程序是指那些放有程序和路由信息的内存。存储这些控制逻辑而不是用硬件来实现是在观念上的一种真正突破,现今,我们早认为这种工作机理是理所当然的了。
为适应每一个应用,这些计算机是被定做出来的(简言之,这些计算机是面向应用的).按今天的标准来看,他们有着奇怪的专用指令以及与主要计算引擎集成在一起的I/O设备,就像一批突变异种者.
微处理器通过提供一个小巧低价的并可以在大系统中像搭积木那样使用的CPU引擎改变了这一情况;它利用一基于被一条总线挂接在一起的不同外设所构建的严格的硬件体系结构并提供一个可以简化编程的通用目的编程模型.
同硬件一起,软件也得到了发展.最初,只有一些简单的开发工具可供用以创建和调试软件.各工程项目的运行软件通常以信手涂鸦的方式编出来.由于编译器经常有很多错误而且也缺乏象样的调试器,这些软件差不多总是用汇编语言或宏语言来写.采用软件构建块和标准库的编程思想直到20世纪70年代中期才流行起来.
用于嵌入式系统的与"搁架"无关的操作系统(OS)在20世纪70年代后期开始出现.它们中的许多是用汇编语言写就的并且仅能用于为其编写的微处理器上.当这些微处理器变得过时的时候,它们使用的OS也厄运同临.只能在新的处理器上从新写一遍才能运行.今天,许多这种早期的系统只不过成了人们模糊的记忆,还有人能记起MTOS吗?当C语言出现后,OS可以用一种高效的,稳定的和可移植的方式来编写.这种方式对使用和经营有直接的吸引力,因为它承载着人们当微处理器废弃不用时能保护他们的软件投资的希望.听起来,有点儿像商业市场营销中的一段传奇故事.用C来编写OS已经成了一种标准直至今天.总之,软件的可复用性已经为人接受而且正在很好地发挥作用.
在20世纪80年代早期,我特别喜爱的OS是Wendon操作系统;大概只要150美金,就可以获得它的C源代码库.它是一个开发套件,人们可以通过选择一些组件来构建自己的OS---整个过程就像是从中餐菜单里订餐一样.比如,可以从库中的多个可行选项列表中精选出一种任务调度算法和内存管理方案.
许多用于嵌入式系统的的商业操作系统在20世纪80年代获得了蓬勃发展.(Wendon)这一原始的炖菜已经发展成为了商业操作系统这一现代炖肉.今天已经有几打的商业性操作系统可供选择.出现了许多互相竞争的产品,如VxWorks,pSOS,Neculeus和WindowsCE.
许多嵌入式系统根本就没有操作系统,只不过有一个控制环而已.对很简单的嵌入式系统来说,这可能已经足够.不过,随着嵌入式系统在复杂性上的增长,一个操作系统显得重要起来,因为否则的话,将使(控制)软件复杂度变得极不合理.可悲的是,现实中确实有一些复杂得另人生畏的嵌入式系统,而且它们之所以变得复杂就因为它们的设计者坚持认为它们的系统不需要操作系统.
渐渐地,更多的嵌入式系统需要被连接到某些网络上,因而,需要在嵌入式系统中有网络协议栈(支持);甚至很多宾馆中的门把手都有一个连接到网络的微处理器.
把网络栈添加到一个仅用控制环来实现的简单嵌入式系统所带来的复杂程度可能足以唤起人们对一个操作系统的渴望.
除了各种商业性操作系统以外,还有多种私拥操作系统.其中,有很多是涂鸦式写就的,像Cisco公司的IOS等.有些则源于对别的操作系统的改写,像很多网络产品都衍生于同一版本的伯克利UNIX操作系统,因为后者有完整的网络支持能力;而还有一些则基于公共域OS,比如KA9Q就来源于PhilKarn.
作为侯选的嵌入式操作系统,LINUX有一些引人的优势:它可以移植到多个有不同结构的CPU和硬件平台上,很好的稳定性,各种性能的升级能力,而且开发更容易.。
开发工具--打破了传统仿真器的阻碍
在开发嵌入式系统中极为关键的一项是有各种可用的工具.就像任何一个行当一样,好的工具有助于快捷而圆满地完成任务;在嵌入式系统开发的不同阶段,可能要用到不同的工具.
传统上,开发嵌入式系统的首选工具是仿真器。这是一块比较昂贵的设备,一般插于微处理器和它的总线之间的电路中,从而让开发者监视和控制所有输入和输出 微处理器的各种活动和行为.在装配起来,可能有一些困难,并且由于它们的侵入性,装上后可能造成不稳定 的性能;尽管这样,它们却能在总线级上给出一个系统正在发生什么的清晰的描绘并排除了很多在硬件和软件 接口最底层上的猜测工作.
在以往,一些工程项目依赖它--经常在开发周期中的各个阶段--作为主要的调试工具.不过,一旦当编制的软件 有能力支持一个串型口的时候,大量的调试可以不用ICE而使用别的方法来完成.同样,大部分新一代的嵌入式系统 采用蛮像食谱式的微处理器设计;通信工作的启动代码常常是具备的以使串型口尽快地工作,这意味着开发者能 在没有ICE的情况下也能很好地进展;去掉了ICE,从而降低了开发成本.一旦串型口可以工作起来,便能用于支持那 些日渐复杂的开发工具的相关(软件)层
LINUX基于GNU C 编译器;后者作为GNU工具集的一组成部分,和源码级调试器gdb一起工作,提供了在开发一个嵌入式 LINUX系统中要用到的所有软件工具.下面是在为一个新的硬件开发一个新的嵌入式LINUX系统时要用到的典型调试 工具的序列和步骤:
1:写出或移植一段启动代码(后面再详细讨论);
2:写一段代码在串型口上输出一字符串,像"Hello,World!"(其实,我更喜欢人类发明电话后,通过电话说的第一句话"Watson, come here I need you");
3:移植gdb目标码使之能在串型口上工作.这将允许向另一台正运行着gdb程序的LINUX主机会话;你只不过要告诉 gdb是通过串型口调试该目标程序;gdb通过串型口与你的测试计算机上的gdb目标码会话并给出全部C源码级的 调试信息.你也可以利用这一(通信)能力把附加的代码下载到RAM或闪存中.
4:借助gdb,执行余下的直到LINUX内核开始接管之前的所有硬件和软件的初始化代码.
5: 一旦LINUX内核启动后,上述的串型口就成为LINUX的控制台端口并可以利用它的便利来进行后继开发过程.再使用gdb的内核调试版本kgdb.这一步常常不是必需的.如果你有一个网络连接,比如,10BaseT,你或许会想让它紧接着工作起来.
6: 如果你的目标平台运行的Linux kernel是全功能的(即:未经删减过功能),你可以利用gdb或其图形化替代品如xgdb去调试你的应用进程.
实时--真是这样吗?
轻率,大部分的系统并不是如此。嵌入式系统经常被错误地说做实时系统,但是它们中的大多数并不具备实时特性。实时性仅仅是相对的。实时严谨地定义应为硬实时:能在极短的时间(毫秒级)内响应,并以某种确定的方式处理事件。现在,许多硬实时功能正逐渐集中在DSP或ASIC的设计中,通过一些适当的硬件,如FIFO,DMA或其它专用硬件来实现。
对大多数系统来说,有1到5毫秒的实时响应时间应足够了。当然,另一种宽松的要求也是可以接受的,例如:
Windows 98的处理监视器崩溃画面的中断,要求必须在4微秒之内处理的,占所有情况的98%;而在20微秒之内处理的,占各情况的100%.
这些宽松的实时要求可以很容易达到.实现它们过程中涉及到一些探讨,包括现场切换,中断延迟,任务选优和调度.
现场切换曾一度成为操作系统界的一个热点话题,不过,由于大多数CPU在这点上处理得比较令人满意而且CPU速度现在已经变得足够的快,现场切换现已不再是主要关注点了.
实时的严格性要求通常应由一个中断例程或内核中的现场驱动函数来处理以确保行为的一致性.当中断发生后,处理该中断所用的时间,即中断延迟,在很大程度上,由中断优先级与其它能临时屏蔽该中断的软件决定.
(实时系统中的)中断必须应被高效地设计和安排以确保满足时间上的要求,就像在其它OS中那样.在Intel X86处理器系列中,这项工作可以能被扩充了实时性的Linux很好地处理.(实时Linux,即:RTLinux,参看http://www.rtlinux.org/).从本质上说,它提供了一个把Linux作为其后台任务而运行的中断处理调度器.(This essentially provides an interrupt processing scheduler that runs Linux as its background task)一些关键(临界)中断可以不为Linux其他部分所知地得到服务(处理),因而,你就有了对临界时间的控制权.这种做法提供了实时级别和时间限制性较为宽松的基本Linux级别之间的界面.并提供了一个与别的嵌入式操作系统类似的实时处理框架.从根本上讲,为满足实时性要求,采用了把实时性的关键(临界)代码段隔离开来并进行高效的安排,然后对该段代码的处理结果再以更一般的方式(或许在进程级别上)来做进一步处理.
嵌入式系统--定义
一种观点认为: 如果某种应用没有用户界面,从而,用户不能直接地和它交互,那么它就是嵌入式系统.这当然太过简单化了.电梯控制系统是嵌入式系统,但却有一个用户界面: 选择楼层的按钮和显示电梯正到达几层的指示器. 对于那些连入网络的嵌入式系统,如果该系统包含一个用于监视和控制的web服务器,界面上的区别就更显模糊了.一个较好的定义应强调在该系统的重要功能或主要用途上.
由于Linux可以提供一个用以执行嵌入功能的基本内核以及各种你想要的用户界面元素,所以Linux有很强的通用特点.它能处理嵌入性任务和有关用户界面两方面的作业.可以把Linux看作如下的一个连续体:
从一个只有内存管理,任务调度,定时器服务的缩简的微内核到一个支持各种文件系统和多种网络服务的完整服务器.
一个最小的嵌入式系统仅需如下基本组成部分:
1 : 一个用作引导的可用设施(工具);
2 : 一个具备内存管理,进程管理,和定时器服务的Linux微内核;
3 : 一个初始进程;
为让上面的最小嵌入式系统变得有一定实用性,尚需加上一些东西:
1 : 硬件的驱动程序;
2 : 一个或几个应用进程以提供必要的应用功效.
随着对系统要求的增加,也许还要用到下面这些组件:
1 : 一个文件系统(或许放在ROM或RAM中);
2 : TCP/IP 网络协议栈;
3 : 一个磁盘用来存放半易失性数据和提供交换能力.
硬件平台
挑选最好的硬件是一项很复杂的工作,充满着各种顾忌和干扰:公司的政策,各种成(偏)见,其它工程的影响以及缺乏完整或准确的信息.
成本经常是一个关键性因素.当你着眼成本时,一定要考虑产品的整体成本,而不要只看到CPU;有时一个快速而廉价的CPU可能成了这个产品的耗价的问题根源,一旦当你加上总线和延迟逻辑以便同你的各种外设协同工作的时候.如果你是一个仅管听命的软件员,那么你就只能对早已作好的决定撞运气了,如果你是系统的设计者,就需要尽力制订一个合理的预算并且所选用的硬件要能令你满意地处理实时任务.
从实际中观察CPU到底需要多快方能把工作做好,然后把这个速度乘3(才是系统将所要的CPU速度);因为CPU在理论上所能达到的能力到了现实中总是难以置信地大打折扣!不要忘记缓存对你的系统的影响.
也要计算出总线需要运行多快,如果有二级总线(像一条PCI总线那样),也要把它们包含进来;一条慢的或过多参与DMA传输的总线能够让一个快速的CPU变成在爬行.
有集成外设的CPU很不错,因为很少有硬件需要调试.而且,为支持主流CPU,它们的驱动程序经常是可用的.不过在我以往的工程中,这些芯片对外设的组合好象总是不合理或者缺少我们期望的性能(功能);同样,正因为这些外设被集成在一起,所以不要设想这会是一个最低价的(硬件平台)方案.
把10磅的Linux塞进5磅的口袋
对Linux的一个通常观点是,它由于太大而不宜用作嵌入式系统.这种观点不一定是真实的.面向PC机的Linux典型发布版有很多你根本用不上的功能特征甚至也超过了一个真正PC用户的需求.
首先,我们要把内核和各种任务分开来.标准的Linux内核总是长驻内存的.每一个要运行的应用程序需要从磁盘中装载到内存中,并在那里被执行.当该程序运行结束,它占用的内存被释放掉,也就是说,该程序被卸载掉.在一个嵌入式系统中,可能不存在一个磁盘.因系统的复杂程度和硬件的不同设计,有两种方法可用以摆脱对一个磁盘的依赖性.在一个简单的系统中,当系统启动后,内核和各种应用进程均驻留在内存中--这是大多数传统嵌入式系统的工作方式.Linux也支持这种方式.
使用Linux, 出现了另外一种方式.既然Linux有装载和卸载程序的能力,一个嵌入式系统可以利用这一点以节省RAM.考虑一个比较典型的系统:有大约8兆到16兆的闪存和8兆RAM;那么闪存可以被用作文件系统.用闪存驱动程序作为从闪存到文件系统的界面.作为一种选择,也可以用一个闪存磁盘.这是用闪存来摆脱系统对一个磁盘的需求(依赖).使用这种方式的一个例子是M-System(http://www.m-systems.com/)中的DiskOnChip技术,它可以支持160MB.所有应用程序以文件的形式被存放在闪存文件系统中并在必要的时候被装载到内存中;这种"用到时才装载"的能力是一个很强大的特征,从而可以很容易地具有(支持)以下各种能力:
1: 允许系统启动后抛弃(释放)那些初始化代码(空间).典型地,Linux用到很多运行在内核外部的工具性程序(utilities).它们通常仅在系统初始化期间运行一次,以后将再也用不到.而且,它们以互斥的方式,一个接一个地依序运行.这样,随着系统的启动,一段内存可以被反复地使用:"以页调入"的方式运载每一个程序.这可以很好地节省内存,特别是处理像网络栈那样的对象时,因为这些对象一经配置便永不更变.
2: 如果Linux之支持可动态装卸模块的特征被包含在内核之中,不仅各种应用程序,驱动程序也可被动态装卸.这样,系统能够检查硬件环境并有选择性地仅仅装载那些适应当前硬件环境的软件.这可以取消(降低)让一个程序以占用更多闪存的代价处理众多硬件变数的复杂性.
3: 系统的升级更加标准化(模块化),你可以常常在系统运行过程中升级一些应用和驱动程序.
4: 配置信息和运行时间参数可作为数据文件存放在闪存中.
无虚拟内存交换
另外一个Linux的特性就是虚拟内存交换。这一特性将应用程序的编写引入歧途,应用程序的内存需求量可以无限制地上升,因为操作系统在磁盘中提供了交换空间。而在一个无盘的嵌入式系统中,这种特性就用不上了。
如此强大的功能,在嵌入式系统中竟无用武之地。事实上,在一个严格的实时系统中你可能并不需要这种特性,因为它会导致定时功能的失控。如同其它的嵌入式系统一样,软件的设计必须很紧凑,以适应较小的物理内存。
注意,这取决于你的CPU体系,比较明智的做法是保留Linux的这段代码,毕竟,砍掉这些代码,还是要付出一些工作量的。但是,这些代码依然有保留的理由。因为它们能够支持代码共享,多个进程可以共享某一软件的同一拷贝。如果无此功能,那么,每一个程序都必须拥有库程序的独立拷贝,例如:printf 。
将交换空间大小简单地置为零,就可以关掉系统虚拟内存的分页交换机制了。当你的程序要求内存大于实际的内存时,系统的表现就如同交换空间溢出时一样:你的程序不会被加载,或者,当要求过多内存时,malloc 调用失败。
在许多的CPU体系中,虚存机制提供的内存管理可使进程的地址空间相互隔离。一般在嵌入式系统中不是这样,地址空间是简单的,平坦的情况。Linux的虚存机制使出错的进程不致影响整个系统。在许多嵌入式系统中,因为效率原因而设置的全局数据,同时被几个进程所共享,这在Linux中也存在,那就是内存共享,它可以经过设置,使某一段内存成为共享内存。
文件系统
许多嵌入式系统不存在一个磁盘或者一个文件系统。没有它们中的任何一个,Linux也可以运行。正如前面提到的那样,应用任务可以随同内核一起被编译并在启动时作为一个映像被加载.对简单系统来说,这已经胜任.不过,它却缺少前面描述到的各种灵活性.
事实上,如果你观察过许多商业性嵌入式系统,你会发现他们把文件系统作为可选项来提供.大部分要么是一个私人拥有的(专门)文件系统,要么是一个与MS-DOS兼容的文件系统.Linux不但支持许多其它文件系统也支持MS-DOS兼容的文件系统,通常推荐使用除MS-DOS兼容文件系统以外的其它文件系统,因为它们有较优的健壮性和容错性.Linux也有检查和修复工具(不过商业卖主一般不提供),这对从网络上进行更新的闪存系统尤为重要.如果系统在升级中断电,可能导致系统无法再用.一个修复工具通常可以排解此类问题.
文件系统可以放在一个传统磁盘驱动中,或闪存中,或任何可用的其它介质中.同样,一个小的RAM disk常常可以很好地存放暂时性文件(易失性文件).闪存被分隔成很多小块(并被组织起来).它们中可能有一个引导块,它存放了CPU上电后运行的第一个软件.可能存放的就是Linux的引导程序.余下的闪存块可以被用作文件系统.Linux的内核可以被引导程序从闪存中拷贝到RAM中;或者,作为另一种选择,可以把内核放在闪存的一个独立区中并从那里直接运行.
对一些系统来说,另一可行的选择是包含一个廉价的CD-ROM驱动.它可能比闪存还要便宜.并且借助更换CD-ROM盘片就可很容易地得到升级.通过这种方法,Linux只需从CD-ROM启动并可以像对一个硬盘那样从该CD-ROM中获得所有用到的程序.
最后,对于网络上的嵌入式系统(有网络支持的嵌入式系统),Linux支持NFS(Network File System).这一着打开了在一个网络支持系统中实现各种增值特征的通道.首先它允许通过网络加载各种应用程序.由于用在每一个嵌入式系统上的软件可以从一个公用的服务器上加载,这在控制软件的修订或升级中是很重要的.在系统运行的过程中,导入和导出数据,配置,状态信息的一个备份也很有用.对用户监控而言,这是一个非常强大的特征;举例来说,一个嵌入式系统可能装配了一个RAM disk,它包含着与系统当前状态的更新维持一致的(状态)文件.那么别的嵌入式系统仅需通过网络把这个RAM disk作为远程磁盘mount过来便可以访问那些位于远端RAM disk中的状态文件.这也允许在另一台机器上的WEB服务器借助简单的CGI脚本来访问那些状态信息.运行在其他机器上的应用程序包能够很容易地访问这些数据.对更复杂的监控,像MatLab(http://www.mathworks.com/products/matlab/)这样的一个应用程序包能很容易地用图形化来显示在一个系统操作者的PC或工作站上的系统操作.
引导内核--当没有LILO或BIOS时
当一个微处理器最初启动时,它首先执行在一个预定地址处的指令.通常这个位置是只读内存,其中存放着系统初始化或引导程序.在PC中,它就是BIOS.这些程序要执行低级的CPU初始化并配置其他硬件.BIOS接着判断出哪一个磁盘包含有操作系统,再把OS拷贝到RAM中,并把控制权交给OS.实际上,整个过程远非这么简单,不过对我们的理解已经足够.运行在PC上的Linux系统依赖于该PC的BIOS来提供这些配置和OS加载功能.
在一个嵌入式系统里,常常没有上述的BIOS;这样,你就需要去提供等价的启动代码.还好,一个嵌入式系统的BIOS并不需要像PC BIOS引导程序那样有那么多的灵活性,因为它通常仅需处理一种硬件配置方案.这些代码比较简单但也另人厌烦.它是一些把特定的数写入指定硬件寄存器的指令序列.不过这是很关键的代码,因为这些数值必须要符合你的硬件并且要按特定顺序来完成.在大多数情况下,(这些代码中)有一个最小化的加电自检模块用以检查内存,让一些LED闪现一下,也可能探测一些其它让Linux OS启动和运行的必要硬件.这些启动代码是高度硬件专用性的,因而,不具移植性.
有幸的是,大多数系统为核心微处理器和内存使用了食谱式的硬件设计.典型地,芯片制造商有一个可供设计参考的演示板--新设计多少可以从中直接拷贝一些.对这些食谱式的设计,经常有现成的启动代码可用,而且可以很容易地被修改以适应你的需要.很少(会遇到)有需要从头开始编写的启动代码.
为了测试你的(启动)代码,你可以使用一个包含它自己的仿真内存的电子仿真器(in-circuit emulator),这里的仿真内存用以替换目标内存.你把待测代码加载到仿真器中并通过仿真器调试它.如果没有可用的仿真器,也可以跳过这一步,不过需要一个较长的调试周期.
这些代码最终要从非易失性存储器中运行,通常是用闪存或EPROM芯片,你要想办法把这些代码放进前述芯片中,放入的具体方法依赖于"目标"硬件和工具.一个常见的方法是把闪存或EPROM芯片插入到一个EPROM或闪存"烧炉"中.这种方法将把你的程序"烧入"(存入)芯片中.然后,把芯片插入到你目标板上的一个插槽中,打开电源.这种方法要求在板子上具有插槽化(socketed)部分;然而,有些设备包格式(结构)不允许被插槽化(socketed).
另一种办法是通过一个JTAG接口.一些芯片包含一个JTAG接口,从而可以对芯片编程.这是一种最简便的办法.芯片可以被永久地焊接到板子上.一段电缆从板子上的JTAG连接器(通常是一个PC卡)连接着一个JTAG接口.接下来要求在操纵JTAG接口的PC上做一些用户定制性编程.在仅需较少运行量的产品中,也可以使用这种方法.
健壮性---比政客的承诺还可靠
这是显而易见的,作为一种选择,Linux已被普遍地认为能够在PC平台上可靠地,稳定地运行。嵌入式内核自身有多稳定?对于大多数微处理器来说,Linux是很好用的。将Linux移植到新的微处理器体系也是非常迅捷的。一般是将其移植到一种新型的目标板,这种新型的目标板包含有独特的外设,当然还有CPU.
幸运的是,大部分的内核代码都是相同的,因为它们与微处理器无关,所以,移植的工作都集中在那些不同的部分,通常是一些存储器管理及中断处理程序。一旦完成,它们往往是非常稳定。如同前面谈到的,引导的过程非常依赖于硬件的变化而变化,所以,一定要周密地计划一番。
设备驱动程序虽然变化多端,但其中一些已相当稳定。同时,你的选择也不算太多,一旦离开PC平台,那就只有自己去写了。所幸的是,在我们周围已有许多既有的设备驱动程序,你总能找到一个近似的,而去修改它。驱动程序的接口是明确定义的。大多数驱动程序之间都是相似的,所以,移植一个磁盘,网络,串行口驱动程序,从一个设备到另一种,经常不是太难。我发现大多数的驱动程序都写的很好,并很好理解,而难题反而是在我手上的那本讲解内核结构的书。
就我个人的经验而言,Linux与我曾经使用过的那些顶顶大名的商业操作系统一样稳定。总的来说,关于这些操作系统(包括Linux)的问题都源于对系统工作策略的误解,而不是纯代码bug或基本设计错误。大量的操作系统的bug故事,在这里没有必要再重提。而Linux的优点,就在于其源码是公开的,并有很好的注释和完整的文档说明,从而,你也就拥有了控制与解决一切问题的能力基础。
即内核与驱动程序之后,还有一点:如果一个系统中有一只硬盘,那么,系统的可靠性可能会成为问题。我们拥有两年的经验和时间,都沉浸在有盘嵌入式系统中。这些系统从不关机,电源有几次被意外地断掉,可是,EXT2文件系统表现得很好。标准的Linux初始化脚本运行了fsck程序。这个程序做了一个漂亮的检查与清理不正常节点的工作。还有一处英明之举,也是一处较大的变动,我们将update程序由30秒执行一次,调整为5或10秒执行一次。这使得磁盘高速缓存中的数据能较频繁地写回到磁盘里,从而,降低了丢失数据的可能性。
没有万能钥匙
嵌入式Linux当然有它的不足。比如,它很占内存,尽管不比一些商业竞争者的情况更坏。但可以通过消减一些不必要的功能来改善,也有可能得不偿失,因为很可能会产生比较严重的bug。
大多数Linux应用程序都会使用虚拟内存交换,这在很多嵌入式系统中是一种非确定因素,所以,不要假定任何一个无盘嵌入式系统能够运行什么Linux应用程序。
低等级的,内核级的调试工具仍然不是很好使。kgdb会使人感到很不适应,你常要reboot。很不幸,调试仍然是以打印语句为主。
对于我来说最严重的问题是心理作用。Linux是一种极具适用性的操作系统。可是,嵌入式系统在通常情况下是不具备这种性质的;它们是对于特定的用途,进行过仔细优化了。但Linux的这种适用性倾向,保持了系统的通用性和多变性,同时也是一个奢侈的目标,付出的代价很高,需要增添许多额外的工作,会有许多附加的程序产生,从而增加了软件包的体积,有时还会以降低性能为代价。举一个常提到的例子--配置程序:在一个网络接口上配置IP地址,一般是由启动脚本中的ifconfig完成的。这是一个大小约28K左右程序,其实只用几行代码就可以完成它的工作:它负责根据配置文件中的内容初始化一些相应的数据结构。事情就是这样:当一切成为一件合理的事情去做时,你不这样做,又能怎样呢?不然"软件"意义又何在呢。
结束语
在嵌入式系统中使用Linux是可能的,并且已经成功了。它可以可靠地工作。从此,我们就在开发的成本上有了第二种选择。
php爱好者站 http://www.phpfans.net Linux|Apache|IIS.
嵌入式系统 ---- 比摩西还老
那些用以控制设备的计算机,或叫嵌入式系统,差不多同计算机本身一样早地出现在我们的周围。
在通信领域中,这些嵌入式系统早在20世纪60年代后期就被用来控制电话的电子式机械交换并被称为“存储程控控制“系统“计算机”一词在那时尚不常见;所谓的存储程序是指那些放有程序和路由信息的内存。存储这些控制逻辑而不是用硬件来实现是在观念上的一种真正突破,现今,我们早认为这种工作机理是理所当然的了。
为适应每一个应用,这些计算机是被定做出来的(简言之,这些计算机是面向应用的).按今天的标准来看,他们有着奇怪的专用指令以及与主要计算引擎集成在一起的I/O设备,就像一批突变异种者.
微处理器通过提供一个小巧低价的并可以在大系统中像搭积木那样使用的CPU引擎改变了这一情况;它利用一基于被一条总线挂接在一起的不同外设所构建的严格的硬件体系结构并提供一个可以简化编程的通用目的编程模型.
同硬件一起,软件也得到了发展.最初,只有一些简单的开发工具可供用以创建和调试软件.各工程项目的运行软件通常以信手涂鸦的方式编出来.由于编译器经常有很多错误而且也缺乏象样的调试器,这些软件差不多总是用汇编语言或宏语言来写.采用软件构建块和标准库的编程思想直到20世纪70年代中期才流行起来.
用于嵌入式系统的与"搁架"无关的操作系统(OS)在20世纪70年代后期开始出现.它们中的许多是用汇编语言写就的并且仅能用于为其编写的微处理器上.当这些微处理器变得过时的时候,它们使用的OS也厄运同临.只能在新的处理器上从新写一遍才能运行.今天,许多这种早期的系统只不过成了人们模糊的记忆,还有人能记起MTOS吗?当C语言出现后,OS可以用一种高效的,稳定的和可移植的方式来编写.这种方式对使用和经营有直接的吸引力,因为它承载着人们当微处理器废弃不用时能保护他们的软件投资的希望.听起来,有点儿像商业市场营销中的一段传奇故事.用C来编写OS已经成了一种标准直至今天.总之,软件的可复用性已经为人接受而且正在很好地发挥作用.
在20世纪80年代早期,我特别喜爱的OS是Wendon操作系统;大概只要150美金,就可以获得它的C源代码库.它是一个开发套件,人们可以通过选择一些组件来构建自己的OS---整个过程就像是从中餐菜单里订餐一样.比如,可以从库中的多个可行选项列表中精选出一种任务调度算法和内存管理方案.
许多用于嵌入式系统的的商业操作系统在20世纪80年代获得了蓬勃发展.(Wendon)这一原始的炖菜已经发展成为了商业操作系统这一现代炖肉.今天已经有几打的商业性操作系统可供选择.出现了许多互相竞争的产品,如VxWorks,pSOS,Neculeus和WindowsCE.
许多嵌入式系统根本就没有操作系统,只不过有一个控制环而已.对很简单的嵌入式系统来说,这可能已经足够.不过,随着嵌入式系统在复杂性上的增长,一个操作系统显得重要起来,因为否则的话,将使(控制)软件复杂度变得极不合理.可悲的是,现实中确实有一些复杂得另人生畏的嵌入式系统,而且它们之所以变得复杂就因为它们的设计者坚持认为它们的系统不需要操作系统.
渐渐地,更多的嵌入式系统需要被连接到某些网络上,因而,需要在嵌入式系统中有网络协议栈(支持);甚至很多宾馆中的门把手都有一个连接到网络的微处理器.
把网络栈添加到一个仅用控制环来实现的简单嵌入式系统所带来的复杂程度可能足以唤起人们对一个操作系统的渴望.
除了各种商业性操作系统以外,还有多种私拥操作系统.其中,有很多是涂鸦式写就的,像Cisco公司的IOS等.有些则源于对别的操作系统的改写,像很多网络产品都衍生于同一版本的伯克利UNIX操作系统,因为后者有完整的网络支持能力;而还有一些则基于公共域OS,比如KA9Q就来源于PhilKarn.
作为侯选的嵌入式操作系统,LINUX有一些引人的优势:它可以移植到多个有不同结构的CPU和硬件平台上,很好的稳定性,各种性能的升级能力,而且开发更容易.。
开发工具--打破了传统仿真器的阻碍
在开发嵌入式系统中极为关键的一项是有各种可用的工具.就像任何一个行当一样,好的工具有助于快捷而圆满地完成任务;在嵌入式系统开发的不同阶段,可能要用到不同的工具.
传统上,开发嵌入式系统的首选工具是仿真器。这是一块比较昂贵的设备,一般插于微处理器和它的总线之间的电路中,从而让开发者监视和控制所有输入和输出 微处理器的各种活动和行为.在装配起来,可能有一些困难,并且由于它们的侵入性,装上后可能造成不稳定 的性能;尽管这样,它们却能在总线级上给出一个系统正在发生什么的清晰的描绘并排除了很多在硬件和软件 接口最底层上的猜测工作.
在以往,一些工程项目依赖它--经常在开发周期中的各个阶段--作为主要的调试工具.不过,一旦当编制的软件 有能力支持一个串型口的时候,大量的调试可以不用ICE而使用别的方法来完成.同样,大部分新一代的嵌入式系统 采用蛮像食谱式的微处理器设计;通信工作的启动代码常常是具备的以使串型口尽快地工作,这意味着开发者能 在没有ICE的情况下也能很好地进展;去掉了ICE,从而降低了开发成本.一旦串型口可以工作起来,便能用于支持那 些日渐复杂的开发工具的相关(软件)层
LINUX基于GNU C 编译器;后者作为GNU工具集的一组成部分,和源码级调试器gdb一起工作,提供了在开发一个嵌入式 LINUX系统中要用到的所有软件工具.下面是在为一个新的硬件开发一个新的嵌入式LINUX系统时要用到的典型调试 工具的序列和步骤:
1:写出或移植一段启动代码(后面再详细讨论);
2:写一段代码在串型口上输出一字符串,像"Hello,World!"(其实,我更喜欢人类发明电话后,通过电话说的第一句话"Watson, come here I need you");
3:移植gdb目标码使之能在串型口上工作.这将允许向另一台正运行着gdb程序的LINUX主机会话;你只不过要告诉 gdb是通过串型口调试该目标程序;gdb通过串型口与你的测试计算机上的gdb目标码会话并给出全部C源码级的 调试信息.你也可以利用这一(通信)能力把附加的代码下载到RAM或闪存中.
4:借助gdb,执行余下的直到LINUX内核开始接管之前的所有硬件和软件的初始化代码.
5: 一旦LINUX内核启动后,上述的串型口就成为LINUX的控制台端口并可以利用它的便利来进行后继开发过程.再使用gdb的内核调试版本kgdb.这一步常常不是必需的.如果你有一个网络连接,比如,10BaseT,你或许会想让它紧接着工作起来.
6: 如果你的目标平台运行的Linux kernel是全功能的(即:未经删减过功能),你可以利用gdb或其图形化替代品如xgdb去调试你的应用进程.
实时--真是这样吗?
轻率,大部分的系统并不是如此。嵌入式系统经常被错误地说做实时系统,但是它们中的大多数并不具备实时特性。实时性仅仅是相对的。实时严谨地定义应为硬实时:能在极短的时间(毫秒级)内响应,并以某种确定的方式处理事件。现在,许多硬实时功能正逐渐集中在DSP或ASIC的设计中,通过一些适当的硬件,如FIFO,DMA或其它专用硬件来实现。
对大多数系统来说,有1到5毫秒的实时响应时间应足够了。当然,另一种宽松的要求也是可以接受的,例如:
Windows 98的处理监视器崩溃画面的中断,要求必须在4微秒之内处理的,占所有情况的98%;而在20微秒之内处理的,占各情况的100%.
这些宽松的实时要求可以很容易达到.实现它们过程中涉及到一些探讨,包括现场切换,中断延迟,任务选优和调度.
现场切换曾一度成为操作系统界的一个热点话题,不过,由于大多数CPU在这点上处理得比较令人满意而且CPU速度现在已经变得足够的快,现场切换现已不再是主要关注点了.
实时的严格性要求通常应由一个中断例程或内核中的现场驱动函数来处理以确保行为的一致性.当中断发生后,处理该中断所用的时间,即中断延迟,在很大程度上,由中断优先级与其它能临时屏蔽该中断的软件决定.
(实时系统中的)中断必须应被高效地设计和安排以确保满足时间上的要求,就像在其它OS中那样.在Intel X86处理器系列中,这项工作可以能被扩充了实时性的Linux很好地处理.(实时Linux,即:RTLinux,参看http://www.rtlinux.org/).从本质上说,它提供了一个把Linux作为其后台任务而运行的中断处理调度器.(This essentially provides an interrupt processing scheduler that runs Linux as its background task)一些关键(临界)中断可以不为Linux其他部分所知地得到服务(处理),因而,你就有了对临界时间的控制权.这种做法提供了实时级别和时间限制性较为宽松的基本Linux级别之间的界面.并提供了一个与别的嵌入式操作系统类似的实时处理框架.从根本上讲,为满足实时性要求,采用了把实时性的关键(临界)代码段隔离开来并进行高效的安排,然后对该段代码的处理结果再以更一般的方式(或许在进程级别上)来做进一步处理.
嵌入式系统--定义
一种观点认为: 如果某种应用没有用户界面,从而,用户不能直接地和它交互,那么它就是嵌入式系统.这当然太过简单化了.电梯控制系统是嵌入式系统,但却有一个用户界面: 选择楼层的按钮和显示电梯正到达几层的指示器. 对于那些连入网络的嵌入式系统,如果该系统包含一个用于监视和控制的web服务器,界面上的区别就更显模糊了.一个较好的定义应强调在该系统的重要功能或主要用途上.
由于Linux可以提供一个用以执行嵌入功能的基本内核以及各种你想要的用户界面元素,所以Linux有很强的通用特点.它能处理嵌入性任务和有关用户界面两方面的作业.可以把Linux看作如下的一个连续体:
从一个只有内存管理,任务调度,定时器服务的缩简的微内核到一个支持各种文件系统和多种网络服务的完整服务器.
一个最小的嵌入式系统仅需如下基本组成部分:
1 : 一个用作引导的可用设施(工具);
2 : 一个具备内存管理,进程管理,和定时器服务的Linux微内核;
3 : 一个初始进程;
为让上面的最小嵌入式系统变得有一定实用性,尚需加上一些东西:
1 : 硬件的驱动程序;
2 : 一个或几个应用进程以提供必要的应用功效.
随着对系统要求的增加,也许还要用到下面这些组件:
1 : 一个文件系统(或许放在ROM或RAM中);
2 : TCP/IP 网络协议栈;
3 : 一个磁盘用来存放半易失性数据和提供交换能力.
硬件平台
挑选最好的硬件是一项很复杂的工作,充满着各种顾忌和干扰:公司的政策,各种成(偏)见,其它工程的影响以及缺乏完整或准确的信息.
成本经常是一个关键性因素.当你着眼成本时,一定要考虑产品的整体成本,而不要只看到CPU;有时一个快速而廉价的CPU可能成了这个产品的耗价的问题根源,一旦当你加上总线和延迟逻辑以便同你的各种外设协同工作的时候.如果你是一个仅管听命的软件员,那么你就只能对早已作好的决定撞运气了,如果你是系统的设计者,就需要尽力制订一个合理的预算并且所选用的硬件要能令你满意地处理实时任务.
从实际中观察CPU到底需要多快方能把工作做好,然后把这个速度乘3(才是系统将所要的CPU速度);因为CPU在理论上所能达到的能力到了现实中总是难以置信地大打折扣!不要忘记缓存对你的系统的影响.
也要计算出总线需要运行多快,如果有二级总线(像一条PCI总线那样),也要把它们包含进来;一条慢的或过多参与DMA传输的总线能够让一个快速的CPU变成在爬行.
有集成外设的CPU很不错,因为很少有硬件需要调试.而且,为支持主流CPU,它们的驱动程序经常是可用的.不过在我以往的工程中,这些芯片对外设的组合好象总是不合理或者缺少我们期望的性能(功能);同样,正因为这些外设被集成在一起,所以不要设想这会是一个最低价的(硬件平台)方案.
把10磅的Linux塞进5磅的口袋
对Linux的一个通常观点是,它由于太大而不宜用作嵌入式系统.这种观点不一定是真实的.面向PC机的Linux典型发布版有很多你根本用不上的功能特征甚至也超过了一个真正PC用户的需求.
首先,我们要把内核和各种任务分开来.标准的Linux内核总是长驻内存的.每一个要运行的应用程序需要从磁盘中装载到内存中,并在那里被执行.当该程序运行结束,它占用的内存被释放掉,也就是说,该程序被卸载掉.在一个嵌入式系统中,可能不存在一个磁盘.因系统的复杂程度和硬件的不同设计,有两种方法可用以摆脱对一个磁盘的依赖性.在一个简单的系统中,当系统启动后,内核和各种应用进程均驻留在内存中--这是大多数传统嵌入式系统的工作方式.Linux也支持这种方式.
使用Linux, 出现了另外一种方式.既然Linux有装载和卸载程序的能力,一个嵌入式系统可以利用这一点以节省RAM.考虑一个比较典型的系统:有大约8兆到16兆的闪存和8兆RAM;那么闪存可以被用作文件系统.用闪存驱动程序作为从闪存到文件系统的界面.作为一种选择,也可以用一个闪存磁盘.这是用闪存来摆脱系统对一个磁盘的需求(依赖).使用这种方式的一个例子是M-System(http://www.m-systems.com/)中的DiskOnChip技术,它可以支持160MB.所有应用程序以文件的形式被存放在闪存文件系统中并在必要的时候被装载到内存中;这种"用到时才装载"的能力是一个很强大的特征,从而可以很容易地具有(支持)以下各种能力:
1: 允许系统启动后抛弃(释放)那些初始化代码(空间).典型地,Linux用到很多运行在内核外部的工具性程序(utilities).它们通常仅在系统初始化期间运行一次,以后将再也用不到.而且,它们以互斥的方式,一个接一个地依序运行.这样,随着系统的启动,一段内存可以被反复地使用:"以页调入"的方式运载每一个程序.这可以很好地节省内存,特别是处理像网络栈那样的对象时,因为这些对象一经配置便永不更变.
2: 如果Linux之支持可动态装卸模块的特征被包含在内核之中,不仅各种应用程序,驱动程序也可被动态装卸.这样,系统能够检查硬件环境并有选择性地仅仅装载那些适应当前硬件环境的软件.这可以取消(降低)让一个程序以占用更多闪存的代价处理众多硬件变数的复杂性.
3: 系统的升级更加标准化(模块化),你可以常常在系统运行过程中升级一些应用和驱动程序.
4: 配置信息和运行时间参数可作为数据文件存放在闪存中.
无虚拟内存交换
另外一个Linux的特性就是虚拟内存交换。这一特性将应用程序的编写引入歧途,应用程序的内存需求量可以无限制地上升,因为操作系统在磁盘中提供了交换空间。而在一个无盘的嵌入式系统中,这种特性就用不上了。
如此强大的功能,在嵌入式系统中竟无用武之地。事实上,在一个严格的实时系统中你可能并不需要这种特性,因为它会导致定时功能的失控。如同其它的嵌入式系统一样,软件的设计必须很紧凑,以适应较小的物理内存。
注意,这取决于你的CPU体系,比较明智的做法是保留Linux的这段代码,毕竟,砍掉这些代码,还是要付出一些工作量的。但是,这些代码依然有保留的理由。因为它们能够支持代码共享,多个进程可以共享某一软件的同一拷贝。如果无此功能,那么,每一个程序都必须拥有库程序的独立拷贝,例如:printf 。
将交换空间大小简单地置为零,就可以关掉系统虚拟内存的分页交换机制了。当你的程序要求内存大于实际的内存时,系统的表现就如同交换空间溢出时一样:你的程序不会被加载,或者,当要求过多内存时,malloc 调用失败。
在许多的CPU体系中,虚存机制提供的内存管理可使进程的地址空间相互隔离。一般在嵌入式系统中不是这样,地址空间是简单的,平坦的情况。Linux的虚存机制使出错的进程不致影响整个系统。在许多嵌入式系统中,因为效率原因而设置的全局数据,同时被几个进程所共享,这在Linux中也存在,那就是内存共享,它可以经过设置,使某一段内存成为共享内存。
文件系统
许多嵌入式系统不存在一个磁盘或者一个文件系统。没有它们中的任何一个,Linux也可以运行。正如前面提到的那样,应用任务可以随同内核一起被编译并在启动时作为一个映像被加载.对简单系统来说,这已经胜任.不过,它却缺少前面描述到的各种灵活性.
事实上,如果你观察过许多商业性嵌入式系统,你会发现他们把文件系统作为可选项来提供.大部分要么是一个私人拥有的(专门)文件系统,要么是一个与MS-DOS兼容的文件系统.Linux不但支持许多其它文件系统也支持MS-DOS兼容的文件系统,通常推荐使用除MS-DOS兼容文件系统以外的其它文件系统,因为它们有较优的健壮性和容错性.Linux也有检查和修复工具(不过商业卖主一般不提供),这对从网络上进行更新的闪存系统尤为重要.如果系统在升级中断电,可能导致系统无法再用.一个修复工具通常可以排解此类问题.
文件系统可以放在一个传统磁盘驱动中,或闪存中,或任何可用的其它介质中.同样,一个小的RAM disk常常可以很好地存放暂时性文件(易失性文件).闪存被分隔成很多小块(并被组织起来).它们中可能有一个引导块,它存放了CPU上电后运行的第一个软件.可能存放的就是Linux的引导程序.余下的闪存块可以被用作文件系统.Linux的内核可以被引导程序从闪存中拷贝到RAM中;或者,作为另一种选择,可以把内核放在闪存的一个独立区中并从那里直接运行.
对一些系统来说,另一可行的选择是包含一个廉价的CD-ROM驱动.它可能比闪存还要便宜.并且借助更换CD-ROM盘片就可很容易地得到升级.通过这种方法,Linux只需从CD-ROM启动并可以像对一个硬盘那样从该CD-ROM中获得所有用到的程序.
最后,对于网络上的嵌入式系统(有网络支持的嵌入式系统),Linux支持NFS(Network File System).这一着打开了在一个网络支持系统中实现各种增值特征的通道.首先它允许通过网络加载各种应用程序.由于用在每一个嵌入式系统上的软件可以从一个公用的服务器上加载,这在控制软件的修订或升级中是很重要的.在系统运行的过程中,导入和导出数据,配置,状态信息的一个备份也很有用.对用户监控而言,这是一个非常强大的特征;举例来说,一个嵌入式系统可能装配了一个RAM disk,它包含着与系统当前状态的更新维持一致的(状态)文件.那么别的嵌入式系统仅需通过网络把这个RAM disk作为远程磁盘mount过来便可以访问那些位于远端RAM disk中的状态文件.这也允许在另一台机器上的WEB服务器借助简单的CGI脚本来访问那些状态信息.运行在其他机器上的应用程序包能够很容易地访问这些数据.对更复杂的监控,像MatLab(http://www.mathworks.com/products/matlab/)这样的一个应用程序包能很容易地用图形化来显示在一个系统操作者的PC或工作站上的系统操作.
引导内核--当没有LILO或BIOS时
当一个微处理器最初启动时,它首先执行在一个预定地址处的指令.通常这个位置是只读内存,其中存放着系统初始化或引导程序.在PC中,它就是BIOS.这些程序要执行低级的CPU初始化并配置其他硬件.BIOS接着判断出哪一个磁盘包含有操作系统,再把OS拷贝到RAM中,并把控制权交给OS.实际上,整个过程远非这么简单,不过对我们的理解已经足够.运行在PC上的Linux系统依赖于该PC的BIOS来提供这些配置和OS加载功能.
在一个嵌入式系统里,常常没有上述的BIOS;这样,你就需要去提供等价的启动代码.还好,一个嵌入式系统的BIOS并不需要像PC BIOS引导程序那样有那么多的灵活性,因为它通常仅需处理一种硬件配置方案.这些代码比较简单但也另人厌烦.它是一些把特定的数写入指定硬件寄存器的指令序列.不过这是很关键的代码,因为这些数值必须要符合你的硬件并且要按特定顺序来完成.在大多数情况下,(这些代码中)有一个最小化的加电自检模块用以检查内存,让一些LED闪现一下,也可能探测一些其它让Linux OS启动和运行的必要硬件.这些启动代码是高度硬件专用性的,因而,不具移植性.
有幸的是,大多数系统为核心微处理器和内存使用了食谱式的硬件设计.典型地,芯片制造商有一个可供设计参考的演示板--新设计多少可以从中直接拷贝一些.对这些食谱式的设计,经常有现成的启动代码可用,而且可以很容易地被修改以适应你的需要.很少(会遇到)有需要从头开始编写的启动代码.
为了测试你的(启动)代码,你可以使用一个包含它自己的仿真内存的电子仿真器(in-circuit emulator),这里的仿真内存用以替换目标内存.你把待测代码加载到仿真器中并通过仿真器调试它.如果没有可用的仿真器,也可以跳过这一步,不过需要一个较长的调试周期.
这些代码最终要从非易失性存储器中运行,通常是用闪存或EPROM芯片,你要想办法把这些代码放进前述芯片中,放入的具体方法依赖于"目标"硬件和工具.一个常见的方法是把闪存或EPROM芯片插入到一个EPROM或闪存"烧炉"中.这种方法将把你的程序"烧入"(存入)芯片中.然后,把芯片插入到你目标板上的一个插槽中,打开电源.这种方法要求在板子上具有插槽化(socketed)部分;然而,有些设备包格式(结构)不允许被插槽化(socketed).
另一种办法是通过一个JTAG接口.一些芯片包含一个JTAG接口,从而可以对芯片编程.这是一种最简便的办法.芯片可以被永久地焊接到板子上.一段电缆从板子上的JTAG连接器(通常是一个PC卡)连接着一个JTAG接口.接下来要求在操纵JTAG接口的PC上做一些用户定制性编程.在仅需较少运行量的产品中,也可以使用这种方法.
健壮性---比政客的承诺还可靠
这是显而易见的,作为一种选择,Linux已被普遍地认为能够在PC平台上可靠地,稳定地运行。嵌入式内核自身有多稳定?对于大多数微处理器来说,Linux是很好用的。将Linux移植到新的微处理器体系也是非常迅捷的。一般是将其移植到一种新型的目标板,这种新型的目标板包含有独特的外设,当然还有CPU.
幸运的是,大部分的内核代码都是相同的,因为它们与微处理器无关,所以,移植的工作都集中在那些不同的部分,通常是一些存储器管理及中断处理程序。一旦完成,它们往往是非常稳定。如同前面谈到的,引导的过程非常依赖于硬件的变化而变化,所以,一定要周密地计划一番。
设备驱动程序虽然变化多端,但其中一些已相当稳定。同时,你的选择也不算太多,一旦离开PC平台,那就只有自己去写了。所幸的是,在我们周围已有许多既有的设备驱动程序,你总能找到一个近似的,而去修改它。驱动程序的接口是明确定义的。大多数驱动程序之间都是相似的,所以,移植一个磁盘,网络,串行口驱动程序,从一个设备到另一种,经常不是太难。我发现大多数的驱动程序都写的很好,并很好理解,而难题反而是在我手上的那本讲解内核结构的书。
就我个人的经验而言,Linux与我曾经使用过的那些顶顶大名的商业操作系统一样稳定。总的来说,关于这些操作系统(包括Linux)的问题都源于对系统工作策略的误解,而不是纯代码bug或基本设计错误。大量的操作系统的bug故事,在这里没有必要再重提。而Linux的优点,就在于其源码是公开的,并有很好的注释和完整的文档说明,从而,你也就拥有了控制与解决一切问题的能力基础。
即内核与驱动程序之后,还有一点:如果一个系统中有一只硬盘,那么,系统的可靠性可能会成为问题。我们拥有两年的经验和时间,都沉浸在有盘嵌入式系统中。这些系统从不关机,电源有几次被意外地断掉,可是,EXT2文件系统表现得很好。标准的Linux初始化脚本运行了fsck程序。这个程序做了一个漂亮的检查与清理不正常节点的工作。还有一处英明之举,也是一处较大的变动,我们将update程序由30秒执行一次,调整为5或10秒执行一次。这使得磁盘高速缓存中的数据能较频繁地写回到磁盘里,从而,降低了丢失数据的可能性。
没有万能钥匙
嵌入式Linux当然有它的不足。比如,它很占内存,尽管不比一些商业竞争者的情况更坏。但可以通过消减一些不必要的功能来改善,也有可能得不偿失,因为很可能会产生比较严重的bug。
大多数Linux应用程序都会使用虚拟内存交换,这在很多嵌入式系统中是一种非确定因素,所以,不要假定任何一个无盘嵌入式系统能够运行什么Linux应用程序。
低等级的,内核级的调试工具仍然不是很好使。kgdb会使人感到很不适应,你常要reboot。很不幸,调试仍然是以打印语句为主。
对于我来说最严重的问题是心理作用。Linux是一种极具适用性的操作系统。可是,嵌入式系统在通常情况下是不具备这种性质的;它们是对于特定的用途,进行过仔细优化了。但Linux的这种适用性倾向,保持了系统的通用性和多变性,同时也是一个奢侈的目标,付出的代价很高,需要增添许多额外的工作,会有许多附加的程序产生,从而增加了软件包的体积,有时还会以降低性能为代价。举一个常提到的例子--配置程序:在一个网络接口上配置IP地址,一般是由启动脚本中的ifconfig完成的。这是一个大小约28K左右程序,其实只用几行代码就可以完成它的工作:它负责根据配置文件中的内容初始化一些相应的数据结构。事情就是这样:当一切成为一件合理的事情去做时,你不这样做,又能怎样呢?不然"软件"意义又何在呢。
结束语
在嵌入式系统中使用Linux是可能的,并且已经成功了。它可以可靠地工作。从此,我们就在开发的成本上有了第二种选择。
php爱好者站 http://www.phpfans.net Linux|Apache|IIS.
相关阅读 更多 +