嵌入式 Linux 系统标准操作程序

作者:Chi-Hung Chou

开发嵌入式系统的流程非常复杂。新工程师通常需要很长时间才能熟悉这些流程。因此,我们开发了一套标准操作程序 (SOP),以节省构建嵌入式系统的成本并降低复杂性。SOP 包括构建基于 Linux 的嵌入式系统的五个标准流程,如图 1 所示。您可以按照本文中讨论的流程构建原型系统。此外,我们还介绍了十种用于缩小系统尺寸的实用方法。最后,我们展示了这些方法在缩小您的嵌入式系统(一种内容感知网络安全网关)尺寸方面的效果。

Standard Operating Procedures for Embedded Linux Systems

图 1. 构建和缩小嵌入式系统尺寸的流程和方法

五个标准流程

要构建嵌入式系统,第一步是选择目标平台。该平台涉及硬件和软件。硬件平台包括处理器、总线和 I/O;软件平台包括引导加载程序、内核和根文件系统。您必须仔细选择目标平台中的每个项目,以确保硬件和软件协同工作。例如,引导加载程序直接与硬件相关。如果选定的引导加载程序不支持您的硬件平台,则整个嵌入式系统将无法启动。此外,需要 MMU 的操作系统可能无法与支持 MMU 的处理器协同工作。

其次,除了目标平台之外,开发平台也是必要的。您无法在目标平台上编译嵌入式软件程序,因为目标平台通常具有较小的 RAM 和较慢的 CPU,以最大限度地降低成本和功耗。因此,您需要准备一个具有快速 CPU 和大 RAM 的开发平台来编译这些程序。此外,由于这两个平台具有不同的硬件架构,因此需要一个交叉编译环境。Buildroot 就是这样一个提供此环境的软件包。它具有友好的用户界面,可帮助您选择硬件平台和所需的软件包。通过使用 Buildroot,您可以轻松地为您的嵌入式系统生成交叉编译工具链和带有内置应用程序包的根文件系统。

设置好环境后,下一步是确定系统所需的软件包。您可以通过直接从 Buildroot 的 menuconfig 中选择内置软件包来完成此操作,也可以从 Internet 下载它们。实际上,Buildroot 提供了有用的软件包列表,例如 iproute2、freeswan 和 squid。Buildroot 还确保这些软件包可以成功链接到 uClibc,这是一个比 Glibc 更小的 C 库。如果找不到合适的软件包,您将不得不修改现有软件包或编写新的软件包。

获得所需的软件包后,下一步是将它们集成到嵌入式系统中。此处集成是指使用交叉编译器将源代码编译成可以在目标平台中执行的形式,然后将它们添加到根文件系统中。您可以通过 Buildroot 以三种方式将软件包添加到根文件系统中,如图 2 所示。方法 1 是直接从 Buildroot 中的选项中选择它们。如果软件包在 Buildroot 中不可用,您可能需要为软件包编写一个 makefile,以指示如何下载、配置、编译和安装软件包。此外,您需要修改 Buildroot 的 config.in 文件,以在配置菜单中显示软件包的选项。但是,如果您不想编写这些配置文件,则可以使用方法 2。在方法 2 中,您只需将编译后的软件包放置在名为 customize 的目录中,然后 Buildroot 会在构建过程中根据 customize.mk 中给出的规则将这些编译后的软件包复制到根文件系统中。但是,如果您只想验证单个软件包的功能,则无需重建整个映像。方法 3 中的步骤是将根文件系统挂载到任何一个目录上,然后将编译后的软件包复制到该目录中。最后,卸载该目录,您将获得一个更新的根映像。但是,如果挂载的文件系统中的可用空间不足以容纳新的软件包,则方法 3 可能会失败。在这种情况下,您可以调整 ext2root.mk 中给出的参数,以便在系统开发期间在根文件系统中保留更多可用空间。

Standard Operating Procedures for Embedded Linux Systems

图 2. 将软件包添加到根文件系统的三种方法

