根文件系统是制作:
1、从http://www.busybox.net/ 下载busybox-1.7.0.tar.bz2
2、tar xjvf busybox-1.7.0.tar.bz2 解包
3、修改Makefile 文件,指明交叉编译器路径。
4、make menuconfig 配置busybox
busybox 配置主要分两部分。
第一部分是Busybox Settings,主要编译和安装busybox 的一些选
项。这里主要需要配置:
1)、Build Options -- Build BusyBox as a static binary (no shared
libs),表示编译busybox 时,是否静态链接C 库。我们选择动态链
接C 库。
2)、Installation Options -- Applets links (as soft-links) -- (X) as
soft-links,表示安装busybox 时,将各个命令安装为指向busybox
的软链接还是硬链接。我们选择软链接。
3)、Installation Options -- (/work/nfs_root/fs_mini3) BusyBox
installation prefix,表示busybox 的安装位置。我们选择
/work/nfs_root/fs_mini3
4)Busybox Library Tuning。保留Command line editing 以支持命
令行编辑;保留History size 以支持记忆历史命令;选中Tab
completion 和Username completion 以支持命令自动补全
第二部分是Applets,他将busybox 的支持的几百个命令分门别类。
我们只要在各个门类下选择想要的命令即可。这里我们基本保持默认
设置。
1)选中Networking Utilities -- httpd 下的Enable -u <user> option,
以启用http 服务器的功能allows the server to run as a specific user
5、编译busybox make
6、安装busybox make install
安装完成后,可以看到在/work/nfs_root/fs_mini3 目录下生成了
binsbinusr/binusr/sbin 目录,其下包含了我们常用的命令,这些命令
都是指向bin/busybox 的软链接,而busybox 本身的大小不到800K:
进入目录查看:
$ ls
bin linuxrc sbin usr
$ ls -l bin
total 740
busybox 以它娇小的身躯容纳了数以百计的命令代码,实在是让人佩
服不已,其不愧嵌入式系统瑞士军刀之美誉。据说,busybox 的作者
身患绝症,这更让人钦佩GNU 开源软件的作者们。
三、利用交叉编译工具链,构建/lib 目录
光有应用程序(命令)是不够的,因为应用程序本身需要使用C 库
的库函数,因此还必需制作for ARM 的C 库,并将其放置于/lib 目录。
要自己写C 库的源代码吗?不用!还记得交叉编译工具链
的3 个组成部分吗?交叉编译器、for ARM 的C 库和二进制工具。
哈哈,for ARM 的C 库是现成的,我们只需要拷贝过来就可以了。遗
憾的是:整个C 库目录下的文件总大小有26M。而我们根文件系统
所在分区不过区区16M 而已,根本放不下。怎么办呢?
dennis@dennis-desktop:/work/nfs_root/fs_mini3$ du -s --si
/work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux/lib
26M /work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux/lib
需要C 库目录下所有的文件吗?no,absolutely no! 让我们来分析一
下glibc 库目录下内容的组成。该目录下的子目录和文件共分8 类:
• libtool 库文件(.la),在链接库文件时这些文件会被用到,比
如他们列出了当前库文件所依赖的其它库文件,程序运行时无
需这些文件
• gconv 目录,里面是各种链接脚本,在编译应用程序时,他们
用于指定程序的运行地址,各段的位置等
• 静态库文件(.a),例如libm.a,libc.a
• 动态库文件(.so、.so.[0-9]*)
• 动态链接库加载器ld-2.3.6.so、ld-linux.so.2
• 其它目录及文件
很显然,第1、2、3、4、7 类文件和目录是不需要拷贝的。
由于动态链接的应用程序本身并不含有它所调用的C 库函数的代码,
因此执行时需要动态链接库加载器来为它加载相应的C 库文件,所
以第6 类文件是需要拷贝的。
除此之外,第5 类文件当然要拷贝。但第5 类文件的大小也相当大。
dennis@dennis-desktop:/work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux
/lib$ du -c --si *.so*
7.2M total
需要全部拷贝吗?非也,非也!其实,需要哪些库完全取决于要运行
的应用程序使用了哪些库函数。如果我们只制作最简单的系统,那么
我们只需要运行busybox 这一个应用程序即可。通过执行
dennis@dennis-desktop:/work/nfs_root/fs_mini3$ arm-linux-readelf
-a bin/busybox | grep 'Shared'
0x00000001 (NEEDED) Shared library: [libcrypt.so.1]
0x00000001 (NEEDED) Shared library: [libm.so.6]
0x00000001 (NEEDED) Shared library: [libc.so.6]
可知:busybox 只用到了3 个库:通用C 库(libc)、数学库(libm)、
加密库(libcrypt),因此我们只需要拷贝这3 个库的库文件即可。
但是每个库都有4 个文件,4 个文件都要拷贝吗?当然不是。
dennis@dennis-desktop:/work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux
/lib$ ls -l libcrypt[.-]*
-rwxr-xr-x 1 dennis dennis 30700 2008-01-22 05:32
libcrypt-2.3.6.so
-rw-r--r-- 1 dennis dennis 23118 2008-01-22 05:32 libcrypt.a
lrwxrwxrwx 1 dennis dennis 13 2008-12-22 15:38 libcrypt.so ->
libcrypt.so.1
lrwxrwxrwx 1 dennis dennis 17 2008-12-22 15:38 libcrypt.so.1 ->
libcrypt-2.3.6.so
dennis@dennis-desktop:/work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux
/lib$ ls -l libm[.-]*
-rwxr-xr-x 1 dennis dennis 779096 2008-01-22 05:31 libm-2.3.6.so
-rw-r--r-- 1 dennis dennis 1134282 2008-01-22 05:32 libm.a
lrwxrwxrwx 1 dennis dennis 9 2008-12-22 15:38 libm.so ->
libm.so.6
lrwxrwxrwx 1 dennis dennis 13 2008-12-22 15:38 libm.so.6 ->
libm-2.3.6.so
dennis@dennis-desktop:/work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux
/lib$ ls -l libc[.-]*
-rwxr-xr-x 1 dennis dennis 1435660 2008-01-22 05:48 libc-2.3.6.so
-rw-r--r-- 1 dennis dennis 2768280 2008-01-22 05:31 libc.a
-rw-r--r-- 1 dennis dennis 195 2008-01-22 05:34 libc.so
lrwxrwxrwx 1 dennis dennis 13 2008-12-22 15:38 libc.so.6 ->
libc-2.3.6.so
4 个文件中的.a 文件是静态库文件,是不需要拷贝的。另外3 个文件
是:
• 实际的共享链接库:
libLIBRARY_NAME-GLIBC_VERSION.so。当然需要拷贝。
• 主修订版本的符号链接,指向实际的共享链接库:
libLIBRARY_NAME.so.MAJOR_REVISION_VERSION,程序
一旦链接了特定的链接库,将会参用该符号链接。程序启动时,
加载器在加载程序前,会检索该文件。所以需要拷贝。
• 与版本无关的符号链接,指向主修订版本的符号连接(libc.so
是唯一的例外,他是一个链接命令行:libLIBRARY_NAME.so,
是为编译程序时提供一个通用条目)。这些文件在程序被编译
时会被用到,但在程序运行时不会被用到,所以不必拷贝它。
关于共享库的2 个符号链接的作用的特别说明:
当我们使用gcc hello.c -o hello -lm 编译程序时,gcc 会根
据-lm 的指示,加头(lib)添尾(.so)得到libm.so,从而沿着与版
本无关的符号链接(libm.so -> libm.so.6)找到libm.so.6 并记录在案
(hello 的ELF 头中),表示hello 需要使用libm.so.6 这个库文件所
代表的数学库中的库函数。而当hello 被执行的时候,动态链接库加
载器会从hello 的ELF 头中找到libm.so.6 这个记录,然后沿着主修
订版本的符号链接(libm.so.6 -> libm-2.3.6.so)找到实际的共享链
接库libm-2.3.6.so,从而将其与hello 作动态链接。可见,与版本无
关的符号链接是供编译器使用的,主修订版本的符号链接是供动态
链接库加载器使用的,而实际的共享链接库则是供应用程序使用的。
通过以上分析,我们只需要拷贝3 个库(每个库各1 个主修订版本的
符号链接和1 个实际的共享链接库)以及动态链接库加载器(1 个符
号链接和1 个实体文件)。步骤如下:
dennis@dennis-desktop:/work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux
/lib$ mkdir /work/nfs_root/fs_mini3/lib
dennis@dennis-desktop:/work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux
/lib$ cp libcrypt-* /work/nfs_root/fs_mini3/lib
dennis@dennis-desktop:/work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux
/lib$ cp -l libcrypt.so.* /work/nfs_root/fs_mini3/lib
dennis@dennis-desktop:/work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux
/lib$ cp libm-* /work/nfs_root/fs_mini3/lib
dennis@dennis-desktop:/work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux
/lib$ cp -l libm.so.* /work/nfs_root/fs_mini3/lib
dennis@dennis-desktop:/work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux
/lib$ cp libc-* /work/nfs_root/fs_mini3/lib
dennis@dennis-desktop:/work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux
/lib$ cp -l libc.so.* /work/nfs_root/fs_mini3/lib
dennis@dennis-desktop:/work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux
/lib$ cp -l ld-* /work/nfs_root/fs_mini3/lib
四、手工构建/etc 目录
/etc 目录存放的是系统程序的主配置文件,因此需要哪些配置文件取
决于要运行哪些系统程序。即使最小的系统也一定会运行1 号用户进
程init,所以我们至少要手工编写init 的主配置文件inittab。busybox
的inittab 文件的语法、语义与传统的SYSV 的inittab 有所不同。
inittab 文件中每个条目用来定义一个需要init 启动的子进程,并确定
它的启动方式,格式为<id>:<runlevel>:<action>:<process>。例如:
ttySAC0::askfirst:-/bin/sh
• <id>表示子进程要使用的控制台,若省略则使用与init 进程一
样的控制台
• <runlevel>表示运行级别,busybox init 程序这个字段没有意义
• <action>表示init 进程如何控制这个子进程
o sysinit:系统启动后最先执行,只执行一次,init 进程等
待它结束后才继续执行其它动作
o wait:系统执行完sysinit 条目后执行,只执行一次,init
进程等待它结束后才继续执行其它动作
o once:系统执行完wait 条目后执行,只执行一次,init
进程不等待它结束
o respawn:启动完once 进程后,init 进程监测发现子进程
退出时,重新启动它
o askfirst:启动完respawn 进程后,与respawn 类似,不
过init 进程先输出” Please press Enter to activate this
console“,等用户输入回车后才启动子进程
o shutdown:当系统关机时
o restart:Busybox 中配置了
CONFIG_FEATURE_USE_INITAB,并且init 进程接收
到SIGUP 信号时执行,先重新读取、解析/etc/inittab 文
件,再执行restart 程序
o ctrlaltdel:按下ctrl+alt+del 键时执行,不过在串口控制
台中无法输入它
• <process>表示进程对应的二进制文件。如果前面有-号,表示
该程序是“可以与用户进行交互的”
我们制作最简单的/etc/inittab 文件,其内容如下:
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a –r
制作最简单的脚本程序文件/etc/init.d/rcS,其内容如下:
#!/bin/sh
ifconfig eth0 192.168.2.17
修改shell 脚本文件/etc/init.d/rcS 的权限,以使其可被执行:
# chmod a+x /etc/init.d/rcS
五、手工构建最简化的/dev 目录
在linux 机器上,执行ls /dev 可看到几百个设备文件,我需要手工
创建它们吗?maybe,我只需要手工创建几个设备文件!我怎么知道
我应该创建哪几个设备文件呢?管它呢,先看看开发板上可爱的
linux 的反应再说。
启动Linux 操作系统,显示:
VFS: Mounted root (nfs filesystem).
Freeing init memory: 112K
Warning: unable to open an initial console.
这说明,内核已经成功挂载根文件系统,但却未能成功启动第1 个用
户进程init。通过错误消息“unable to open an initial console”搜索内
核源代码,找到init/main.c 文件。
748 static int noinline init_post(void)
749 {
750 free_initmem();
751 unlock_kernel();
752 mark_rodata_ro();
753 system_state = SYSTEM_RUNNING;
754 numa_default_policy();
755
756 if (sys_open((const char __user *) "/dev/console",
O_RDWR, 0) < 0)
757 printk(KERN_WARNING "Warning: unable to open
an initial console.\n");
758
759 (void) sys_dup(0);
760 (void) sys_dup(0);
761
762 if (ramdisk_execute_command) {
763 run_init_process(ramdisk_execute_command);
764 printk(KERN_WARNING "Failed to execute %s\n",
765 ramdisk_execute_command);
766 }
767
768 /*
769 * We try each of these until one succeeds.
770 *
771 * The Bourne shell can be used instead of init if we are
772 * trying to recover a really broken machine.
773 */
774 if (execute_command) {
775 run_init_process(execute_command);
776 printk(KERN_WARNING "Failed to execute
%s. Attempting "
777 "defaults...\n", execute_command);
778 }
779 run_init_process("/sbin/init");
780 run_init_process("/etc/init");
781 run_init_process("/bin/init");
782 run_init_process("/bin/sh");
783
784 panic("No init found. Try passing init= option to kernel.");
785 }
显然,内核错误是由175 行不能打开/dev/console 所致。通过查看已
经安装好的linux 机器的/dev/console 设备文件,可知其是字符设备
文件,主设备号为5,次设备号为1:
dennis@dennis-desktop:/work/nfs_root/fs_mini3/etc$ ls -l
/dev/console
crw------- 1 root root 5, 1 2010-04-08 08:40 /dev/console
因此,我们使用下面的命令创建它:
dennis@dennis-desktop:/work/nfs_root/fs_mini3/dev$ sud
o mknod console c 5 1
还需要创建其它设备文件吗?只有天知道!再看看linux 的反应。
VFS: Mounted root (nfs filesystem).
Freeing init memory: 112K
init: can't open '/dev/null': No such file or directory
这次我们有经验了,如法炮制,创建/dev/null 设备文件:
dennis@dennis-desktop:/work/nfs_root/fs_mini3/dev$ sudo mknod
null c 1 3
再次重启开发板上的linux,显示
VFS: Mounted root (nfs filesystem).
Freeing init memory: 112K
init started: BusyBox v1.7.0 (2010-04-03 23:53:55 CST)
starting pid 229, tty '': '/etc/init.d/rcS'
Please press Enter to activate this console.
starting pid 231, tty '': '/bin/sh'
#
哈哈,我们成功了,终于可以K 歌去了。
六、创建其它空目录
dennis@dennis-desktop:/work/nfs_root/fs_mini3$ mkdir home root
proc sys tmp mnt var
再次重启动开发板上的linux。咦,似乎有些问题。
VFS: Mounted root (nfs filesystem).
Freeing init memory: 112K
init started: BusyBox v1.7.0 (2010-04-03 23:53:55 CST)
starting pid 229, tty '': '/etc/init.d/rcS'
Please press Enter to activate this console.
starting pid 231, tty '': '/bin/sh'
# ps
PID Uid VSZ Stat Command
#
ps 竟然看不到任何进程的存在!让我想想。对了,ps 的机制是通过
查看/proc 中的内容来获得进程信息的。那么,目前/proc 里有哪些内
容呢?
# ls /proc
#
竟然空空如野!这可如何是好?
七、配置系统自动生成/proc 目录
其实/proc 是用来提供内核与进程信息的虚拟文件系统,由内核自动
生成目录下的内容。不过需要我们设置一下,将/etc/init.d/rcS 修改为:
#!/bin/sh
ifconfig eth0 192.168.2.17
mount -t proc none /proc
对于mount -t proc none /proc 的解释:通常情况下mount 命令应
该写为mount –t ext2 /dev/hdb1 /proc。但由于现在挂载的/proc 是虚
拟文件系统,它不与任何物理硬盘分区相对应,因此在表示物理硬盘
分区的位置用占位符none 来表示。
重启开发板上的linux,显示成功了:
Please press Enter to activate this console.
starting pid 232, tty '': '/bin/sh'
# ps
PID Uid VSZ Stat Command
1 0 3088 S init
2 0 SW< [kthreadd]
3 0 SWN [ksoftirqd/0]
4 0 SW< [events/0]
5 0 SW< [khelper]
41 0 SW< [kblockd/0]
42 0 SW< [ksuspend_usbd]
45 0 SW< [khubd]
47 0 SW< [kseriod]
59 0 SW [pdflush]
60 0 SW [pdflush]
61 0 SW< [kswapd0]
62 0 SW< [aio/0]
177 0 SW< [mtdblockd]
226 0 SW< [rpciod/0]
232 0 3092 S -sh
233 0 3092 R ps
#
八、利用udev 构建完整的/dev 目录
高兴地插入U 盘,内核显示识别到了U 盘:
# usb 1-1: new full speed USB device using s3c2410-ohci and
address 2
usb 1-1: not running at top speed; connect to a high speed hub
usb 1-1: configuration #1 chosen from 1 choice
scsi0 : SCSI emulation for USB Mass Storage devices
scsi 0:0:0:0: Direct-Access Teclast CoolFlash 0.00 PQ: 0
ANSI: 2
sd 0:0:0:0: [sda] 12560384 512-byte hardware sectors (6431 MB)
sd 0:0:0:0: [sda] Write Protect is off
sd 0:0:0:0: [sda] Assuming drive cache: write through
sd 0:0:0:0: [sda] 12560384 512-byte hardware sectors (6431 MB)
sd 0:0:0:0: [sda] Write Protect is off
sd 0:0:0:0: [sda] Assuming drive cache: write through
sda: sda1
sd 0:0:0:0: [sda] Attached SCSI removable disk
但当要使用的时候,却找不到设备文件:
# mount /dev/sda1 /mnt
mount: mounting /dev/sda1 on /mnt failed: No such file or directory
# ls /dev/sda1
ls: /dev/sda1: No such file or directory
/dev 目录下只有可怜巴巴的2 个设备文件。
# ls /dev
console null
在linux 机器上,执行ls /dev 可看到几百个设备文件,难道要我揉
着酸疼的眼睛查看这几百个设备文件的主、次设备号,然后再手工使
用mknod 命令来生成这几百个设备文件吗?那你不如杀了我算了!
其实构建/dev 目录有3 种方法:
• 创建静态设备文件:需要使用mknod 命令事先创建很多设备文
件,麻烦大了
• 使用devfs:使用上存在一些问题,在2.6.13 后已被废弃
• 使用udev(user dev),我们采用该方法
udev 的原理是:操作系统启动的时候将识别到的所有设备的信息自
动导出到/sys 目录,然后用户态的应用程序udev 根据/sys 中的设备
信息,自动在/dev 目录下创建所有正确的设备文件。因此我们要做
的就是:配置自动生成/sys 目录下的内容并调用mdev(mdev 是
busybox 中对udev 的简化实现)。
# ls /sys
# mount -t sysfs none /sys
# ls /sys
block class firmware kernel power
bus devices fs module
# ls /dev
console null
# mdev -s
# ls -l dev | wc -l
140
# ls /dev/sda*
/dev/sda /dev/sda1
可是,当我们将U 盘拔出后,发现/dev/sda1 并不自动消失;手工删
除/dev/sda1 后,再重新插入U 盘,/dev/sda1 也不会自动生成。我
们需进行如下操作,以使系统能够实现即插即用:
# echo /sbin/mdev > /proc/sys/kernel/hotplug
将上述工作放到rcS 中:
#!/bin/sh
ifconfig eth0 192.168.2.17
mount -t proc none /proc
mount -t sysfs none /sys
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
九、使用tmpfs 挂载/dev、/tmp、/var 目录
似乎我们的根文件系统已经相当完善了。但仔细想一想Nand flash
的擦写寿命是有限的这个事实,我们就应该明白,我们应该将/dev、
/tmp、/var 三个目录挂载为tmpfs 文件系统。修改rcS 如下:
#!/bin/sh
ifconfig eth0 192.168.2.17
mount -t proc none /proc
mount -t sysfs none /sys
mount -t tmpfs none /dev
mount -t tmpfs none /var
mount -t tmpfs none /tmp
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
十、制作根文件系统的jffs2 映像文件
根文件系统已经制作完毕,最后一个步骤是将其打包为jffs2 映像文
件,以供bootloader 将其烧录到nand flash 上。这只需要执行命令:
dennis@dennis-desktop:/work/nfs_root$ mkfs.jffs2 -n -s 512 -e
16KiB -d fs_mini3 -o fs_mini3.jffs2
这其中:
• -n 表示不要在每个擦除块上都加上清除标记
• -s 512 指明一页大小为512 字节
• -e 16KiB 指明一个擦除块大小为16KB
• -d fs_mini3 指明要打包的目录
• -o fs_mini3.jffs2 指明最终的映像文件名
但由于jffs2 映像文件制作工具程序mkfs.jffs2 尚未安装,另外该程
序需要用到zlib 库,因此我们必须先安装它们:
安装zlib(编译MTD 设备工具包需要它)
• tar xzvf zlib-1.2.3.tar.gz
• cd zlib-1.2.3
• ./configure –shared –prefix=/usr
• make
• sudo make install
安装MTD 工具包(其中含有jffs2 映像文件制作工具)
• tar xjvf mtd-utils-05.07.23.tar.bz2
• cd mtd-utils-05.07.23/util
• make
• sudo make install
搞定