1.使用BusyBox编译根文件系统
mkdir rootfs
//创建好的 rootfs 子目录就用来存放我们的根文件系统了。
tar -vxjf busybox-1.29.0.tar.bz2
//解压busybox至任意目录
打开 busybox 的顶层 Makefile,添加 ARCH 和 CROSS_COMPILE的值:
164 CROSS_COMPILE ?= /usr/local/arm/gcc-linaro-4.9.4-2017.01-
x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
......
190 ARCH ?= arm
2、busybox 中文字符支持
打开文件 busybox-1.29.0/libbb/unicode.c,
3.配置 busybox
①、defconfig,缺省配置,也就是默认配置选项。
②、allyesconfig,全选配置,也就是选中 busybox 的所有功能。
③、allnoconfig,最小配置。
一般使用make defconfig ,然后打开make menuconfig,
a. Settings -> Build static binary (no shared libs) 注意不要选中!
选项“Build static binary (no shared libs)”用来决定是静态编译 busybox 还是动态编译,静
态编译的话就不需要库文件,但是编译出来的库会很大。动态编译的话要求根文件系统中有库
文件,但是编译出来的 busybox 会小很多。这里我们不能采用静态编译!因为采用静态编译的
话 DNS 会出问题!无法进行域名解析。
b. Settings -> vi-style line editing commands ,选中这个
c. Linux Module Utilities -> Simplified modutils ,取消勾选。
d.Linux System Utilities -> mdev (16 kb),确保下面的全部选中。
d.Settings ->Support Unicode (选中)-> Check $LC_ALL, $LC_CTYPE and $LANG environment variables(选中)
4、编译 busybox
配置好 busybox 以后就可以编译了,我们可以指定编译结果的存放目录,我们肯定要将编
译结果存放到前面创建的 rootfs 目录中,输入如下命令:
make
make install CONFIG_PREFIX=/home/linux/nfs/rootfs
件 。
busybox 的工作就完成了,但是此时的根文件系统还不能使用。
5.向根文件系统添加 lib 库
a.向 rootfs 的“/lib”目录添加库文件
在 rootfs 中创建一个名为“lib”的文件夹,命令如下:
mkdir lib
lib 库文件从交叉编译器中获取,前面搭建交叉编译环境的时候将交叉编译器存放到了“/usr/local/arm/”目录中。
cd /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib
cp *so* *.a /home/linux/nfs/rootfs/lib/ -d
#后面的“-d”表示拷贝符号链接
但是其中ld-linux-armhf.so.3是软链接,只有24kb,因此需要删除它,然后复制本尊。
rm ld-linux-armhf.so.3
cd /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib
cp ld-linux-armhf.so.3 /home/linux/nfs/rootfs/lib/
cp *so* *.a /home/linux/nfs/rootfs/lib/ -d
b.向 rootfs 的“usr/lib”目录添加库文件
在 rootfs 的 usr 目录下创建一个名为 lib 的目录,将如下目录中的库文件拷贝到 rootfs/usr/lib
目录下:
cd /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib
*so* *.a /home/linux/nfs/rootfs/usr/lib/ -d
c.创建其他文件夹
在根文件系统中创建其他文件夹,如 dev、proc、mnt、sys、tmp 和 root 等
6.根文件系统的初步测试
nfs挂载
uboot 里面的 bootargs 环境变量会设置“root”的值,所以我们将 root 的值改为 NFS 挂载即可。在 Linux 内核源码里面有相应的文档讲解如何设置,文档为 Documentation/filesystems/nfs/nfsroot.txt,格式如下:
root=/dev/nfs nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>] ip=<client-ip>:<server-ip>:<gwip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>
<server-ip>:服务器 IP 地址,也就是存放根文件系统主机的 IP 地址,那就是 Ubuntu 的 IP
地址,比如我的 Ubuntu 主机 IP 地址为 192.168.1.250。
<root-dir>:根文件系统的存放路径,比如我的就是/home/zuozhongkai/linux/nfs/rootfs。
<nfs-options>:NFS 的其他可选选项,一般不设置。
<client-ip>:客户端 IP 地址,也就是我们开发板的 IP 地址,Linux 内核启动以后就会使用
此 IP 地址来配置开发板。此地址一定要和 Ubuntu 主机在同一个网段内,并且没有被其他的设
备使用,在 Ubuntu 中使用 ping 命令 ping 一下就知道要设置的 IP 地址有没有被使用,如果不能
ping 通就说明没有被使用,那么就可以设置为开发板的 IP 地址,比如我就可以设置为
192.168.1.251。
<server-ip>:服务器 IP 地址,前面已经说了。
<gw-ip>:网关地址,我的就是 192.168.1.1。
<netmask>:子网掩码,我的就是 255.255.255.0。
<hostname>:客户机的名字,一般不设置,此值可以空着。
<device>:设备名,也就是网卡名,一般是 eth0,eth1….,正点原子的 I.MX6U-ALPHA 开
发板的 ENET2 为 eth0,ENET1 为 eth1。如果你的电脑只有一个网卡,那么基本只能是 eth0。
这里我们使用 ENET2,所以网卡名就是 eth0。
<autoconf>:自动配置,一般不使用,所以设置为 off。
<dns0-ip>:DNS0 服务器 IP 地址,不使用。
<dns1-ip>:DNS1 服务器 IP 地址,不使用。
根据上面的格式 bootargs 环境变量的 root 值如下:
root=/dev/nfs nfsroot=192.168.1.250:/home/zuozhongkai/linux/nfs/rootfs,proto=tcp rw
ip=192.168.1.251:192.168.1.250:192.168.1.1:255.255.255.0::eth0:off
“proto=tcp”表示使用 TCP 协议,“rw”表示 nfs 挂载的根文件系统为可读可写。启动开发
板,进入 uboot 命令行模式,然后重新设置 bootargs 环境变量,命令如下:
setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.1.250:
/home/zuozhongkai/linux/nfs/rootfs,proto=tcp rw ip=192.168.1.251:192.168.1.250:192.168.1.1:
255.255.255.0::eth0:off' //设置 bootargs
saveenv //保存环境变量
设置好以后使用“boot”命令启动 Linux 内核。如果没有启动进入根文件系统的话可以重启一次开发板试试。
可以看出 ls 命令工作正常!那么是不是说明我们的 rootfs 就制作成功了呢?大家注意,在
进入根文件系统的时候会有下面这一行错误提示:
can't run '/etc/init.d/rcS': No such file or directory
7.完善根文件系统
a.创建/etc/init.d/rcS 文件,輸入如下内容
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 //使用 mount 命令来挂载所有的文件系统,这些文件系统由文件/etc/fstab 来指定,
所以我们一会还要创建/etc/fstab 文件。
8 mkdir /dev/pts
9 mount -t devpts devpts /dev/pts //devpts 挂载到/dev/pts 目录中。
10
11 echo /sbin/mdev > /proc/sys/kernel/hotplug
12 mdev -s
第 11 和 12 行,使用 mdev 来管理热插拔设备,通过这两行,Linux 内核就可以在/dev 目录
下自动创建设备节点。关于 mdev 的详细内容可以参考 busybox 中的 docs/mdev.txt 文档。
chmod 777 rcS
设置好以后就重新启动 Linux 内核,出现
b. 创建/etc/fstab 文件
在 rootfs 中创建/etc/fstab 文件,fstab 在 Linux 开机以后自动配置哪些需要自动挂载的分区,
格式如下:
<file system> <mount point> <type> <options> <dump> <pass>
<file system>:要挂载的特殊的设备,也可以是块设备,比如/dev/sda 等等。
<mount point>:挂载点。
<type>:文件系统类型,比如 ext2、ext3、proc、romfs、tmpfs 等等。
<options>:挂载选项,在 Ubuntu 中输入“man mount”命令可以查看具体的选项。一般使
用 defaults,也就是默认选项,defaults 包含了 rw、suid、 dev、 exec、 auto、 nouser 和 async。
<dump>:为 1 的话表示允许备份,为 0 不备份,一般不备份,因此设置为 0。
<pass>:磁盘检查设置,为 0 表示不检查。根目录‘/’设置为 1,其他的都不能设置为 1,
其他的分区从 2 开始。一般不在 fstab 中挂载根目录,因此这里一般设置为 0。
按照上述格式,在fstab文件中输入如下内容:
1 #<file system> <mount point> <type> <options> <dump> <pass>
2 proc /proc proc defaults 0 0
3 tmpfs /tmp tmpfs defaults 0 0
4 sysfs /sys sysfs defaults 0 0
fstab 文件创建完成以后重新启动 Linux,没有任何提示错误信息。
但是还需要创建一个 /etc/inittab
c.创建/etc/inittab 文件
inittab 的详细内容可以参考 busybox 下的文件 examples/inittab。init 程序会读取/etc/inittab
这个文件,inittab 由若干条指令组成。每条指令的结构都是一样的,由以“:”分隔的 4 个段组
成,格式如下:
<id>:<runlevels>:<action>:<process>
<id>:每个指令的标识符,不能重复。但是对于 busybox 的 init 来说,<id>有着特殊意义。
对于 busybox 而言<id>用来指定启动进程的控制 tty,一般我们将串口或者 LCD 屏幕设置为控
制 tty。
<runlevels>:对 busybox 来说此项完全没用,所以空着。
<action>:动作,用于指定<process>可能用到的动作。如下所示:
<process>:具体的动作,比如程序、脚本或命令等。
参考 busybox 的 examples/inittab 文件,我们也创建一个/etc/inittab,
1 #etc/inittab
2 ::sysinit:/etc/init.d/rcS //系统启动以后运行/etc/init.d/rcS 这个脚本文件。
3 console::askfirst:-/bin/sh //将 console 作为控制台终端,也就是 ttymxc0。
4 ::restart:/sbin/init //重启的话运行/sbin/init。
5 ::ctrlaltdel:/sbin/reboot //按下 ctrl+alt+del 组合键的话就运行/sbin/reboot,重启系统。
6 ::shutdown:/bin/umount -a -r //关机的时候执行/bin/umount,也就是卸载各个文件系统。
7 ::shutdown:/sbin/swapoff -a //关机的时候执行/sbin/swapoff,也就是关闭交换分区。
/etc/inittab 文件创建好以后就可以重启开发板即可,至此!根文件系统要创建的文件就已经
全部完成了。
8.根文件系统其他功能测试
a. 软件运行测试
我们使用 Linux 的目的就是运行我们自己的软件,我们编译的应用软件一般都使用动态库,
使用动态库的话应用软件体积就很小,但是得提供库文件,库文件我们已经添加到了根文件系
统中。我们编写一个小小的测试软件来测试一下库文件是否工作正常。
1 #include <stdio.h>
2
3 int main(void)
4 {
5 while(1) {
6 printf("hello world!\r\n");
7 sleep(2);
8 }
9 return 0;
10 }
arm-linux-gnueabihf-gcc hello.c -o hello
file hello //查看 hello 的文件类型以及编码格式
输出
hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked……
ARM 架构的,并且是动态链接的。
我们将其拷贝至到 rootfs/drivers 目录下,在开发板中输入如下命令来执行这
个可执行文件:
cd /drivers //进入 drivers 目录
./hello //执行 hello
结果如下图所示
b. 中文字符测试
b.1.设置 SecureCRT 使用 UTF-8 编码
b.2.创建中文文件
在 ubuntu 中向在 rootfs 目录新建一个名为“中文测试”的文件夹,然后在 SecureCRT 下查
看中文名能不能显示正确。输入“ls”命令。\
c. 开机自启动测试
进入根文件系统的时候会运行/etc/init.d/rcS 这个 shell 脚本,因此我们可以在这个脚本里面添加自启动相关内容
1 #!/bin/sh
2 PATH=/sbin:/bin:/usr/sbin:/usr/bin
3 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib
4 runlevel=S
5 umask 022
6 export PATH LD_LIBRARY_PATH runlevel
7
8 mount -a
9 mkdir /dev/pts
10 mount -t devpts devpts /dev/pts
11
12 echo /sbin/mdev > /proc/sys/kernel/hotplug
13 mdev -s
14
15 #开机自启动
16 cd /drivers
17 ./hello &
18 cd /
以重启开发板,看看 hello 这个软件会不会自动运行,可以自动运行,说明设置成功。
d.外网连接测试
ping www.baidu.com
可以看出,测试失败,提示 www.baidu.com 是个“bad address”,也就是地址不对,显然我
们的地址是正确的。之所以出现这个错误提示是因为 www.baidu.com 的地址解析失败了,并没
有解析出其对应的 IP 地址。我们需要配置域名解析服务器的 IP 地址,一般域名解析地址可以
设置为所处网络的网关地址,比如 192.168.1.1。也可以设置为 114.114.114.114,这个是运营商
的域名解析服务器地址。
在 rootfs 中新建文件/etc/resolv.conf,然后在里面输入如下内容:
1 nameserver 114.114.114.114
2 nameserver 192.168.1.1
//nameserver 表示这是个域名服务器,设置了两个域名服务器地址:
114.114.114.114 和 192.168.1.1
如果使用“udhcpc”命令自动获取 IP 地址,“udhcpc”命令会修改 nameserver 的值,一般是将其设置为对应的网关地址。修改好以后保存退出,重启开发板!
重启以后重新 ping 一下百度官网,成功。