根文件系统定义

根文件系统首先是内核启动时所 mount(挂载)的第一个文件系统,内核代码映像文件保存在根文件系统中,而系统引导启动程序会在根文件系统挂载之后从中把一些基本的初始化脚本和服务等加载到内存中去运行。

根文件系统是 Linux 内核启动以后挂载(mount)的第一个文件系统,然后从根文件系统中读取初始化脚本,比如 rcS, inittab 等。根文件系统和 Linux 内核是分开的,单独的 Linux 内核是没法正常工作的,必须要搭配根文件系统。

常用子目录

目录 含义
/bin 存放可执行文件一般都是命令
/dev 设备文件
/etc 各种配置文件
/lib linux所需的必备库文件(共享库)
/mnt 临时挂载目录(一般为空)
/proc 作为 proc 文件系统的挂载点(文件是临时存在的,一般存储系统运行信息文件)
/usr Unix 操作系统软件资源目录
/var 存放一些可以改变的数据
/sbin 此目录页用户存放一些可执行文件
/sys 此目录作为 sysfs 文件系统的挂载点
/opt 可选的文件、软件存放区(用户选择放哪些)

BusyBox构建根文件系统

顶层Makefile修改

1
2
3
164 CROSS_COMPILE ?= /usr/local/arm/gcc/bin/arm-linux-gnueabihf-
......
190 ARCH ?= arm

修改busybox支持中文字符

打开/libbb/printable_string.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
12 const char* FAST_FUNC printable_string(uni_stat_t *stats, const char
*str)
13 {
14 char *dst;
15 const char *s;
16
17 s = str;
18 while (1) {
......
30 if (c < ' ')
31 break;
32 /* 注释掉下面这个两行代码 */
33 /* if (c >= 0x7f)
34 break; */
35 s++;
36 }
37
38 #if ENABLE_UNICODE_SUPPORT
39 dst = unicode_conv_to_printable(stats, str);
40 #else
41 {
42 char *d = dst = xstrdup(str);
43 while (1) {
44 unsigned char c = *d;
45 if (c == '\0')
46 break;
47 /* 修改下面代码 */
48 /* if (c < ' ' || c >= 0x7f) */
49 if( c < ' ')
50 *d = '?';
51 d++;
52 }
......
59 #endif
60 return auto_string(dst);
61 }

接着打开文件 busybox-1.29.0/libbb/unicode.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
1003 static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char *src, unsigned width, int flags)
1004 {
1005 char *dst;
1006 unsigned dst_len;
1007 unsigned uni_count;
1008 unsigned uni_width;
1009
1010 if (unicode_status != UNICODE_ON) {
1011 char *d;
1012 if (flags & UNI_FLAG_PAD) {
1013 d = dst = xmalloc(width + 1);
......
1022 /* 修改下面一行代码 */
1023 /* *d++ = (c >= ' ' && c < 0x7f) ? c : '?'; */
1024 *d++ = (c >= ' ') ? c : '?';
1025 src++;
1026 }
1027 *d = '\0';
1028 } else {
1029 d = dst = xstrndup(src, width);
1030 while (*d) {
1031 unsigned char c = *d;
1032 /* 修改下面一行代码 */
1033 /* if (c < ' ' || c >= 0x7f) */
1034 if(c < ' ')
1035 *d = '?';
1036 d++;
1037 }
1038 }
......
1044 return dst;
1045 }
......
1047
1048 return dst;
1049 }

配置busybox

要先对 busybox 进行默认的配置,有以下几种配置选项:

①defconfig,缺省配置,也就是默认配置选项。
②allyesconfig,全选配置,也就是选中 busybox 的所有功能。
③allnoconfig,最小配置。

也可以图形化配置

1
make menuconfig

配置路径:

1
2
3
Location:
-> Settings
-> Build static binary (no shared libs)

这个选项不选。

选项“Build static binary (no shared libs)”用来决定是静态编译 busybox 还是动态编译,静态编译的话就不需要库文件,但是编译出来的库会很大。动态编译的话要求根文件系统中有库文件,但是编译出来的 busybox 会小很多。不能采用静态编译!因为采用静态编译的话 DNS 会出问题!无法进行域名解析 。

