make LEM

本文介绍了Linux在嵌入式系统中的应用。阐述了嵌入式系统的发展,分析了Linux用作嵌入式系统的优缺点,列举了不同类型的嵌入式Linux系统,还介绍了软件和硬件要求、相关硬件方案、实时应用等内容,最后说明了配置步骤。
Share And Enjoy : 自由-开放-协作-分享,期待大家更多的交流与分享, 也请大家不要在不同栏目重复发布同一论题,谢谢

嵌入式 Linux 应用:概述
sharkhuang - 2/23/2005 - 12:43

现在 Linux 广泛用于各类计算应用,不仅包括 IBM 的微型 Linux 腕表、手持设备(PDA 和蜂窝电话)、因特网装置、瘦客户机、防火墙、工业机器人和电话基础设施设备,甚至还包括了基于集群的超级计算机。让我们看一下 Linux 用作嵌入式系统需要提供哪些功能,以及它在目前可用的选择中最具吸引力的原因所在。

嵌入式系统的出现
用于控制设备的计算机,也叫做嵌入式系统,它的历史几乎和计算机自身的历史一样长。它们最初于六十年代晚期在通讯中被用于控制机电电话交换机。由于在过去 的十多年里,计算机产业不断朝着更小的系统方向发展,嵌入式系统也与之一起为这些小型机器提供了更多的功能。渐渐地,就需要把这些嵌入式系统连接到某种网 络上,因而也就产生了对网络栈的要求,这提高了系统的复杂程度并要求更多的存储器和接口,还有,您猜对了,操作系统的服务。

七十年代晚期出现了用作嵌入式系统的现成的操作系统,现在有许多可行的选择方案。其中,一些主要的竞争者开始崭露头角,比如,VxWorks、pSOS、Neculeus 和 Windows CE。

在嵌入式系统中使用 Linux 的优点和缺点
虽然大多数 Linux 系统运行在 PC 平台上,但 Linux 也可以作为嵌入式系统的可靠主力。Linux 流行的“back-to-basics”方法使得它的安装和管理比 UNIX 更加简单灵活,这对于那些 UNIX 专家们来说又是一个优点,他们已经因为 Linux 中有许多命令和编程接口同传统的 UNIX 一样而赏识它了。

典型的压缩包装 Linux 系统经过打包,在拥有硬盘和大容量内存的 PC 机上运行,嵌入式系统可不要这么高的配置。一个功能完备的 Linux 内核要求大约 1 MB 内存。而 Linux 微内核只占用其中很小一部分内存,包括虚拟内存和所有核心的操作系统功能在内,只需占用 Pentium CPU 系统的 100 K 内存。只要有 500 K 的内存,一个有网络栈和基本实用程序的完全的 Linux 系统就可以在一台 8 位总线(SX)的 Intel 386 微处理器上运行的很好了。由于内存要求常常是需要的应用所决定的,比如 Web 服务器或者 SNMP 代理,Linux 系统甚至可以仅使用 256 KB ROM 和 512 KB RAM 进行工作。因此它是一个瞄准嵌入式市场的轻量级操作系统。

与传统的实时操作系统相比(RTOS),采用象嵌入式 Linux 这样的开放源码的操作系统的另外一个好处是 Linux 开发团体看来会比 RTOS 的供应商更快地支持新的 IP 协议和其它协议。例如,用于 Linux 的设备驱动程序要比用于商业操作系统的设备驱动程序多,如网络接口卡(NIC)驱动程序以及并口和串口驱动程序。

闪存
快闪 RAM 内存是大多数 Palm 设备用来存储操作系统的专用的存储器。它具有允许操作系统升级的优点,还可以用于数字式蜂窝电话、数字式照相机、LAN 交换机、PC 卡、数字式机顶盒、嵌入式控制器和其它小型设备。嵌入式系统,如嵌入式 Linux,不要求有磁盘驱动器,尽管可能使用其它的内存组织方式。因此如果,打个比方,Linux 用完了闪存,它就可以将其中一部分作为只读的文件系统来存储额外的程序和静态数据。

核心 Linux 操作系统本身的微内核体系结构相当简单。网络和文件系统以模块形式置于微内核的上层。驱动程序和其它部件可在运行时作为可加载模块编译到或者是添加到内 核。这为构造定制的可嵌入系统提供了高度模块化的构件方法。而在典型情况下该系统需结合定制的驱动程序和应用程序以提供附加功能。

嵌入式系统也常常要求通用的功能,为了避免重复劳动,这些功能的实现运用了许多现成的程序和驱动程序,它们可以用于公共外设和应用。Linux 可以在外设范围广泛的多数微处理器上运行,并早已经有了现成的应用库。

