[ARM Linux系统移植] 根文件系统构建
linux 移植“三巨头”,目前已经完成了两个:u-boot 和 linux 内核。本篇就说明剩下的最后一个——根文件系统。
根文件系统由一堆系统所需要的文件构成,我们这里使用 busybox 来构建,使用的版本为 1.29.0。
1. 编译 busybox
首先我们修改 busybox 的顶层 makefile,指定 CROSS_COMPILE 和 ARCH 的值,这和之前的 u-boot、linux 的移植是一样的。CROSS_COMPILE 这边我们使用绝对路径,确保编译不出错。
- CROSS_COMPILE ?= /home/tim/tool/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
- ……
- ARCH ?= arm
接着设置配置选项:
- > make defconfig
- > make menuconfig
在图形化配置界面中,我们需要确认和更改以下选项:
Settings - Build static binary (no shared libs):确认这个选项没有开启。需要使用动态编译,静态编译会使得编译出的文件非常大。
Settings - vi-style line editing commands:开启这个选项。
Linux Module Utilities - Simplified modutils:取消这个选项。
Linux System Utilities - mdev (16 kb):确认这个选项以及子目录下的选项均已开启。
Settings - Support Unicode - Check $LC_ALL, $LC_CTYPE and $LANG environment variables:开启这个选项。
保存好配置之后,就可以开始编译了,执行以下命令:
- > make
- > make install CONFIG_PREFIX=/home/time/nfs/rootfs
其中,CONFIG_PREFIX 指定编译结果安装的目录,这里我们直接安装到 nfs 服务器所在目录。
2. 添加库文件
由于我们是动态编译 busybox 的,所以需要将交叉编译环境的库文件拷贝到第 1 节中安装的根文件系统中。
lib 目录
首先我们在根文件系统中创建 lib 目录:
- > cd /home/time/nfs/rootfs
- > mkdir lib
将交叉编译环境 libc/lib 下的文件拷贝其中:
- > cd gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/lib
- > cp *so* *.a /home/time/nfs/rootfs/lib/ -d
cp 指令的 -d 选项表示拷贝符号链接。这边有个比较特殊的库文件 ld-linux-armhf.so.3,它是一个软链接,但是 ld-linux-armhf.so.3 不能作为符号链接,否则在根文件系统中无法执行。
需要做的是,先将 rootfs/lib 下的删除:
- > rm ld-linux-armhf.so.3
然后不带 -d 选项重新复制:
- > cp ld-linux-armhf.so.3 /home/time/nfs/rootfs/lib/
接着同样拷贝交叉编译环境下的 lib 目录:
- > cd gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/lib
- > cp *so* *.a /home/time/nfs/rootfs/lib/ -d
至此,rootfs/lib 目录就“制作”完毕了。
usr/lib 目录
首先我们在根文件系统中创建 usr/lib 目录:
- > cd /home/time/nfs/rootfs
- > mkdir usr/lib
接着将交叉编译环境下 libc/usr/lib 下内容拷贝其中:
- > cd gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/lib
- > cp *so* *.a /home/time/nfs/rootfs/usr/lib/ -d
3. 根文件系统测试
接下来我们先初步测试一下制作的根文件系统系统是否可行。在此之前,我们再建立一下常用的文件夹,建立好的所有文件夹如下图所示:

我们使用 NFS 挂载根文件系统,需要设置的参数如下:
- root=/dev/nfs
- nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]
- ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>
在 u-boot 命令行模式下,重新设置 bootargs 环境变量如下:
- > setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=169.254.155.83:/home/tim/nfs/rootfs,proto=tcp rw ip=169.254.155.84:169.254.155.83:169.254.1.1:255.255.0.0::eth0:off'
- > saveenv
参数中一些 ip 地址的设置,便不再赘述。proto=tcp 表示使用 tcp 协议,rw 表示根文件系统可读可写;eth0 是网卡名,对应开发板上的 ENET2 网口。
设置好后重启开发板,就可以进入到文件系统了。但是会有
- can't run '/etc/init.d/rcS': No such file or directory
的提示。即虽然根文件系统可以正常使用了,但还需要进一步的完善。
4. 完善根文件系统
创建 etc/init.d/rcS 文件
linux 启动之后需要启动一些服务,rcS 就是规定启动哪些文件的脚本文件。我们创建 etc/init.d/rcS 文件,内容如下:
- #!/bin/sh
- PATH=/sbin:/bin:/usr/sbin:/usr/bin:$PATH
- LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib
- export PATH LD_LIBRARY_PATH
- mount -a
- mkdir /dev/pts
- mount -t devpts devpts /dev/pts
- echo /sbin/mdev > /proc/sys/kernel/hotplug
- mdev -s
注意需要赋予其可执行权限:
- > chmod 777 rcS
创建 etc/fstab 文件
fstab 在 linux 开机以后自动配置哪些需要自动挂载的分区。创建的内容如下:
- #<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 文件
linux init 程序会读取 inittab 文件,其内容由若干条指令组成。创建的内容如下:
- #etc/inittab
- ::sysinit:/etc/init.d/rcS
- console::askfirst:-/bin/sh
- ::restart:/sbin/init
- ::ctrlaltdel:/sbin/reboot
- ::shutdown:/bin/umount -a -r
- ::shutdown:/sbin/swapoff -a
5. 根文件系统测试
最后,我们编译一个简单的 "hello world" 程序,以此验证根文件系统中的库文件是否正常。编写 hello.c 如下:
- #include <stdio.h>
- int main()
- {
- while (1)
- {
- printf("Hello World!\r\n");
- sleep(2);
- }
- return 0;
- }
交叉编译:
- > arm-linux-gnueabihf-gcc hello.c -o hello
在开发板上运行,如果正常输出 ""Hello World!" 字样,就代表根文件系统相应的库文件正常。
结尾
翻找之前做的笔记,移植部分的第一篇笔记是 2020-12-6 开始写的。这一下子都过去八个月了,学习过程拉的着实有点长。这篇文章就算是移植部分的结束了,但有些恍惚,感觉什么都没学,又忘记当初为什么学了。
转而想了又想,也算是终于要开始 linux 驱动学习的重头戏了。希望之后一步一步实现自己的目标,继续努力!