最后,图 2 描述了测试和验证软件包功能是否正常的两种方法。基本方法是将根文件系统下载到目标平台并直接执行软件包。但是,这样做通常需要很长时间。另一种方法是在虚拟机(例如 QEMU 和 VMware)中启动根文件系统。使用虚拟机检查编译后的映像快速而方便,但虚拟机可能无法模拟某些特性,例如硬件中断。硬件中断涉及快速反应行为,因此虚拟机无法轻松实现它们。最后,如果目标平台与您的开发平台具有相同的 CPU 架构,则可以使用 chroot 将您的本地系统替换为目标根文件系统。

通过遵循概述的五个流程,您可以为您的嵌入式系统构建根文件系统。但是,仍然有一个问题可能会困扰您——如何缩小嵌入式系统的尺寸,或者如何使用更少的闪存 RAM 来存储内核和根文件系统。减少 RAM 需求意味着您可以降低嵌入式系统的成本。

十种缩小尺寸的方法

缩小尺寸问题的组织结构显示在图 1 的底部。我们将方法分为两部分,因为嵌入式系统的软件平台通常由内核和根文件系统组成。第一部分是如何获得一个小的内核,第二部分是如何缩小根文件系统中每个组件(包括库和 shell)的尺寸。第二部分还讨论了如何压缩整个根文件系统。我们在下面详细描述了所有方法,以及实验结果。在解释完所有方法后,我们展示了这些方法对我们实验室嵌入式系统(称为 Wall 系统)的影响。表 1 列出了 Wall 系统的规格。该系统是一个网络安全网关,提供应用层内容过滤器,例如反垃圾邮件和防病毒。

表 1. Wall 系统规格

 功能软件包
内核X86、MMU、QoS、以太网、无线Linux 2.6.6;1,302,362 字节
连接LAN、DMZ、WAN、DHCP、DNS 中继、动态 DNS、链路负载均衡、桥接模式ppp-2.4.1、rp-pppoe-3.5
安全IPSec、PPTP、L2TP、SSL-VPNfreeswan2.06、12tpd-0.69
防火墙NAT、防火墙、UPNP、流量分析、APP 防火墙iptables-1.2.9、hotplug、iproute2
邮件反垃圾邮件、防病毒、POP3 代理p3scan
Web透明代理、URL、URL 关键词、内容关键词p3scan
IMMSN 日志基于 L7Filter 的开发
带宽控制TCTC
管理Web、SSL、FTP、日志轮换thttpd-2.21b、Openssl-0.9.7d、putre-ftpd-1.0.17a、cron
平台i386、IXP(简化版本) 
Linux 内核的方法

选择合适的内核是缩小内核尺寸的第一步。如果您选择不合适的内核,系统可能不仅体积庞大,而且也无法有效地利用处理器能力。例如,没有 MMU 的硬件平台上的标准 Linux 内核无法正常工作。这样的硬件平台需要特定的无 MMU 内核,例如 uClinux。大多数人使用标准 Linux 内核并尝试修剪其大小。

下一步是通过正确的配置仅在标准 Linux 内核中包含必要的模块。实际上,Linux 内核的默认配置包含许多未使用的模块,这会导致您拥有一个庞大的内核。图 3(a) 显示了正确配置对缩小尺寸效果的实验结果。在这种情况下,支持 TCP/IP 的系统的内核映像大小仅为支持所有网络协议的系统的 59.84%。

Standard Operating Procedures for Embedded Linux Systems

图 3. 缩小尺寸方法对内核的影响

为了缩小内核尺寸,第三步是在编译内核时使用优化参数。使用参数 -O1、-O2 或 -O3 可以提高性能,而使用 -Os 可以减小尺寸。但是,同时优化性能和尺寸是不可能的。因此,我们通常选择 -O2 以在尺寸和性能之间取得平衡。如图 3(b) 所示,与 -O3 相比,-Os 参数使内核映像的大小减少了 22.82%,但这会导致性能下降。

除了仅包含必要的模块并使用最佳参数编译内核之外,为了进一步缩小内核尺寸,您可以减小内核中分配的静态缓冲区和数组的大小,因为内核通常为标准 PC 声明一个大的缓冲区和数组。要找出哪个缓冲区或数组占用了大量内存空间,您可以使用命令 nm。此命令可以列出对象文件中每个变量的分配大小。有了这些信息,您可以浏览对象文件的相应源代码并更改缓冲区或数组的初始大小。缩小缓冲区大小的另一种方法是修改内核的 menuconfig 中的选项,以减少支持的最大外围设备数量,如图 3(c) 和 (d) 所示。