继续配置,选上。

1
2
3
Location:
-> Settings
-> vi-style line editing commands
1
2
3
Location:
-> Linux Module Utilities
-> Simplified modutils

不选simplified modutils

1
2
3
Location:
-> Linux System Utilities
-> mdev (16 kb) //确保下面的全部选中,默认都是选中的

最后使能busybox的unicode编码支持中文:

1
2
3
4
Location:
-> Settings
-> Support Unicode //选中
-> Check $LC_ALL, $LC_CTYPE and $LANG environment variables //选中

编译busybox

要将编译结果存放到前面创建的 rootfs 目录中

1
make install CONFIG_PREFIX=/home/moss/linux/nfs/rootfs

busybox 的工作就完成了,但是此时的根文件系统还不能使用,还需要一些其他的文件,我们继续来完善 rootfs。

向根文件系统添加 lib 库

Linux 中的应用程序一般都是需要动态库的 。所以在rootfs中创建一个lib文件夹。

1
mkdir lib

库文件从哪里来呢? lib 库文件从交叉编译器中获取,前面我们搭建交叉编译环境的时候将交叉编译器存放到了“/usr/local/arm/”目录中。

进入对应路径的目录:

1
cd	/usr/local/arm/gcc/arm-linux-gnueabihf/libc/lib

此目录下有很多的* so*(*是通配符)和.a 文件,这些就是库文件,将此目录下所有的so*和.a文件都拷贝到 rootfs/lib 目录中,拷贝命令如下:

1
cp *so* *.a /home/moss/linux/nfs/rootfs/lib/ -d

后面的“-d”表示拷贝符号链接,这里有个比较特殊的库文件: ld-linux-armhf.so.3,此库文件也是个符号链接,

要把它变成本身的文件。

1
rm ld-linux-armhf.so.3

然 后 重 进 入 到 /usr/local/arm/gcc/armlinux-gnueabihf/libc/lib 目录中,重新拷贝 ld-linux-armhf.so.3,命令:

1
cp ld-linux-armhf.so.3 /home/moss/linux/nfs/rootfs/lib/

继续进入如下目录中:

1
2
cd	/usr/local/arm/gcc/arm-linux-gnueabihf/libc/lib
cp *so* *.a /home/moss/linux/nfs/rootfs/lib/ -d

向rootfs的usr/lib目录添加库文件

在 rootfs 的 usr 目录下创建一个名为 lib 的目录 ,之前arm-linux-gnueabihf/usr/lib的so和.a库文件复制到rootfs/usr/lib目录中

1
2
3
cp *so* *.a /home/moss/linux/nfs/rootfs/usr/lib/ -d
cd /home/moss/linux/nfs/rootfs //进入根文件系统目录
du ./lib ./usr/lib/ -sh //查看 lib 和 usr/lib 这两个目录的大小

在根文件系统中创建其他文件夹,如 dev、 proc、 mnt、 sys、 tmp 和 root 等 .

初步测试根文件系统

首先使用nfs挂载根文件系统,首先熟悉bootargs环境变量的root值的格式:

1
root=/dev/nfs nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>] ip=<client-ip>:<server-ip>:<gwip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>

:服务器IP地址。(也就是Ubuntu的IP)

:根文件系统存放路径,也就是rootfs的绝对路径,可用pwd命令查询。

:NFS的其他选项一般不用。

:开发板IP地址

:服务器IP

:网关地址,我的是192.168.1.1

:子网掩码,255.255.255.0

:客户机名字,一般不用

:网卡名字:一般eth0,eht1,之类的

:自动配置,一般为off

:DNS0服务器IP地址,不使用

:DNS1服务器IP地址,不使用

1
2
3
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.1.115:
/home/moss/linux/nfs/rootfs,proto=tcp rw ip=192.168.1.114:192.168.1.115:192.168.1.1:
255.255.255.0::eth0:off'

完善根文件系统

Linux 内核启动以后需要启动一些服务,而 rcS 就是规定启动哪些文件的脚本文件。在 rootfs 中创建/etc/init.d/rcS 文件

