嵌入式Linux开发

2019-07-13 05:25发布

嵌入式Linux开发和研究是Linux领域研究的一个热点,目前已开发成功的嵌入式系统有一半以上都是Linux。Linux到底有什么优势,使之取得如此辉煌的成绩呢?本文分为两大部分:Linux的优点、Linux开发。 一、Linux的优势: 广泛的硬件支持 Linux能够支持x86、ARM、MIPS、ALPHA、PowerPC等多种体系结构,目前已经成功移植到数十种硬件平台,几乎能够运行在所有 流行的CPU上。Linux有着异常丰富的驱动程序资源,支持各种主流硬件设备和最新硬件技术,甚至可以在没有存储管理单元(MMU)的处理器上运行,这 些都进一步促进了Linux在嵌入式系统中的应用。 内核高效稳定 Linux内核的高效和稳定已经在各个领域内得到了大量事实的验证,Linux的内核设计非常精巧,分成进程调度、内存管理、进程间通信、虚拟文件 系统和网络接口五大部分,其独特的模块机制可以根据用户的需要,实时地将某些模块插入到内核或从内核中移走。这些特性使得Linux系统内核可以裁剪得非 常小巧,很适合于嵌入式系统的需要。 开放源码,软件丰富 Linux是开放源代码的自由操作系统,它为用户提供了最大限度的自由度,由于嵌入式系统千差万别,往往需要针对具体的应用进行修改和优化,因而获 得源代码就变得至关重要了。Linux的软件资源十分丰富,每一种通用程序在Linux上几乎都可以找到,并且数量还在不断增加。在Linux上开发嵌入 式应用软件一般不用从头做起,而是可以选择一个类似的自由软件做为原型,在其上进行二次开发。 优秀的开发工具 开发嵌入式系统的关键是需要有一套完善的开发和调试工具。传统的嵌入式开发调试工具是在线仿真器(In-Circuit Emulator,ICE),它通过取代目标板的微处理器,给目标程序提供一个完整的仿真环境,从而使开发者能够非常清楚地了解到程序在目标板上的工作状 态,便于监视和调试程序。在线仿真器的价格非常昂贵,而且只适合做非常底层的调试,如果使用的是嵌入式Linux,一旦软硬件能够支持正常的串口功能时, 即使不用在线仿真器也可以很好地进行开发和调试工作,从而节省了一笔不小的开发费用。嵌入式Linux为开发者提供了一套完整的工具链(Tool Chain),它利用GNU的gcc做编译器,用gdb、kgdb、xgdb做调试工具,能够很方便地实现从操作系统到应用软件各个级别的调试。 完善的网络通信和文件管理机制 Linux至诞生之日起就与Internet密不可分,支持所有标准的Internet网络协议,并且很容易移植到嵌入式系统当中。此外,Linux还支持ext2、fat16、fat32、romfs等文件系统,这些都为开发嵌入式系统应用打下了很好的基础。 二、嵌入式Linux开发技术: 嵌入式系统是一种根据特定用途所专门开发的系统,它只完成预期要完成的功能,因此其开发过程和开发环境同传统的软件开发相比有着显著的不同。 1.开发流程 在嵌入式系统的应用开发中,整个系统的开发过程如图2所示: 图2 嵌入式系统的开发流程 嵌入式/Linux/开发
嵌入式系统发展到今天,对应于各种微处理器的硬件平台一般都是通用的、固定的、成熟的,这就大大减少了由硬件系统引入错误的机会。此外,由于嵌入式 操作系统屏蔽了底层硬件的复杂性,使得开发者通过操作系统提供的API函数就可以完成大部分工作,因此大大简化了开发过程,提高了系统的稳定性。嵌入式系 统的开发者现在已经从反复进行硬件平台设计的过程中解脱出来,从而可以将主要精力放在满足特定的需求上。 嵌入式系统通常是一个资源受限的系统,因此直接在嵌入式系统的硬件平台上编写软件比较困难,有时候甚至是不可能的。目前一般采用的解决办法是首先在通用计算机上编写程序,然后通过交叉编译生成目标平台上可以运行的二进制代码格式,最后再下载到目标平台上的特定位置上运行。 需要交叉开发环境(Cross Development Environment)的支持是嵌入式应用软件开发时的一个显著特点,交叉开发环境是指编译、链接和调试嵌入式应用软件的环境,它与运行嵌入式应用软件的环境有所不同,通常采用宿主机/目标机模式,如图3所示。 图3 交叉开发环境
嵌入式/Linux/开发 宿主机(Host)是一台通用计算机(如PC机或者工作站),它通过串口或者以太网接口与目标机通信。宿主机的软硬件资源比较丰富,不但包括功能强 大的操作系统(如Windows和Linux),而且还有各种各样优秀的开发工具(如WindRiver的Tornado、Microsoft的 Embedded Visual C++等),能够大大提高嵌入式应用软件的开发速度和效率。 目标机(Target)一般在嵌入式应用软件开发期间使用,用来区别与嵌入式系统通信的宿主机,它可以是嵌入式应用软件的实际运行环境,也可以是能 够替代实际运行环境的仿真系统,但软硬件资源通常都比较有限。嵌入式系统的交叉开发环境一般包括交叉编译器、交叉调试器和系统仿真器,其中交叉编译器用于 在宿主机上生成能在目标机上运行的代码,而交叉调试器和系统仿真器则用于在宿主机与目标机间完成嵌入式软件的调试。在采用宿主机/目标机模式开发嵌入式应 用软件时,首先利用宿主机上丰富的资源和良好的开发环境开发和仿真调试目标机上的软件,然后通过串口或者以网络将交叉编译生成的目标代码传输并装载到目标 机上,并在监控程序或者操作系统的支持下利用交叉调试器进行分析和调试,最后目标机在特定环境下脱离宿主机单独运行。 建立交叉开发环境是进行嵌入式软件开发的第一步,目前常用的交叉开发环境主要有开放和商业两种类型。开放的交叉开发环境的典型代表是GNU工具链、 目前已经能够支持x86、ARM、MIPS、PowerPC等多种处理器。商业的交叉开发环境则主要有Metrowerks CodeWarrior、ARM Software Development Toolkit、SDS Cross compiler、WindRiver Tornado、Microsoft Embedded Visual C++等。 2. 交叉编译和链接 在完成嵌入式软件的编码之后,需要进行编译和链接以生成可执行代码,由于开发过程大多是在使用Intel公司x86系列CPU的通用计算机上进行 的,而目标环境的处理器芯片却大多为ARM、MIPS、PowerPC、DragonBall等系列的微处理器,这就要求在建立好的交叉开发环境中进行交 叉编译和链接。 交叉编译器和交叉链接器是能够在宿主机上运行,并且能够生成在目标机上直接运行的二进制代码的编译器和链接器。例如在基于ARM体系结构的gcc交 叉开发环境中,arm-linux-gcc是交叉编译器,arm-linux-ld是交叉链接器。通常情况下,并不是每一种体系结构的嵌入式微处理器都只 对应于一种交叉编译器和交叉链接器,比如对于M68K体系结构的gcc交叉开发环境而言,就对应于多种不同的编译器和链接器。如果使用的是COFF格式的 可执行文件,那么在编译Linux内核时需要使用m68k-coff-gcc和m68k-coff-ld,而在编译应用程序时则需要使用m68k- coff-pic-gcc和m68k-coff-pic-ld。 嵌入式系统在链接过程中通常都要求使用较小的函数库,以便最后产生的可执行代码能够尽可能地小,因此实际运用时一般使用经过特殊处理的函数库。对于 嵌入式Linux系统来讲,功能越来越强、体积越来越大的C语言函数库glibc和数学函数库libm已经很难满足实际的需要,因此需要采用它们的精化版 本uClibc、uClibm和newlib等。 目前嵌入式的集成开发环境都支持交叉编译和交叉链接,如WindRiver Tornado和GNU工具链等,编写好的嵌入式软件经过交叉编译和交叉链接后通常会生成两种类型的可执行文件:用于调试的可执行文件和用于固化的可执行文件。 3. 交叉调试 嵌入式软件经过编译和链接后即进入调试阶段,调试是软件开发过程中必不可少的一个环节,嵌入式软件开发过程中的交叉调试与通用软件开发过程中的调试 方式有所差别。在通用软件开发中,调试器与被调试的程序往往运行在同一台计算机上,调试器是一个单独运行着的进程,它通过操作系统提供的调试接口来控制被 调试的进程。而在嵌入式软件开发中,调试时采用的是在宿主机和目标机之间进行的交叉调试,调试器仍然运行在宿主机的通用操作系统之上,但被调试的进程却是 运行在基于特定硬件平台的嵌入式操作系统中,调试器和被调试进程通过串口或者网络进行通信,调试器可以控制、访问被调试进程,读取被调试进程的当前状态, 并能够改变被调试进程的运行状态。 交叉调试(Cross Debug)又常常被称为远程调试(Remote Debug),是一种允许调试器以某种方式控制目标机上被调试进程的运行方式,并具有查看和修改目标机上内存单元、寄存器以及被调试进程中变量值等各种调 试功能的调试方式。一般而言,远程调试过程的结构如图4所示。 图4远程调试结构
嵌入式/Linux/开发 嵌入式系统的交叉调试有多种方法,可以被细分成不同的层次,但一般都具有如下一些典型特点: 调试器和被调试进程运行在不同的机器上,调试器运行在PC或者工作站上(宿主机),而被调试的进程则运行在各种专业调试板上(目标机)。 调试器通过某种通信方式与被调试进程建立联系,如串口、并口、网络、DBM、JTAG或者专用的通信方式。 在目标机上一般会具备某种形式的调试代理,它负责与调试器共同配合完成对目标机上运行着的进程的调试。这种调试代理可能是某些支持调试功能的硬件设备(如DBI 2000),也可能是某些专门的调试软件(如gdbserver)。 目标机可能是某种形式的系统仿真器,通过在宿主机上运行目标机的仿真软件,整个调试过程可以在一台计算机上运行。此时物理上虽然只有一台计算机,但逻辑上仍然存在着宿主机和目标机的区别。 在嵌入式软件开发过程中的调试方式有很多种,应根据实际的开发要求和条件进行选择。就调试方法而言,嵌入式系统的交叉调试可以分为硬件调试和软件调试两种,前者使用仿真调试器协助调试过程,而后者则使用软件调试器完成调试过程。 硬件调试 相对于软件调试而言,使用硬件调试器可以获得更强大的调试功能和更优秀的调试性能。硬件调试器的基本原理是通过仿真硬件的执行过程,让开发者在调试 时可以随时了解到系统的当前执行情况。目前嵌入式系统开发中最常用到的硬件调试器是ROM Monitor、ROM Emulator、In-Circuit Emulator和In-Circuit Debugger。 采用ROM Monitor方式进行交叉调试需要在宿主机上运行调试器,在目标机上运行ROM监视器(ROM Monitor)和被调试程序,宿主机通过调试器与目标机上的ROM监视器建立通信连接,它们之间的通信遵循远程调试协议。ROM监视器可以是一段运行在 目标机ROM上的可执行程序,也可以是一个专门的硬件调试设备,它负责监控目标机上被调试程序的运行情况,能够与宿主机端的调试器一同完成对应用程序的调 试。在使用这种调试方式时,被调试程序首先通过ROM监视器下载到目标机,然后在ROM监视器的监控下完成调试,目前使用的绝大部分ROM监视器能够完成 设置断点、单步执行、查看寄存器、修改内存空间等各项调试功能。 采用ROM Emulator方式进行交叉调试时需要使用ROM仿真器,它通常被插入到目标机上的ROM插槽中,专门用于仿真目标机上的ROM芯片。在使用这种调试方 式时,被调试程序首先下载到ROM仿真器中,它等效于下载到目标机的ROM芯片上,然后在ROM仿真器中完成对目标程序的调试。ROM Emulator调试方式通过使用一个ROM仿真器,虽然避免了每次修改程序后都必须重新烧写到目标机ROM中这一费时费力的操作,但由于ROM仿真器本 身比较昂贵,功能相对来讲又比较单一,因此只适应于某些特定场合。 采用In-Circuit Emulator(ICE)方式进行交叉调试时需要使用在线仿真器,它是仿照目标机上的CPU而专门设计的硬件,可以完全仿真处理器芯片的行为,并且提供 了非常丰富的调试功能。在使用在线仿真器进行调试的过程中,可以按顺序单步执行,也可以倒退执行,还可以实时查看所有需要的数据,从而给调试过程带来了很 多的便利。嵌入式系统应用的一个显著特点是与现实世界中的硬件直接相关,存在各种异变和事先未知的变化,从而给微处理器的指令执行带来各种不确定因素,这 种不确定性在目前情况下只有通过在线仿真器才有可能发现,因此尽管在线仿真器的价格非常昂贵,但仍然得到了非常广泛的应用。 采用In-Circuit Debugger(ICD)方式进行交叉调试时需要使用在线调试器。由于ICE的价格非常昂贵,并且每种CPU都需要一种与之对应的ICE,使得开发成本 非常高,一个比较好的解决办法是让CPU直接在其内部实现调试功能,并通过在开发板上引出的调试端口,发送调试命令和接收调试信息,完成调试过程。目前 Motorola公司提供的开发板上使用的是DBM调试端口,而ARM公司提供的开发板上使用的则是JTAG调试端口,使用合适的软件工具与这些调试端口 进行连接,可以获得与ICE类似的调试效果。 软件调试 软件调试通常要在不同的层次上进行,有时可能需要对嵌入式操作系统的内核进行调试,而有时可能仅仅只需要调试嵌入式应用程序就可以了。在嵌入式系统的整个开发过程中,不同层次上的软件调试需要使用不同的调试方法。 嵌入式操作系统的内核调试相对来讲比较困难,这是因为在内核中不便于增加一个调试器程序,而只能通过远程调试的方法,通过串口和操作系统内置的"调 试桩"(debug stub)进行通信,共同完成调试过程。调试桩可以看成是一个调试服务器,它通过操作系统获得一些必要的调试信息,并且负责处理宿主机发送来的调试命令。 具体到嵌入式Linux系统内核,调试时可以先在Linux内核中设置一个调试桩,用作调试过程中和宿主机之间的通信服务器,然后就可以在宿主机中通过调 试器的串口与调试桩进行通信,并通过调试器控制目标机上Linux内核的运行。 嵌入式应用软件的调试可以使用本地调试和远程调试两种方法,相对于操作系统的调试而言,这两种方式都比较简单。如果采用的是本地调试,首先要将所需 的调试器移植到目标系统中,然后就可以直接在目标机上运行调试器来调试应用程序了;如果采用的是远程调试,则需要移植一个调试服务器到目标系统中,并通过 它与宿主机上的调试器共同完成应用程序的调试。在嵌入式Linux系统的开发中,远程调试时目标机上使用的调试服务器通常是gdbserver,而宿主机 上使用的调试器则是gdb,两者相互配合共同完成调试过程。 3. 系统测试 嵌入式系统的硬件一般采用专门的测试仪器进行测试,而软件则需要有相关的测试技术和测试工具的支持,并要采用特定的测试策略。测试技术指的是软件测 试的专门途径,以及能够更加有效地运用这些途径的特定方法。在嵌入式软件测试中,常常要在基于目标机的测试和基于宿主机的测试之间做出折衷,基于目标机的 测试需要消耗较多的时间和经费,而基于宿主机的测试虽然代价较小,但毕竟是在仿真环境中进行的,因此难以完全反映软件运行时的实际情况。这两种环境下的测 试可以发现不同的软件缺陷,关键是要对目标机环境和宿主机环境下的测试内容进行合理取舍。 测试工具指的是那些能够用来辅助测试的工具,测试工具主要用来支持测试人员的测试工作,本身不能直接用来进行测试,测试工具一般都是通用工具,测试 人员应该根据实际情况对它们进行适当的调整。嵌入式软件测试中经常用到测试工具主要有内存分析工具、性能分析工具、覆盖分析工具、缺陷跟踪工具等。 内存分析工具 嵌入式系统的内存资源通常是受限的,内存分析工具可以用来处理在进行动态内存分配时产生的缺陷。当动态分配的内存被错误地引用时,产生的错误通常难 以再现,可出现的失效难以追踪,使用内存分析工具可以很好地检测出这类缺陷。目前常用的内存分析工具有软件和硬件两种,基于软件的内存分析工具可能会对代 码的执行性能带来很大影响,从而影响系统的实时性;基于硬件的内存分析工具价格昂贵,并且只能在特定的环境中使用。 性能分析工具 嵌入式系统的性能通常是一个非常关键的因素,开发人员一般需要对系统的某些关键代码进行优化来改进性能,而首先遇到的问题自然就是确定需要对哪些代 码进行优化。性能分析工具可以为开发人员提供有关的数据,说明执行时间是如何消耗的,是什么时候消耗的,以及每个进程所使用的时间。这些数据可以帮助确定 哪些进程消耗了过多的执行时间,从而可以决定如何优化软件,以获得更好的时间性能。此外,性能分析工具还可以引导开发人员发现在系统调用中存在的错误以及 程序结构上的缺陷。 覆盖分析工具 在进行白盒测试时,可以使用代码覆盖分析工具追踪哪些代码被执行过,分析过程一般通过插桩来完成,插桩可以是在测试环境中嵌入硬件,也可以是在可执 行代码中加入软件,或者是两者的结合。开发人员通过对分析结果进行总结,可以确定哪些代码被执行过,哪些代码被遗漏了。目前常用的覆盖分析工具一般都会提 供有关功能覆盖、分支覆盖、条件覆盖等信息。 本文讨论了嵌入式Linux系统的开发与一般通用计算机软件开发的不同点及应该注意的事项,这些都是今后在进行嵌入式Linux系统开发时必须具备的基础知识。