根文件系统的方法

如图 1 所示,我们确定了六种缩小根文件系统尺寸的方法。首先,您可以采用一个名为 BusyBox 的工具,该工具为任何小型或嵌入式系统提供了一个相当完整的环境。BusyBox 将许多常见的 UNIX 实用程序的微型版本组合到一个小的可执行文件中,并且它是高度模块化的,允许在编译时包含或排除命令。如图 4 所示,BusyBox 占用的空间是原始工具的 7.04%。

Standard Operating Procedures for Embedded Linux Systems

图 4. 缩小尺寸方法对根文件系统的影响

接下来,我们介绍三种删除未使用库或缩小所需库尺寸的方法。首先,您可以使用命令 ldd 来识别每个程序所需的共享库,然后使用此信息,您可以删除未使用的库。值得注意的是,如果某个共享库未被程序使用,您还应检查它是否被其他共享库使用。图 4 显示,删除冗余库可以将根文件系统的大小减少到其原始大小的 6.55%。其次,您可以将标准 C 库替换为小型 C 库,例如 uClibc、Newlib 或 diet libc。此类库删除了未使用的函数,因此它们的大小小于 Glibc,如表 2 所示。此表列出了四个库之间在功能方面的差异。第三,您可以使用名为 Libopt 的库优化器工具来重建库,这些库仅包含根文件系统中找到的可执行程序和共享库所需的函数。此工具利用 objdump 和 nm 来收集有关库对象文件、共享库和可执行程序的信息。

表 2. 不同 C 库之间的比较

 GNU C 库uClibcdiet libcNewlib
大小最大最小
兼容性良好良好正常
速度最快
可移植性
支持无 MMU
许可LGPLLGPLLGPLBSD、GPL
设置 menuconfig仅 make./configure
注意标准 C 库需要交叉编译器工具链通常链接为静态库由 Red Hat 管理

缩小根文件系统尺寸的第五种方法是删除不必要的文件。您可以删除一些目录,例如 /home、/mnt、/opt、/root、/boot 和 /proc(如果未使用)。您还可以删除 man、info、include 和 example 目录,以在将软件包额外集成到根文件系统时减小尺寸。一般来说,嵌入式系统仅执行特定程序,因此用户可以轻松操作它,而无需这些目录中的帮助文档或示例。

最后一种方法是避免将整个根文件系统解压缩到 SDRAM 中。根文件系统被压缩以节省存储空间,例如闪存 RAM。但是,在将文件系统解压缩到 SDRAM 中后,分配给文件系统的闪存存储器不再是必需的。例如,如果根文件系统的压缩大小为 4MB,压缩率为 50%,则系统占用 4MB 的闪存存储器和 8MB 的 SDRAM。因此,由于数据重复,系统浪费了大量内存存储。对于这个问题,您可以使用 CRAMFS。CRAMFS 是一种只读文件系统,旨在实现简单性和空间效率。您无需在挂载 CRAMFS 映像之前对其进行解压缩。CRAMFS 映像是 zlib 压缩的,一次一页,以实现随机读取访问。元数据未压缩,但以比传统文件系统(例如 ext2 或 FAT)更节省空间的简洁表示形式表示。但是,由于压缩文件的只读属性,随机写入访问很难为它们实现。如图 4 所示,CRAMFS 将文件系统压缩到其原始大小的 12.77%。

现在我们已经介绍了六种方法,让我们继续讨论这些方法对 Wall 项目的影响,如图 5 所示。首先,我们使用 BusyBox 代替原始 shell 中使用的多个实用程序程序。然后,我们使用参数 --strip-unneeded 和 -O2 编译了所有必需的软件包。接下来,我们使用命令 strip 和 objcopy 删除软件包的不必要内容。最后,我们删除了不必要的目录,例如 man、info 和 example。图 5(a) 说明了这些过程的结果。但是,Wall 的大小仍然为 139MB。因此,我们不得不深入查看 /usr 的内容,如图 5(b) 和 (c) 所示。在 Wall 项目中,删除不需要的文档和文件节省了 20.6MB 的空间。然后,通过消除未使用的库,可以节省约 15.9MB 的空间。但是,正如您所看到的,Perl 在我们的系统中占用了大量空间。可能存在其他方法来解决这个问题,但仅考虑我们上面所做的工作就足够了。

