引言
Embedded Linux技术基于开放源码的资源,并且已经是当今最重要的嵌入式应用技术之一。Embedded Linux是烧录在目标装置上的系统,1个Embedded Linux系统包含Linux kernel与 root filesystem 2大部分,Embedded Linux系统到底包含哪些组成要素构成,本文将由概念的层面进行解析。 本文 由于目前的目标装置,都必须嵌入极为复杂的功能,所以嵌入式操作系统(Embedded system)成为嵌入式系统不可或缺的要素。由于嵌入式系统是功能导向的系统,因此必须设计、选择或购买正确(或适合)的目标装置,才能开始实作并嵌入嵌入式系统。因此,嵌入式系统技术是以功能、与目标装置为分类的1种技术。 例如,与PDA相关的目标装置(即硬件)、与MP3播放器相关的目标装置、与3G手机相关的目标装置...等等;使用这些目标装置所开发的特定功能系统,便是PDA的嵌入式系统、MP3音乐播放的嵌入式系统、3G手机的嵌入式系统。 Embedded Linux其实并不是1个操作系统,而是代表应用Linux系统于Embedded system的名词。Embedded Linux的技术核心主轴是在研究如何将Linux系统嵌入至嵌入式目标装置里。 Embedded Linux是基于Linux系统的特殊应用,当然也要符合众多标准才行。LSB与FHS标准是重要的2大标准,跟随标准不但可以提供系统间的兼容性,也可以提供我们1个Linux系统的建构依据。 GNU/Linux的2个标准 由FSG (Free Standards Group) 所主持的 LSB (Linux Standard Base) 项目即是在制定 GNU/Linux 标准。根据LSB标准所发展的GNU/Linux系统,才能提供应用程序最小的可执行环境,并且可在依循LSB标准的Linux distributions上执行无误。例如,我们可以在符合LSB标准的Red Hat Linux上发展应用程序,只要自行发展的Embedded Linux系统符合LSB标准所订定的规范,应用程序就可以顺利移植到Embedded Linux上执行。 LSB标准提供我们发展Embedded Linux的依据,虽然Embedded Linux系统是最小化的Linux,但因为Embedded Linux是嵌入式系统的软件平台,所以我们不能任意精简Linux系统,在精简的过程中仍要保留最基本的操作系统环境,而LSB的标准正是在制定这些基本的需求。 FHS全名为Filesystem Hierarchy Standard,是定义档案与目录标准的文件,FHS的标准,定义了目录与档案的摆放位置,而UNIX-like的系统则是根据这个标准,管理整个档案结构。因此,不管是系统厂商、Linux/UNIX distribution发展者、应用程序作者、套件管理者、系统维护人员都应该要依照FHS的标准来管理UNIX系统的目录与档案。 Embedded Linux的特色是大量使用自由软件、与开放源码软件(FOSS- Free & Open Source Softwar)资源,任何你想要的软件,几乎都能在网络上找到自由软件已经成为Embedded Linux技术的重要支柱。自由软件资源包山包海,举凡应用程序、系统工具、网络工具、链接库、图形接口、小型浏览器、程序发展工具...等等都能找得到。 Busybox Busybox是重要的Embedded Linux工具箱,这个工具箱提供基本的UNIX指令、系统程序(daemon)与开机程序(init process)。Busybox用来建造1个基本、最小化且可开机的Linux系统,由于Busybox里的指令与工具都经过最小化处理,因此已经是目前主要应用在Embedded Linux实作上的开放源码项目了。 Embedded Linux的组成 图 Embedded Linux整体架构 Embedded Linux平台除了Linux kernel外,还包含共享链接库(shared library)。shared libraries是Linux kernel的重要支持,并且也是Linux架构里独立的1层。在应用程序方面,许多现存的开放源码项目都可以直接移植到ARM9平台。但这里所指的移植是对原始码进行跨平台编译(cross compile),并不是BSP(board support package)的移植。 跨平台编译 因为开放源码开发工具的特性,在应用程序级别的移植工具上,可以有1套比较系统化的方法,也有相关的工具与环境可以使用,目前最热门的跨平台编译环境为OpenEmbedded。开放源码软件采用GNU Autoconf与GNU Automake来撰写编译法则(Makefile),因此实务上,要将应用程序移植到ARM9平台,大部分案例只需要做跨平台编译即可。要了解如何将原始码移植到ARM9平台,需要学会GNU Autoconf以及GNU Automake的使用。 GNU Autoconf Autoconf是m4宏的扩充套件,可以用来自动设定软件套件的原始码。Autoconf会产生1个协助程序编译的设定文稿执行档(configuration script),以方便编译原始码前进行系统检查与设定,使用GNU Autoconf时,必须安装GNU m4套件。 GNU Automake Automake是自动产生Makefile.in的工具,需配合Autoconf使用,以产生可以让GNU Make自动编译原始码的”Makefile”档案。 GNU Make GNU Make会根据“Makefile”来自动编译程序,而编译完成的程序为执行文件。GNU Make的重要特点,是没有特定程序语言限制,甚至可以应用在非程序语言编译的环境中,例如:系统维护工作与套件安装,因此GNU Make可以说是系统自动化的好工具。 GNU Make根据“Makefile”档案里所定义的规则,执行Unix命令,简单的Makefile规格,可以利用编辑器手动撰写,但较复杂且与针对不同平台的设定,则建议采用GNU Autoconf/GNU Automake来产生“Makefile”。当我们能够产生使用cross toolchain的Makefile时,就可以将套件编译成ARM9的执行档。 ARM 平台的选择与支持 嵌入式装置的硬件选择当然没有所谓的标准,但若是谈论到嵌入式Linux的应用,在平台的选择上就会有一些考虑。最重要的考虑因素,当然就是处理器对于操作系统的支持,如此一来,没有MMU(内存管理单元)的ARM7平台,就不在主要的选择范围内。以下列出几个目前普遍使用的ARM9应用程序处理器(application processor): 在选择解决方案时,若是决定采用Linux做为嵌入式操作系统,首先当然就是要确定厂商是否提供完整的BSP。不过,由于Linux是由社群所维护发展,因此,选择目前Linux kernel内有支持的平台,将会是较好的选择,这也是为什么有许多大厂,主动贡献并提交BSP给kernel.org的原因。 目前在kernel社群比较活跃的ARM9厂商,或是社群主动积极协助维护的SOC平台,像是ATMEL、Samsung与TI OMAP等,这些都是kernel.org的Linux kernel就有支持的处理器,这表示让Linux支持这些平台的方式也很简单,就是到kernel.org下载官方的Linux kernel即可。 Crosstool 针对ARM9或是其它平台的开发,最重要的工具就是Cross Toolchain。Cross Toolchain的制作一直是Embedded Linux开发者的梦靥,大多数人选择由网络下载现成的开发工具,但经常会遇到缺乏链接库的编译错误。完整的Cross Toolchain包含1套基本的gcc cross compiler以及其它的应用链接库;Cross Toolchain是制作基本gcc cross compiler的工具,透过crosstool即可制作ARM9的基本toolchain。 Root Filesystem概念 Root filesystem的建置,即是在建立1个基本的Linux系统(base system),让kernel在完成开机后,进入user mode执行使用者程序。Root filesystem的建置主要是以Busybox为主,并加入(移植)客制化的开放源码(open source)与自由软件(free software)。 因为嵌入式Linux的root filesystem是依照需求加入套件,与桌面环境的Linux distribution不同,因此都是用从头打造的方式做起。在建立root filesystem时,链接库相依 (library dependencies) 的议题是相当重要的项目。当root filesystem缺少必要的library时,程序当然无法执行,甚至系统也会无法顺利启动。分析应用程序所需的相依链接库,观念如下: (1)先利用Cross Toolchain的objdump指令观察ELF格式里的「NEEDED」项目。 (2)必须再检查这些library是否相依其它library。 1个基本且可开机的root filesystem,也称做bootstrap root filesystem,1个可用的bootstrap root filesystem只需要包含busybox与libc即可。传统的Embedded Linux应用,大多是以NFS的方式来测试目标装置的完整root filesystem(full root filesystem)。以NFS进行Embedded Linux开发测试,主要是针对目标装置的full root filesystem做立即(right now)的系统执行测试(run-time),免除不断打包image file、开机的恶梦。这是1种流行很久的Embedded Linux系统测试与开发方式,其概念如下: (1)将target的完整root filesystem(例如ARM9 root filesystem)建置后,存放于host端的某个目录下,例如/home/rootfs。 (2)为target制作1个NFS root filesystem,也就是bootstrap root filesystem+ NFS功能,并使用NFS root filesystem将目标装置开机。 (3)设定host端为NFS server。 (4)以NFS mount方式将host端上的root filesystem目录mount进来,即可在目标装置上执行full root filesystem里的应用程序。 这种方式不但简单,而且方便,需要的基础建设如下: 1.目标装置使用的kernel必须支持NFS。 2.制作bootstrap root filesystem时,需要加入mount指令,并且开启mount指令的NFS功能。 3.加入NFS functionality至bootstrap root filesystem。 4.设定NFS server。 制作完成的root filesystem必须做打包的动作,将整个root filesystem包装成1个映像档(image file)。根据目标装置的不同,我们可以将映像档包装成ROM fs、Compress ROM fs、ext2fs或是compress RAM fs。 ROM file system ROM file system(romfs)是1种只读的档案系统,在Embedded Linux里的主要应用为制作romfs格式的档案系统映像文件。我们将root filesystem制作成romfs filesystem的image檔。开机后,整个filesystem仅能读取。要使用romfs filesystem必须将Linux kernel里的CONFIG_ROMFS_FS功能选项打开。制作ROM fs映像档所使用的工具为genromfs。 Compressed ROM file system Compressed ROM file system(cromfs)即是压缩过的ROM file system,其制作方式相当简单,只要使用gzip将ROM file system的映像档压缩即可。 制作ext2fs映像档 制作ext2fs映像档的方式有2种。1种是使用dd指令产生1个空白的映像档,接着再将此映像档以mkfs.ext2指令格式化成ext2的格式。制作好的空白映像档再以loopback mount方式挂载到1个目录下,再将root filesystem整个复制到此目录下,即可完成ext2fs映像档的制作。 另外1种建立ext2fs映像档的方式是使用genext2fs工具,此工具的好处是,当我们需要在root filesystem里预先建立(pre-built)装置文件时(device file),只需要编写1个装置文件表格,genext2fs工具会在打包映像档时,自动在root filesystem里建立装置文件。 Initial RAM disk(initrd) RAM disk是存在于内存中的虚拟磁盘,也就是将RAM拿来当成磁盘使用。在Embedded Linux的应用中,我们通常会将ramdisk当成暂存目录来使用。例如将/dev/ram1附挂到/tmp目录,以便能让应用程序存放暂时性档案。/dev/ram?为ramdisk的device file。由于整个root filesystem是从真正的储存装置读取并加载至ramdisk,因此有1个重要的特性是对file system所做的任何修改,都不会影响到真正root filesystem的内容。 initrd全名为initialize RAM disk,是1个特殊的RAM disk。bootloader会将initrd载至内存,Linux kernel则可在/dev/ram0找到initrd。initrd会在Linux kernel开机前就加载,initrd正式的用途是用来存放开机时所需要的驱动程序(因root filesystem尚未mount进来)。在Embedded Linux应用上,我们会利用initrd来存放整个档案系统(root filesystem),也就是将root filesystem制作成ext2或romfs格式(或其它档案系统)的映像文件,并在开机时由bootloader加载内存,initrd均位于/dev/ram0。要使用RAM disk与initrd,必须将Linux kernel的CONFIG_BLK_DEV_RAM以及CONFIG_BLK_DEV_INITRD)。 使用initrd做为root filesystem装置 将initial RAM disk当成root filesystem来使用,是在Embedded Linux应用上是相当常见的技巧,如果我们想将initial RAM disk当成存放root filesystem的装置来使用,在开机时,只需要配合root=的kernel开机参数即可。 initramfs Linus本人在Linux 2.6时代所提出的 "initramfs" ,是1种更好的 "root=" 做法。简单来说,initramfs就是kernel 2.6 的 initrd,initramfs是属于1种compressed ramfs(ram filesystem)的映像档。 C链接库 在C链接库方面,除了标准的glibc也被广泛应用在嵌入式系统领域外,也有一些专门针对嵌入式系统应用所发展的C链接库,像是uClibc以及Diet libc。但是由于现在的ARM9处理器计算效能都很快,平台也多搭载大容量NAND闪存,所以许多实作都直接使用libc来实作root filesystem。 Linux驱动程序 由于嵌入式系统整体来看,除了软件开发外,也包含硬件客制化,因此驱动程序在嵌入式系统技术领域中,占了举足轻重的地位。学习驱动程序需要确实了解硬件的规格与微处理器架构,并且工程师还要能分得清楚哪些东西是接口(interfacing),也就是与硬件无关的程序(machine-independent);以及哪些是站在第一线做硬件控制的程序(machine-dependent)。各种软件硬接口与汇流排也都要精通。 了解Linux驱动程序的架构,是进入嵌入式Linux领域的重点功课,因为许多针对ARM9平台的驱动程序都是参考框架、或是针对特定开发板的实作,因此必须了解Linux驱动程序的架构,并进行修改,以符合自己的开发板与外围规格。 Linux驱动程序,采取严谨的分层式架构设计(layered architecture),利用分层的架构设计来彻底区分generic device driver(machine independent)与machine dependent driver。 Linux驱动程序透过注册与回呼的机制来清楚区分每1层的关系。分层架构的实作必须在下层将自己注册给上层,上层再回呼下层;上层的驱动程序必须提供注册函数供下层呼叫,下层驱动程序所使用的注册函数也将决定自己的上层架构。 与user application如何互动,是撰写驱动程序时所要考虑的重要一环,因此撰写驱动程序时,要提供什么功能给应用程序引用,就必须事先定义清楚。Linux的 generic device driver层已经帮我们把这些功能定义清楚了。Linux驱动程序如何透过I/O port或I/O memory来控制装置,也就是与芯片组的沟通,方式是使用Linux kernel所提供的I/O函数来存取并控制实体硬件装置。 Linux驱动程序的装置文件 Device files是UNIX系统的独特观念,在UNIX系统底下我们把外部的周边装置均视为1个档案,并透过此档案与实体硬件沟通,这样的档案就叫做device files或special files。 Device file的major number代表1个特定的装置,例如major number 1为”null”虚拟装置,major number定义于kernel文件目录Documentation/devices.txt。Minor number代表装置上的子装置,例如同1个硬盘上的分割区就用不同的major number来代表,但其major number相同。 我们在设计device driver时,会先透过1个“注册”(register)的动作,将自己注册到kernel里,注册时,我们会指定1个major number参数,以指定此驱动程序所要实作的外围装置。当user开启device file时,kernel便会根据device file的 major number找到对应的驱动程序响应使用者。Minor number则是device driver内部所使用,kernel并不会处理不同的minor number。 Linux 2.6的kobject模型 Linux 2.6在驱动程序的架构方面,加入kobject的概念。kobject以更有系统、组织的方式维护系统里的driver(集中式管理),但并非改变现有(kernel 2.4以来)的driver架构。在kobject的模型下,可以看到1个platform driver观念。所谓「platform driver」就是machine- dependent driver,当驱动程序设计师在kernel 2.6底下实作machine-dependent driver时,就要以platform driver的架构来实作。例如,针对我们的目标装置进行硬件层的驱动程序撰写时,就要以platform driver的方式来撰写,实作上,只是多1个注册到platform driver层的动作而已。 Flash装置的支持 针对嵌入式系统经常使用的闪存(Flash)储存装置,Linux kernel支持JFFS2与NFTL 2个专门针对快闪记亿体设计的档案系统。JFFS2(Journaling Flash File System version 2)是专门针对 NOR 型闪存所设计的档案系统。NFTL(NAND Flash Translation Layer)则是专门针对NAND型闪存设计的档案系统。 结论 综合而言,Embedded Linux是1个平台、也是一些工具的集合、也是1个嵌入式软件的开发环境;实作上,Embedded Linux除了会进行kernel的修改、驱动程序的移植或开发外,也会是系统管理与系统整合的再应用,这是一门集大成的技术,并不只是1个嵌入式操作系统,也不只是1套开发工具。