Linux 用于嵌入式的因特网设备也是很合适的,原因是它支持多处理器系统,该特性使 Linux 具有了伸缩性。因而设计人员可以选择在双处理器系统上运行实时应用,提高整体的处理能力。例如,您可以在一个处理器运行 GUI,同时在另一个处理器上运行 Linux 系统。

在嵌入式系统上运行 Linux 的一个缺点是 Linux 体系提供实时性能需要添加实时软件模块。而这些模块运行的内核空间正是操作系统实现调度策略、硬件中断异常和执行程序的部分。由于这些实时软件模块是在内 核空间运行的,因此代码错误可能会破坏操作系统从而影响整个系统的可靠性,这对于实时应用将是一个非常严重的弱点。

另一方面,现成的 RTOS 完全是为实时性能而设计的,它通过在由用户而非系统级进程启动时分配给某个进程以高于其它进程的优先级的方式来实现可靠性。进程在操作系统看来就是在内存 里或硬盘驱动器上执行的程序。给他们指定进程 ID 或者数字标识符为的是让操作系统跟踪正在执行的程序和这些程序的相关联的优先等级。这样的方式保证了 RTOS 时间能比 Linux 提供更高的可靠性(可预见性)。但最重要的,这还是一种更加经济的选择。

不同类型的嵌入式 Linux 系统
已经有许多嵌入式 Linux 系统的示例;可以有把握地说,某种形式的 Linux 能在几乎任一台执行代码的计算机上运行。例如,ELKS(可嵌入 Linux 内核子集)方案计划在 Palm Pilot 上使用 Linux。下面列出了一些更加广为人知的小型嵌入式 Linux 版本:

ETLinux — 设计用于在小型工业计算机,尤其是 PC/104 模块上运行的 Linux 的完全分发版。

LEM — 运行在 386 上的小型(<8 MB)多用户、网络 Linux 版本。

LOAF — “Linux On A Floppy”分发版,运行在 386 上。

uClinux — 在没有 MMU 的系统上运行的 Linux。目前支持 Motorola 68K、MCF5206 和 MCF5207 ColdFire 微处理器。

uLinux — 在 386 上运行的 tiny Linux 分发版。

ThinLinux — 面向专用的照相机服务器、X-10 控制器、MP3 播放器和其它类似的嵌入式应用的最小化的 Linux 分发版。

软件和硬件要求
许多的用户接口工具和程序增强了 Linux 基本内核的多功能性。就此而论,可以把 Linux 看作是这样一个连续范围,从只有存储器管理、任务转换和定时器服务最小化的微内核一直到完整的一系列文件系统和网络服务的功能完善的服务器。

最小的嵌入式 Linux 系统仅需要三个基本元素:

引导实用程序
Linux 微内核,由内存管理、进程管理和定时服务构成
初始化过程
要实现最低限度的工作能力,您还需要添加:

硬件驱动程序
一个或多个应用进程,以提供所需功能
随着要求的增加,您可能还需要:

一个文件系统(可能是在 ROM 或者是 RAM 里)
TCP/IP 网络栈
储存半瞬态数据和提供交换空间的磁盘
32 位内置 CPU(所有完全的 Linux 系统都需要)
相关的硬件方案
下面是一些现有的为 Linux 操作系统定制的嵌入式硬件方案。

PLEB:带有 ARM SA-1100 / ArmLinux Ucsimm / Uclinux Flash EPROM 的袖珍 Linux 嵌入式机器。

Linux Lab: Linux Lab 方案旨在帮助人们开发 Linux 数据采集和过程控制软件。它计划提供从硬件支持到应用开发的广阔范围内应用的标准化开发环境。

控制器域网:Linux GPIB 的控制器域网(CAN)总线驱动程序;Linux GPIB 包是一个对普通 GPIB(IEEE 488.1) 硬件的支持包。驱动程序支持 National Instruments AT-GPIB、TNT488.2 以及 PCII 和 PCIIa 板。这个包里有完整的开发环境,包括测试和配置工具、库以及对 tcl 和 python 语言的支持。

