从零构建一个400K的嵌入式Linux根文件系统UCFS
- UID
- 852722
|
从零构建一个400K的嵌入式Linux根文件系统UCFS
从零构建一个400K的嵌入式Linux根文件系统UCFS
所谓根文件系统,即可以作启动盘的文件系统。根文件系统(以下在不引起歧义的地方简称文件系统)主要包括etc/、bin/、sbin/、lib/、proc/等五个根目录。创建根文件系统使用了busybox工具,同时,为了保持交叉编译器和文件系统库的一致性,以buildroot、uClibc和gcc等工具构建了一个交叉编译器。文件系统使用buildroot工具编译的uClibc库。使用该方法创建的文件系统压缩后可以达到400K左右。
1.1 利用buildroot制作交叉编译器
1.1.1 buildroot简介
作为嵌入式系统,最为紧缺的资源就是存储空间。精简嵌入式系统所使用的库是减小存储空间最常用的方法之一。GNU的Glibc是一个非常宠大而完整的库,至少对于嵌入式系统来说,其体积显得过于大了一些。uClibc的提出较好的解决了这样一个问题。uClibc尽可能的兼容Glibc,大多数应用程序可以在很小或完全不修改的情况下就可能使用uClibc替代glibc。通过uClibc来代替Glibc,可以在不改变应用程序功能的前提下,大大减少发布文件的大小,无论应用程序以静态链接来编译,还是以动态链接形式编译。
不过使用uClibc代替并不是简单的设置一两个参数就行了,通常需要使用一个不同的工具集(gcc/binutils等)来编译代源码。手工的构造这样一个环境,对于大多数普通程序员来说,不一定是一件很简单的事情,因此,uClibc的开发者创造出一个叫做buildroot的工具集。
buildroot将自动构造编译基于uClibc代码的工具集和uClibc库,并提供一个可配置的框架和一些构建一个基本系统的配置文件。用户只需要通过配置菜单选择了相应的目标软件,buildroot就可以从构建基本工具集开始,一直到最后构建出目标系统所需要的东西,如嵌入式系统常用的基于ext2的initrd,jffs根文件系统,压缩的根目录树等,这些代码都是基于uClibc而不是系统的Glibc的。Buildroot对主机系统的要求较小,通常只需要主机系统提供足以构建工具链(toolchain)的工具,如gcc/binutils等,当工具链编译完成后,对目标系统需要的源码的编译过程与主机系统的开发工具集基本上就没有什么关系了。因此,不同的主机如果能够通过第一步,编译完成工具链,那么编译出来的目标系统的执行代码就可以几乎不存在由于系统引起的差异。这样,开发人员就可能在各自喜欢的Linux发行版上进行开发,而不必担心出现什么兼容性问题。
1.1.2 下载相关资源
下载http://buildroot.uclibc.org/downloads/old/buildroot-0.9.27.tar.gz,创建/home/br目录,将buildroot-0.9.27.tar.gz拷贝到该目录,执行下面的命令解压缩,得到目录buildroot。
tar –zxvf buildroot-0.9.27.tar.gz
进入buildroot目录。buildroot默认情况情况下使用wget从互联网下载所需的相关资源,在这里,创建dl目录,作为资源的存放目录。
cd buildroot
mkdir dl
接下来,需要下载http://www.uclibc.org/downloads/uClibc-0.9.27.tar.bz2,并在http://buildroot.uclibc.org/downloads/buildroot-sources/下载ccache-2.3.tar.gz、binutils-2.15.91.0.2.tar.bz2、genext2fs_1.3.orig.tar.gz、linux-libc-headers-2.4.27.tar.bz2。最后还需下载http://ftp.gnu.org/gnu/gcc/gcc-3.3.4/gcc-3.3.4.tar.bz2,一共六个文件,全部拷贝到刚才创建的dl目录。
1.1.3 配置
返回buildroot目录,运行make menuconfig命令配置buildroot,见下图:
图 1-1 buildroot配置界面
Target Architecture(arm) --->选择arm,因为目标板CPU为AT91RM9200,为ARM 920T系列。
Build options --->
()wget command 因为所有的工具已经下载到dl本地目录,故不需要从互联网下载资源,将此项清空。
[ ]Tar verbose
($BUILD_DIR/staging_dir) Toolchain and header file location?这个选项表明编译生成的交叉编译器所在目录,不用修改。
(1) Number of jobs to run simultaneously。
Toolchain Options --->
--- Kernel Header Options
Kernel Headers (Linux 2.4.27 kernel headers) ---> 注意内核头文件版本和目标板将使用的内核版本一致。我的目标板是2.4.19,使用了一个较为接近的2.4.27替代,目前看来没有太大的问题。
--- uClibc Options
[ ] Use daily snapshot of uClibc?是否使用最新版本的uClibc。
[ ] Enable local/gettext/i18n support?
--- Binutils Options
Bintuils Version (bintuils 2.15.91.0.2) --->
--- Gcc Options
GCC compiler Version (gcc 3.3.4) -->
( ) Additional gcc options
[ * ] Build/install c++ compiler and libstdc++>
[ ] Build/install java compiler and libgcj?
--- Cache Options
[ * ] Enable ccache support? 不启用ccache的支持,否则将来在制作交叉编译器时可能因为符号链接而提出头文件路径出错的问题。
--- Gdb Options
[ ] Build gdb degugger for the Target
[ ] Build gdb server for the Target
--- Common Toolchain Options
[ ] Enable multilib support?
[ ] Enable large file (files > 2GB) support?
[ ] Use software floating point by default
(-Os –pipe) Target Optimizations
Package Selection for the target ---> 这一项没有选择任意一项,busybox我是手工完成的,并不打算让buildroot自动构建。
Target Options --->
[ ] cramfs root filesystem for the target device
[ * ] ext2 root filesystem for the target device
[ ] jffs2 root filesystem for the target device
[ ] squashfs root filesystem for the target device
最后退出配置界面,并选择保存设置。
1.1.4 编译
执行make命令编译buildroot,在编译过程中有三个提示,首选是询问ARM版本,选择920T,然后询问端模式,选择Little Endian,由于9200是带有MMU的,所以询问MMU时选择Y。
编译过程在我的VMWare虚拟中持续了大半个小时。
我们需要的交叉编译器现在就在build_arm/staging_dir目录下。这下面最关心的三个目录:bin、lib、include。bin目录包含了所有的交叉编译器,我们关注的是以arm-linux-uclibc-开头的这一组编译器。lib目录是uclibc编译的结果,inlcude是2.4.27内核头文件。
3.3.4的交叉编译器就算完成了。交叉编译器的制作涉及的工具很多,编译的时间很长,非常考验一个人的耐心。下一步,将利用这个交叉工具链去编译busybox。
1.2 利用busybox制作二进制工具
1.2.1 busybox简介
BusyBox 是很多标准 Linux? 工具的一个单个可执行实现。BusyBox 包含了一些简单的工具,例如 cat 和 echo,还包含了一些更大、更复杂的工具,例如 grep、find、mount 以及 telnet(不过它的选项比传统的版本要少);有些人将 BusyBox 称为 Linux 工具里的瑞士军刀。本文将探索 BusyBox 的目标,它是如何工作的,以及为什么它对于内存有限的环境来说是如此重要。
BusyBox 的诞生BusyBox 最初是由 Bruce Perens 在 1996 年为 Debian GNU/Linux 安装盘编写的。其目标是在一张软盘上创建一个可引导的 GNU/Linux 系统,这可以用作安装盘和急救盘。一张软盘可以保存大约 1.4-1.7MB 的内容,因此这里没有多少空间留给 Linux 内核以及相关的用户应用程序使用。
BusyBox 揭露了这样一个事实:很多标准 Linux 工具都可以共享很多共同的元素。例如,很多基于文件的工具(比如 grep 和 find)都需要在目录中搜索文件的代码。当这些工具被合并到一个可执行程序中时,它们就可以共享这些相同的元素,这样可以产生更小的可执行程序。实际上, BusyBox 可以将大约 3.5MB 的工具包装成大约 200KB 大小。这就为可引导的磁盘和使用 Linux 的嵌入式设备提供了更多功能。我们可以对 2.4 和 2.6 版本的 Linux 内核使用 BusyBox。
为了让一个可执行程序看起来就像是很多可执行程序一样,BusyBox 为传递给 C 的 main 函数的参数开发了一个很少使用的特性。回想一下 C 语言的 main 函数的定义如下:
int main( int argc, char *argv[] )
在这个定义中,argc 是传递进来的参数的个数(参数数量),而 argv 是一个字符串数组,代表从命令行传递进来的参数(参数向量)。argv 的索引 0 是从命令行调用的程序名。利用这个性质,我们可以创建一个到可执行程序的符号链接,在执行这个符号链接时,这个符号链接的名字就是argv[0]。
BusyBox 使用了符号链接以便使一个可执行程序看起来像很多程序一样。对于 BusyBox 中包含的每个工具来说,都会这样创建一个符号链接,这样就可以使用这些符号链接来调用 BusyBox 了。BusyBox然后可以通过 argv[0] 来调用内部工具。
1.2.2 下载资源
下载http://www.busybox.net/downloads/busybox-1.00.tar.bz2,创建/home/busybox目录,将busybox-1.00.tar.bz2拷贝到该目录,执行tar –jxvf busybox-1.00.tar.bz2解压缩,得到busybox-1.00目录。进入该目录,下一步就是配置busybox并编译和安装了。
1.2.3 配置和安装
在busybox-1.00目录中执行make menuconfig命令进入busybox的配置界面,见下图:
图 1-2 busybox配置界面
由于选项较多,只罗列主要选项,每有提高的采用默认选项:
General Configuration --->
Buffer allocation policy(Allocate on the Stack) --->
[ * ] Show verbose applet usage messages
[ * ] Support for devfs
[ * ] Use the devpts filesystem for Unix98 PTYs
Build Options --->
[ ] Build BusyBox as a static binary (no share libs) 这一项不选,即采用动态库德方式,可以节省几百K的空间。
[ * ] Do you want to build BusyBox with a Cross Compiler?
(/home/br/buildroot/build_arm/staging_dir/bin/arm-linux-uclibc-) Cross Compiler 指定交叉编译器,即为利用buildroot构建的交叉编译器路径。
[ * ] Don’t use /usr 这一项一定要选中,不然会覆盖/usr下面的库。
(./_install) BusyBox installation prefix 安装路径,执行make install后会在busybox-1.00目录下生成_install目录,包含了busybox编译结果和相关的工具符号连接。
Archival Utilities ---> 主要涉及文档压缩和解压缩的相关工具,保持默认选项即可。
Editors ---> 去掉vi,基本上不再嵌入式目标板上运行vi编辑器。
Finding Utilities --->
Init Utilities ---> 一定要选中init选项,其它保持默认。
Login/Password Management Utilities ---> 选中gettty,事实上,还需要mingetty,不过busybox不支持,只有后面手工编译并加入到文件系统中。
Miscellaneous Utilities --->
Linux Module Utilities ---> 动态模块加载相关,动态加载模块给应用程序调试带来很大的方便,所以选择支持,因为内核是2.4版本,所以该项选中以下选项:
[ * ] insmod
[ * ] Support version 2.2.x to 2.4.x Linux kernels
[ * ] lsmod
[ * ] modprobe
[ * ] rmmod
[ * ] Support tainted module checking with new kernels
Another Bourne-like Shell ---> 选中ash作为默认的bash,这可以节省很多空间。
Linux System Utilities --->
为了支持NFS,注意将以下三项选中
[ * ] mount
[ * ] Support mounting NFS file system
[ * ] umount
loop设备使用较多,这里也选中
[ * ] Support for loop devices
Debugging Options ---> 不需要调试符号
配置结束后保存设置,退出配置界面。
1.2.4 编译安装
执行make编译busybox,编译结束后执行make install,则会在busybox-1.00目录下生成_install目录,其中又包含了bin和sbin两个子目录。两个子目录中除了busybox文件外,其他均是指向该文件的符号连接。
到这一步,busybox已经编译完了,接下来就是构建根文件系统,因为该文件系统是基于uclibc构建的,我将其称之为ucfs。
1.3 构建ucfs
1.3.1 利用loop设备创建文件系统
进入/tmp目录,并创建/mnt/ucfs目录作为将来的挂载位置。执行dd if=/dev/zero of=ucfs bs=1k count=15360,创建一个15M的文件,该文件全部被初始化为0。如果反复在一个文件系统上执行增加或删除操作,会使本来没有使用的部分也难以压缩,将未使用部分初始化为0能够使该部分被压缩。
使用losetup命令将loop设备和ucfs文件关联:losetup –e none /dev/loop0 ucfs。如果loop0已被使用,可以使用loop1,loop2等。
在loop设备上创建ext2文件系统:mke2fs -m 0 /dev/loop0 15360。-m 0的作用是不为root保留使用空间,从而提高文件系统的利用率。
加载文件系统:mount -t ext2 /dev/loop0 /mnt/ucfs。
接下来进入/mnt/ucfs目录,可以看到里面已经有了一个lost+found目录。在/mnt/ucfs目录中添加相关的文件和目录(接下来马上讲解),完成根文件系统的构建。
1.3.2 安装二进制工具、库及其他
在上一小节最后提高的卸载/dev/loop0之前,首先需要在/mnt/ucfs中添加用于制作根文件系统的目录及相关文件。先拷贝二进制工具,即busybox工具集。cp –a /home/busybox/busybox-1.00/_install/* /mnt/ucfs,将busybox和相关的符号连接拷贝到ucfs,观察/mnt/ucfs,增加了bin和sbin目录,删除linuxrc符号连接。
接下来,拷贝交叉编译器中的uclibc库。进入交叉编译器的lib目录,cd /home/br/buildroot/build_arm/staging_dir/lib。然后拷贝必要的库。cp -a *-*.so /mnt/ucfs/lib,cp -a *.so.[*0-9] /mnt/ucfs/lib。回到/mnt/ucfs/lib目录,删除libstdc++,暂时不用c++的标准库,
rm –f libstdc++*。
接下来,利用busybox的示例来构建/etc目录。进入/home/busybox/busybox-1.00/example/bootfloppy目录。拷贝etc目录到/mnt/ucfs,cp –r /etc /mnt/ucfs。关于创建根文件系统,可以仔细阅读bootfloppy.txt文件,很有参考价值。在这里,需要修改busybox默认创建的inittab文件,vi /mnt/ucfs/etc/inittab,将最后两行删除,只保留以下两行:
::sysinit:/etc/init.d/rcS
::respawn:-/bin/sh
最后还需创建空目录,/mnt/ucfs/proc。至此,根文件系统制作完毕。最后离开/mnt/ucfs以便卸载loop0设备。执行umount /dev/loop0和losetup –d /dev/loop0,使修改保存到ucfs。这个ucfs由gzip压缩后就可以作为系统的根文件系统了。使用gzip –v9 ucfs压缩后,ucfs.gz只有407304字节,不到400K!
最后来看一看成果,见下图。大功告成!
图 1-3 自制文件系统启动界面 |
|
|
|
|
|