1
2
3
4
5
6
7
8
9
10
11
12
1 #!/bin/sh
2
3 PATH=/sbin:/bin:/usr/sbin:/usr/bin:$PATH
4 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib
5 export PATH LD_LIBRARY_PATH
6
7 mount -a
8 mkdir /dev/pts
9 mount -t devpts devpts /dev/pts
10
11 echo /sbin/mdev > /proc/sys/kernel/hotplug
12 mdev -s

第 1 行,表示这是一个 shell 脚本。
第 3 行, PATH 环境变量保存着可执行文件可能存在的目录,这样我们在执行一些命令或者可执行文件的时候就不会提示找不到文件这样的错误。
第 4 行, LD_LIBRARY_PATH 环境变量保存着库文件所在的目录。
第 5 行,使用 export 来导出上面这些环境变量,相当于声明一些“全局变量”。
第 7 行,使用 mount 命令来挂载所有的文件系统,这些文件系统由文件/etc/fstab 来指定,所以我们一会还要创建/etc/fstab 文件。
第 8 和 9 行,创建目录/dev/pts,然后将 devpts 挂载到/dev/pts 目录中。
第 11 和 12 行,使用 mdev 来管理热插拔设备,通过这两行, Linux 内核就可以在/dev 目录下自动创建设备节点。

使用chmod给予/ec/init.d/rcS 可执行权限

创建/etc/fstab 文件

fstab 在 Linux 开机以后自动配置哪些需要自动挂载的分区,格式如下:

1
<file system> <mount point> <type> <options> <dump> <pass>

:要挂载的特殊的设备,也可以是块设备,比如/dev/sda

:挂载点。

:文件系统类型,比如 ext2、 ext3、 proc、 romfs、 tmpfs 等等。

:挂载选项,在 Ubuntu 中输入“man mount”命令可以查看具体的选项。一般使用 defaults,也就是默认选项,

defaults 包含了 rw、 suid、 dev、 exec、 auto、 nouser 和 async。

:为 1 的话表示允许备份,为 0 不备份,一般不备份,因此设置为 0。

:磁盘检查设置,为 0 表示不检查。根目录‘/’设置为 1,其他的都不能设置为 1,其他的分区从 2 开始。一般不在

fstab 中挂载根目录,因此这里一般设置为 0。

1
2
3
4
#<file system> <mount point> <type> <options> <dump> <pass>
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0

创建/etc/inittab 文件

init 程序会读取/etc/inittab这个文件, inittab 由若干条指令组成。每条指令的结构都是一样的,由以“:”分隔的 4 个段组成,格式如下:

1
<id>:<runlevels>:<action>:<process>

:每个指令的标识符,不能重复。但是对于 busybox 的 init 来说, 有着特殊意义。对于 busybox 而言用来指定启动进程的控制 tty,一般我们将串口或者 LCD 屏幕设置为控制 tty。

: 对 busybox 来说此项完全没用,所以空着。

:动作,用于指定可能用到的动作。 : 具体的动作,比如程序、脚本或命令等。动作表如下:

1
2
3
4
5
6
7
8
								示例代码 38.4.3.1 /etc/inittab 文件
1 #etc/inittab
2 ::sysinit:/etc/init.d/rcS
3 console::askfirst:-/bin/sh
4 ::restart:/sbin/init
5 ::ctrlaltdel:/sbin/reboot
6 ::shutdown:/bin/umount -a -r
7 ::shutdown:/sbin/swapoff -a

第 2 行,系统启动以后运行/etc/init.d/rcS 这个脚本文件。
第 3 行,将 console 作为控制台终端,也就是 ttymxc0。
第 4 行,重启的话运行/sbin/init。
第 5 行,按下 ctrl+alt+del 组合键的话就运行/sbin/reboot,看来 ctrl+alt+del 组合键用于重启系统。
第 6 行,关机的时候执行/bin/umount,也就是卸载各个文件系统。
第 7 行,关机的时候执行/sbin/swapoff,也就是关闭交换分区。