硬件平台选项
挑选最佳硬件的过程会相当复杂,问题起源于公司内部政策、成见、其它方案的遗留问题、缺乏全面的或者精确的信息以及成本 — 需考虑总的产品成本,而不仅仅是 CPU 本身。有时,一旦把 CPU 使用其它外围设备所必需的总线逻辑和延迟时间考虑在内,那么快速而廉价的 CPU 也可能变得昂贵。要计算任意给定的项目所需的 CPU 速度,首先要现实地看看为了完成一个给定的任务 CPU 得运行多快然后再乘以三。还要确定总线需要运行多快。如果还有二级总线,比如 PCI 总线,那么将它们也考虑在内。一条慢的总线(即一条被 DMA 通信阻塞的总线)将会显著降低高速 CPU 的速度。下面是一些嵌入式 Linux 应用的最佳硬件解决方案。

Bright Star Engineering:Bright Star Engineering 的 ipEngine-1 是支持嵌入式 Linux 的信用卡大小的单片机。它利用了基于 PowerPC 的 CPU,并提供了一组板上外设,有 Ethernet、LCD/视频控制器、USB、串口 I/O 以及一个 16K 门的可由用户配置的 FPGA。BSE 的嵌入式 Linux 配置允许 Linux 从 ipEngine 的板上 4MB 闪存中引导。

Calibri:CalibriTM-133 是将嵌入式 Linux 作为其操作系统来使用的网络设备,它方便使用、紧凑,并且可以用于多种用途。它为防火墙、VPN 和路由要求提供了一种高效、低成本的解决方案。

EmbeddedPlanet:EmbeddedPlanet 创造了后 PC 时代的计算机,它出现时就装有 MontaVista 的 HardHat Linux。由基于 PowerPC 的计算引擎和匹配的 I/O 卡驱动,Linux Planet 装在一个彩色的透明盒子里并且带有触摸屏,还可以访问数字及模拟 I/O。

Eurotech:Eurotech 提供了嵌入式 PC SBC 并资助了 ET-Linux,一个为在小型工业计算机上运行而专门设计的基于 glibc 2.1.2 的完全的 Linux 系统。

Microprocess Ingenierie:Microprocess 为产业和嵌入式市场开发、生产以及销售标准的和定制的产品。Microprocess 在实时软件方面活动范围遍及全球,并具有系统集成的专业知识。它的产品,比如 740 PowerPC compactPCI 板可以与标准的 Linux 分发版或者嵌入式 Linux 版本一起订购。

Moreton Bay:Moreton Bay 发布基于 Linux 的 Internet 路由器,其范围在 NETtel 2520 和 NETtel 2500 之间。这些小型的、易于连接的智能路由器解决方案设计旨在为平面网络提供简便、安全和价格适中的外部网友好的虚拟私有网络(VPN)。NETtel 路由器系列运行的是嵌入式 Linux 内核。现有一套开发工具能够把定制代码存在闪存中并在 NETtel 内部执行。代码可能含有特定的加密或者身份验证协议,或者在 NETtel 被用作远程控制设备代码时,会含有一些本地监视脚本。

Matrix Orbital:这是个可选的、但不是推荐的附加项。Matrix Orbital 生产的一系列串行 LCD 和 VFD 被许多 Linux 用户添加到了他们的嵌入式系统中。这条生产线的范围包括了 8x2 到 40x4 的字符 LCDs、20x2 和 20x4 的 VFD 加上 240x64 图形 LC(128x128 还在生产之中)。运用显示器的通信不是通过 RS232 就是通过 I2C 实现的,两者都是其所有模块上的标准。模块的 BIOS 中包含一个全面的命令集。

实时嵌入式 Linux 应用
有关嵌入式系统最重要的事务之一就是要求有一个实时操作系统。这里实时有好几种定义。对有些人来说,实时意味着在 1 微秒的时间内对事件作出反应,但对另外一些人来说,那就可能是 50 毫秒了。实时的硬度也各不相同。一些系统需要硬实时响应,在很短的时间内对事件作出确定性响应。但是,当我们对许多系统进行仔细分析时,我们发现事实上对 响应时间的要求只是接近实时。实时的要求常常是时间和缓冲空间的折衷。随着内存越来越便宜,CPU 速度越来越快,现在接近实时比硬实时更加常见,许多商用的所谓实时操作系统远非硬实时。通常情况下,当您进入这些系统的详细设计部分时,就需提高警惕必须 非常仔细地设计驱动器的中断和应用以满足实时要求。

RT-Linux(实时扩展的 Linux 系统)里含有时间紧要的函数可以用中断管理器来精确控制中断处理,从而很好地确保了关键性中断可以在需要时得到执行。这种方法的硬度主要取决于 CPU 中断结构和环境转换的硬件支持。这种方法可以满足广泛范围内的实时要求。即使没有实时扩展,Linux 也能很好地处理多个事件流。例如,运行于低端 Pentium 上的 Linux PC 系统能让多个 10BaseT 接口有效地执行,同时又以全速的 56KBPS 运行字符级串口,而不会丢失任何数据。