我们发现,在将新软件包集成到根文件系统时,优化软件包大小对于缩小尺寸也很有用。实际上,大多数程序和库默认以优化级别 2(gcc 选项 -g 和 -O2)编译,并且是为特定 CPU 编译的。在 Intel 平台上,软件默认针对 i386 处理器编译。为了最大限度地减小软件包大小,您不应采用 -g 选项,该选项会在执行文件中添加调试信息。此外,请记住使用 -strip 和 --strip-all 删除所有符号。在更高级的方法中,我们使用命令 readelf 检查执行文件中是否有任何冗余部分,并使用 objcopy 删除这些冗余部分。但是,这种方法对于小型程序可能效率不高。

Standard Operating Procedures for Embedded Linux Systems

图 5. Wall 项目根文件系统的缩小尺寸结果

结论

本文介绍了构建基于 Linux 的嵌入式系统的五个流程,并描述了缩小内核和根文件系统尺寸的十种方法。在使用这些方法后,我们的 Wall 项目缩小了 26.18%。实验结果表明,两种最有效的方法是提供正确的内核编译参数以及在根文件系统中使用简化的工具和库。希望本文能帮助您理解在构建基于 Linux 的嵌入式系统时的流程和问题。

资源

John Lombardo,《嵌入式 Linux》,第 1 版,New Riders,2001 年 7 月 5 日。

Todd Fischer,“优化嵌入式 Linux”,《Dr. Dobb's》,2002 年 5 月:www.ddj.com/184405050

Lei Yang、Robert P. Dick、Haris Lekatsas 和 Srimat Chakradhar,“CRAMES:嵌入式系统的压缩 RAM”,国际硬件软件协同设计会议,第 3 届 IEEE/ACM/IFIP 国际硬件/软件协同设计和系统综合会议论文集,新泽西州泽西市,2005 年,第 93-98 页。

“Buildroot—用法和文档 v1.2”,2004 年 12 月 28 日:buildroot.uclibc.org/buildroot.html

Karim Yaghmour,《构建嵌入式 Linux 系统》,第 1 版,O'Reilly,2004 年。

Chi-Hung Chou 目前在国立交通大学攻读计算机科学硕士学位。他的研究兴趣包括网状网络和嵌入式系统。可以通过电子邮件 payton345.cs95g@nctu.edu.tw 与他联系。

Tsung-Hsien Yang 目前在国立交通大学攻读计算机科学硕士学位。他的研究兴趣包括自动块模块测试和嵌入式系统。可以通过电子邮件 thyang.cs95g@nctu.edu.tw 与他联系。

Shih-Chiang Tsao 是国立交通大学计算机科学博士候选人,自 2003 年以来一直由林盈达博士指导。他的研究兴趣包括 TCP 友好拥塞控制算法、公平排队算法和 Web QoS。可以通过电子邮件 weafon@cs.nctu.edu.tw 或通过他的网站 (www.cs.nctu.edu.tw/~weafon) 与他联系。

林盈达于 1993 年获得加州大学洛杉矶分校 (UCLA) 计算机科学博士学位。自 1999 年以来,他一直担任国立交通大学计算机科学教授。他还是网络基准实验室 (NBL) 的创始人和主任,该实验室审查网络产品的功能、性能、一致性和互操作性,范围从交换机、路由器和 WLAN 到网络和内容安全以及 VoIP。他的研究兴趣包括网络协议和算法的设计、分析、实现和基准测试、线速交换和路由、服务质量、网络安全、内容网络和嵌入式硬件软件协同设计。可以通过电子邮件 ydlin@cs.nctu.edu.tw 或通过他的网站 (www.cs.nctu.edu.tw/~ydlin) 与他联系。

加载 Disqus 评论