值得考虑的实时硬件和软件 Linux API 有 RTLinux、RTAI、EL 和 Linux-SRT。RTLinux 是一个最初在新墨西哥理工学院开发的硬实时 Linux API。RTAI(DIAPM)是由 Polytechnic Politecnico di Milano(DIAPM)航天工程部的程序员们开发的 RTLinux 实时 API 的副产品。EL/IX 是一个计划中的基于 POSIX 硬实时 Linux API,由 Red Hat 发起。Linux-SRT 是个实时 API 的软实时替代品,它可以使所有的 Linux 程序无需修改或者重新编译即可增强性能。

请参阅本文后面的参考资料部分,查找有关前面内容的资料和一些 Web 站点,那里提供了用于不同类型的标准 Linux 操作系统的软件扩展、开发工具、支持以及培训课程。

短暂的确定性响应时间
某些实时嵌入式系统需要迅速对外部事件作出响应,以完成一项特定任务。比如,嵌入一枚导弹的一个定制的微控制器在指引导弹瞄准它周围环境的一个特定目标之 前,需要迅速对诸如移动目标、天气和人等的外部事件作出迅速响应。短暂的确定性响应时间是指嵌入式系统可以确定它对外部事件作出响应的时间。

配置步骤
现在让我们来看一下如何 make LEM,它是一个小型的可嵌入 Linux 分发版,既提供网络又提供 X 服务器。您可以下载该分发版,尽管它并非必需。您需要一个完全的 Linux 分发版来建立自己的嵌入式 Linux 操作系统,其中将包括您所需要的一切(实用程序、源代码、编译器、调试器和文档)。下面是能用来 make LEM 的软件列表:

TinyLogin:TinyLogin 是一套 tiny UNIX 实用程序,它用于登录嵌入式系统、接受其验证身份、为其修改密码,并能维护其用户和用户组。为了增强系统安全性它还支持影子口令。正如它的名字所暗示的, TinyLogin 非常小,对嵌入式系统上的 BusyBox 是极好的补充。

BusyBox:BusyBox 是一个多调用的二进制文件,它提供了 POSIX 式的命令和专用函数的最小子集。它适合于非常小的嵌入式系统,比如引导磁盘等等。特别用

这个代码是什么意思 /* The main program. Parse the command line and do it... */ int main(int argc, char **argv){ static int version = 0; static int rpflag = 0; static int basisflag = 0; static int compress = 0; static int quiet = 0; static int statistics = 0; static int mhflag = 0; static int nolinenosflag = 0; static int noResort = 0; static int sqlFlag = 0; static int printPP = 0; static struct s_options options[] = { {OPT_FLAG, "b", (char*)&basisflag, 0, "Print only the basis in report."}, {OPT_FLAG, "c", (char*)&compress, 0, "Don't compress the action table."}, {OPT_FSTR, "d", 0, handle_d_option, "Output directory. Default '.'"}, {OPT_FSTR, "D", 0, handle_D_option, "Define an %ifdef macro."}, {OPT_FLAG, "E", (char*)&printPP, 0, "Print input file after preprocessing."}, {OPT_FSTR, "f", 0, 0, "Ignored. (Placeholder for -f compiler options.)"}, {OPT_FLAG, "g", (char*)&rpflag, 0, "Print grammar without actions."}, {OPT_FSTR, "I", 0, 0, "Ignored. (Placeholder for '-I' compiler options.)"}, {OPT_FLAG, "m", (char*)&mhflag, 0, "Output a makeheaders compatible file."}, {OPT_FLAG, "l", (char*)&nolinenosflag, 0, "Do not print #line statements."}, {OPT_FSTR, "O", 0, 0, "Ignored. (Placeholder for '-O' compiler options.)"}, {OPT_FLAG, "p", (char*)&showPrecedenceConflict, 0, "Show conflicts resolved by precedence rules"}, {OPT_FLAG, "q", (char*)&quiet, 0, "(Quiet) Don't print the report file."}, {OPT_FLAG, "r", (char*)&noResort, 0, "Do not sort or renumber states"}, {OPT_FLAG, "s", (char*)&statistics, 0, "Print parser stats to standard output."}, {OPT_FLAG, "S", (char*)&sqlFlag, 0, "Generate the *.sql file describing the parser tables."}, {OPT_FLAG, "x", (char*)&version, 0, "Print the version number."}, {OPT_FSTR, "T", 0, handle_T_option, "Specify a template file."}, {OPT_FSTR, "W", 0, 0, "Ignored. (Placeholder for '-W' compiler options.)"}, {OPT_FLAG,0,0,0,0} }; int i; int exitcode; struct lemon lem; struct rule *rp; (void)argc; OptInit(argv,options,stderr); if( version ){ printf("Lemon version 1.0\n"); exit(0); } if( OptNArgs()!=1 ){ fprintf(stderr,"Exactly one filename argument is required.\n"); exit(1); } memset(&lem, 0, sizeof(lem)); lem.errorcnt = 0; /* Initialize the machine */ Strsafe_init(); Symbol_init(); State_init(); lem.argv0 = argv[0]; lem.filename = OptArg(0); lem.basisflag = basisflag; lem.nolinenosflag = nolinenosflag; lem.printPreprocessed = printPP; Symbol_new("$"); /* Parse the input file */ Parse(&lem); if( lem.printPreprocessed || lem.errorcnt ) exit(lem.errorcnt); if( lem.nrule==0 ){ fprintf(stderr,"Empty grammar.\n"); exit(1); } lem.errsym = Symbol_find("error"); /* Count and index the symbols of the grammar */ Symbol_new("{default}"); lem.nsymbol = Symbol_count(); lem.symbols = Symbol_arrayof(); for(i=0; i<lem.nsymbol; i++) lem.symbols[i]->index = i; qsort(lem.symbols,lem.nsymbol,sizeof(struct symbol*), Symbolcmpp); for(i=0; i<lem.nsymbol; i++) lem.symbols[i]->index = i; while( lem.symbols[i-1]->type==MULTITERMINAL ){ i--; } assert( strcmp(lem.symbols[i-1]->name,"{default}")==0 ); lem.nsymbol = i - 1; for(i=1; ISUPPER(lem.symbols[i]->name[0]); i++); lem.nterminal = i; /* Assign sequential rule numbers. Start with 0. Put rules that have no ** reduce action C-code associated with them last, so that the switch() ** statement that selects reduction actions will have a smaller jump table. */ for(i=0, rp=lem.rule; rp; rp=rp->next){ rp->iRule = rp->code ? i++ : -1; } lem.nruleWithAction = i; for(rp=lem.rule; rp; rp=rp->next){ if( rp->iRule<0 ) rp->iRule = i++; } lem.startRule = lem.rule; lem.rule = Rule_sort(lem.rule); /* Generate a reprint of the grammar, if requested on the command line */ if( rpflag ){ Reprint(&lem); }else{ /* Initialize the size for all follow and first sets */ SetSize(lem.nterminal+1); /* Find the precedence for every production rule (that has one) */ FindRulePrecedences(&lem); /* Compute the lambda-nonterminals and the first-sets for every ** nonterminal */ FindFirstSets(&lem); /* Compute all LR(0) states. Also record follow-set propagation ** links so that the follow-set can be computed later */ lem.nstate = 0; FindStates(&lem); lem.sorted = State_arrayof(); /* Tie up loose ends on the propagation links */ FindLinks(&lem); /* Compute the follow set of every reducible configuration */ FindFollowSets(&lem); /* Compute the action tables */ FindActions(&lem); /* Compress the action tables */ if( compress==0 ) CompressTables(&lem); /* Reorder and renumber the states so that states with fewer choices ** occur at the end. This is an optimization that helps make the ** generated parser tables smaller. */ if( noResort==0 ) ResortStates(&lem); /* Generate a report of the parser generated. (the "y.output" file) */ if( !quiet ) ReportOutput(&lem); /* Generate the source code for the parser */ ReportTable(&lem, mhflag, sqlFlag); /* Produce a header file for use by the scanner. (This step is ** omitted if the "-m" option is used because makeheaders will ** generate the file for us.) */ if( !mhflag ) ReportHeader(&lem); } if( statistics ){ printf("Parser statistics:\n"); stats_line("terminal symbols", lem.nterminal); stats_line("non-terminal symbols", lem.nsymbol - lem.nterminal); stats_line("total symbols", lem.nsymbol); stats_line("rules", lem.nrule); stats_line("states", lem.nxstate); stats_line("conflicts", lem.nconflict); stats_line("action table entries", lem.nactiontab); stats_line("lookahead table entries", lem.nlookaheadtab); stats_line("total table size (bytes)", lem.tablesize); } if( lem.nconflict > 0 ){ fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict); } /* return 0 on success, 1 on failure. */ exitcode = ((lem.errorcnt > 0) || (lem.nconflict > 0)) ? 1 : 0; exit(exitcode); return (exitcode); }
最新发布
09-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值