LV05-03-根文件系统-02-busybox构建根文件系统
本文主要是使用busybox构建根文件系统的相关笔记。若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows版本 | windows11 |
Ubuntu版本 | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
终端软件 | MobaXterm(Professional Edition v23.0 Build 5042 (license)) |
Linux开发板 | 正点原子 i.MX6ULL Linux 阿尔法开发板 |
uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) |
linux内核 | linux-4.15(NXP官方提供) |
Win32DiskImager | Win32DiskImager v1.0 |
点击查看本文参考资料
分类 | 网址 | 说明 |
官方网站 | https://www.arm.com/ | ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档 |
https://www.nxp.com.cn/ | NXP官方网站 | |
https://www.nxpic.org.cn/ | NXP 官方社区 | |
https://u-boot.readthedocs.io/en/latest/ | u-boot官网 | |
https://www.kernel.org/ | linux内核官网 |
点击查看相关文件下载
分类 | 网址 | 说明 |
NXP | https://github.com/nxp-imx | NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库 |
https://elixir.bootlin.com/linux/latest/source | 在线阅读linux kernel源码 | |
nxp-imx/linux-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga | NXP linux内核仓库tags中的rel_imx_4.1.15_2.1.0_ga | |
nxp-imx/uboot-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga | NXP u-boot仓库tags中的rel_imx_4.1.15_2.1.0_ga | |
I.MX6ULL | i.MX 6ULL Applications Processors for Industrial Products | I.MX6ULL 芯片手册(datasheet,可以在线查看) |
i.MX 6ULL Applications ProcessorReference Manual | I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网) |
一、文件准备
这里我们主要是获取 Busybox 源码。
1. 源码获取途径
我们可以从这里获取 Busybox 的源码:Index of /downloads (busybox.net)
2. 获取源码文件
我下载的是 busybox-1.22.1.tar.bz2 这个压缩包并将其存放于 ubuntu 下的共享文件夹下,路径为:
1 | /mnt/hgfs/Sharedfiles/4I.MX6U/busybox-1.22.1.tar.bz2 |
下载的时候可能会非常慢,要是特别慢的话,有条件的可以科学处理一下,哈哈。
3. 拷贝解压
然后我们可以将这个源码文件拷贝解压到 ubuntu 中,方便后边编译,我们可以在终端执行以下命令:
1 | cp /mnt/hgfs/Sharedfiles/4I.MX6U/busybox-1.22.1.tar.bz2 ~/5ALPHA # 拷贝压缩包 |
然后我们可以得到下边这些文件(通过 ls 命令查看):
二、根文件系统移植
根文件系统移植我们主要是使用 Busybox 来进行根文件系统的制作,前边我们已经准备好了相关的文件并解压了,我们进入 Busybox 源码的顶层目录:
1 | cd ~/5ALPHA/busybox-1.22.1/ |
1. 配置 busybox
与我们编译 Uboot、 Linux kernel 一样,我们要先对 busybox 进行默认的配置,有以下几种配置选项:
1 | make defconfig # 缺省配置,也就是默认配置选项。 |
我们一般使用默认配置即可,注意这里一定要先配置一下再编译,否则最后编译出来的根文件系统可能会少很多命令。默认配置结束后我们就可以进行一些其他的配置了,busybox 具有和 linux 内核类似的图形配置界面,我们可以通过下边的命令进入图形配置界面:
1 | make menuconfig |
1.1 交叉编译器配置
配置界面如下:
我们在进行根文件系统制作的时候需要先配置一下 busybox 使用的编译器,配置如下信息即可:
1 | Busybox Configuration |
配置完成后通过方向键选择 Exit 退出,并保存配置信息。
点击查看交叉编译器版本
我这里使用的是 gcc version 4.9.4 (Linaro GCC 4.9-2017.01) ,交叉编译器前缀为 arm-linux-gnueabihf-,直接使用下边的命令就能看到交叉编译器的版本啦:
1 | arm-linux-gnueabihf-gcc -v |
1.2 动态编译配置
静态编译的话就不需要库文件,但是编译出来的库会很大。动态编译的话要求根文件系统中有库文件,但是编译出来的 busybox 会小很多。正点原子的资料中写到这里我们不能采用静态编译,因为采用静态编译的话 DNS 会出问题,无法进行域名解析, 我就暂时先按这个来啦。
1 | Busybox Configuration |
保证这个选项不要选中即可。
1.3 vi编辑器配置
1 | Busybox Configuration |
1.4 Simplified modutils配置
1 | Busybox Configuration |
确保 Simplified modutils 这个选项没有被选中,不选中这个选项的时候下边几个选项会显示,下边几个选项是与 linux 模块加载卸载相关的,一定要选为 *。
1.5 mdev 配置
1 | Busybox Configuration |
确保这几个选项是选中的(默认也是选中的)。
2. 编译 busybox
上一步配置完成就可以开始编译 busybox 了,我们可以执行如下命令:
1 | make |
这个比较简单,只需要一个 make 命令就可以开始编译了,这是因为我们在图形配置界面中已经配置了交叉编译器了。编译完成后,会在源码的顶层目录下生成一个 busybox 文件。
3. 安装 busybox
上一步只是进行了编译,我们还需要安装 busybox ,我们在终端执行:
1 | make install # 默认会安装到 busybox-1.22.1/_install 中 |
安装完成后会有如下提示信息(也可能没有,但不重要,只要不报错就可以了):
若未指定安装的目录,我们会发现在 busybox 源码顶层目录下会生成一个 _install 目录,我们看一下这个目录中都有什么:
1 | ls _install -ah |
我们会发现有如下文件:
这里边就是我们所需要的根文件系统,包含了根文件系统中所需的 shell 命令文件。
4. 添加 lib 库
4.1 拷贝 lib 库
我们还需要将交叉编译工具链中的库文件拷贝到 _install 目录下,我们先进入 _install 目录下,新建一个 lib 目录:
1 | cd _install/ |
lib 文件夹创建好了,库文件从哪里来呢? lib 库文件从交叉编译器中获取。交叉编译器里面有很多的库文件,这些库文件具体是做什么的作为初学者得我肯定不知道,既然不知道那就把所有的库文件都放到我们的根文件系统中,这就意味着这样做出来的根文件系统肯定很大,但是我们现在是学习阶段,还做不了裁剪,所以这就意味着我们的eMMC需要比较大才会够用啦。
- 交叉编译器的 arm-linux-gnueabihf/libc/lib 目录
我们进入交叉编译器目录拷贝相关文件:
1 | cd /home/hk/2software/gcc-linaro-4.9.4/arm-linux-gnueabihf/libc/lib # 我是放在这里的 |
拷贝完成后,根文件系统目录中的 lib 目录中的文件如下:
注意 cp 命令后面的“-d”选项表示拷贝符号链接,这里有个比较特殊的库文件: ld-linux-armhf.so.3,此库文件也是个符号链接,它会链接到库 ld-2.19-2014.08-1-git.so 上,输入以下命令查看此文件详细信息
1 | hk@ubuntu2204:~/5ALPHA/busybox-1.22.1/_install/lib$ ls ld-linux-armhf.so.3 -l |
ld-linux-armhf.so.3 后面有个“->”,表示其是个软连接文件,链接到文件 ld-2.19-2014.08-1-git.so,因为其是一个“快捷方式”,因此大小只有 24B。但是, ld-linuxarmhf.so.3 不能作为符号链接,否则的话在根文件系统中执行程序无法执行。所以我们需要将实际的文件拷贝过来,方法很简单,那就是重新复制 ld-linuxarmhf.so.3,只是不复制软链接即可命令如下:
1 | cd ~/5ALPHA/busybox-1.22.1/_install/lib |
重新拷贝后,结果如下:
- 交叉编译器的 arm-linux-gnueabihf/lib
1 | cd /home/hk/2software/gcc-linaro-4.9.4/arm-linux-gnueabihf/lib |
最终我们得到的 lib 中的文件如下:
4.2 向 /usr/lib 添加库
在 _install 的 usr 目录下创建一个名为 lib 的目录,将如下目录中的库文件拷贝到 busybox-1.22.1/_install/usr/lib 目录下:
1 | cd /home/hk/2software/gcc-linaro-4.9.4/arm-linux-gnueabihf/libc/usr/lib |
完成后的 busybox-1.22.1/_install/usr/lib 目录如下:
4.3 查看库大小
目前我们的库已经添加完毕了,可以使用“du”命令来查看一下 busybox-1.22.1/_install/lib 和 busybox-1.22.1/_install/usr/lib 这两个目录的大小,命令如下:
1 | hk@vm:~/5ALPHA/busybox-1.22.1/_install$ du ./lib ./usr/lib -sh |
可以看出lib和usr/lib这两个文件的大小分别为57MB和67MB,加起来就是57+67=124MB。
5. 创建其他目录
在根文件系统中还需要创建其他文件夹,如 dev 、 proc 、 mnt 、 sys 、 tmp 和 root 等:
1 | cd _install/ |
创建完成后,我们的根文件系统目录如下图所示:
我们刚才创建的几个都是空目录,其实可以发现我们还是少了一个 etc 目录,我们这里先不创建,一会先进行测试看看没有这个目录会有什么事情发生。
6. 根文件系统初步测试
6.1 拷贝根文件系统
我们这里将开发板设置为从 nfs 挂载根文件系统,我们需要先复制根文件系统到 nfs 目录下:
1 | cd ~/5ALPHA/busybox-1.22.1/_install/ |
6.2 设置 bootargs
1 | => setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.10.101:/home/hk/4nfs/imx6ull_rootfs,proto=tcp rw ip=192.168.10.102:192.168.10.101:192.168.10.1:255.255.255.0::eth0:off init=/linuxrc' |
6.3 重启开发板测试
注意我们使用的 uboot 参数与是从 tftp 加载 linux 内核和设备树(前边已经学习过了,有相关的笔记),从 nfs 挂载根文件系统。
这里需要注意,由于我是先搞的根文件系统,所以用的linux镜像和设备树都是正点原子出厂的,这样似乎会有驱动加载过程,但是我们又没有驱动文件,有些地方会卡很久,需要等一下,要是用我们自己移植的linux内核和设备树的话这个过程应该就很快了,另外就是有时候可能挂在不成功,这个时候就要看一下网络是否正常。从图中我们会发现,根文件系统正常挂载了,并且 ls 命令也可正常运行,但是有一个错误提示:
1 | can't run '/etc/init.d/rcS': No such file or directory |
这个错误提示其实很简单,说是无法运行“ /etc/init.d/rcS 这个文件,因为这个文件不存在。
7. 完善根文件系统
首先我们需要在nfs共享目录的根文件系统中创建一个 /etc 目录。
7.1 创建 /etc/init.d/rcS 文件
rcS 是个 shell 脚本, Linux 内核启动以后需要启动一些服务,而 rcS 就是规定启动哪些文件的脚本文件。该文件应改位于 etc/init.d/rcS 文件,我们创建:这个文件并添加以下内容:
1 | !/bin/sh |
- 第 1 行:表示这是一个 shell 脚本。
- 第 3 行:PATH 环境变量保存着可执行文件可能存在的目录,这样我们在执行一些命令或者可执行文件的时候就不会提示找不到文件这样的错误。
- 第 4 行:LD_LIBRARY_PATH 环境变量保存着库文件所在的目录 。
- 第 5 行:使用 export 来导出上面这些环境变量,相当于声明一些“全局变量”。
- 第 7 行:使用 mount 命令来挂载所有的文件系统,这些文件系统由文件 etc/fstab 来指定。
- 第 8 和 9 行:创建目录/dev/pts,然后将 devpts 挂载到/dev/pts 目录中。
- 第 11 行和第 12 行:使用 mdev 来管理热插拔设备,通过这两行, Linux 内核就可以在 /dev 目录下自动创建设备节点。关于 mdev 的详细内容可以参考 busybox 中的 docs/mdev.txt 文档。
【说明】
(1)这只是我们自己做的根文件系统中的 rcS 文件,这个是比较简单的,只有这么几行,但是 Ubuntu 或者其他大型 Linux 操作系统中的 rcS 文件,会更加的复杂,一般来说复杂的 rcS 文件也是借助其他工具创建的,比如 buildroot 等。
(2) etc/init.d/rcS 这个文件是需要有可执行文件才可以运行的。
1 | chmod 777 rcS |
7.2 创建 /etc/fstab 文件
etc/fstab 文件在 Linux 开机以后自动配置哪些需要自动挂载的分区,该文件中内容的格式如下:
1 | <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 。
我们创建好后添加以下内容:
1 | <file system> <mount point> <type> <options> <dump> <pass> |
7.3 创建 /etc/inittab 文件
inittab 的详细内容可以参考 busybox 下的文件 examples/inittab 。 init 程序会读取 etc/inittab 这个文件, inittab 由若干条指令组成。每条指令的结构都是一样的,由以 : 分隔的 4个段组成,格式如下:
1 | <id>:<runlevels>:<action>:<process> |
【参数说明】
- id :每个指令的标识符,不能重复。但是对于 busybox 的 init 来说, id 有着特殊意义。对于 busybox 而言 id 用来指定启动进程的控制 tty ,一般我们将串口或者 LCD 屏幕设置为控制 tty 。
- runlevels :对 busybox 来说此项完全没用,所以可以空着。
- action :动作,用于指定 process 可能用到的动作。
点击查看busybox支持的动作
动作 | 描述 |
sysinit | 在系统初始化的时候 process 才会执行一次。 |
respawn | 当 process 终止以后马上启动一个新的。 |
askfirst | 和respawn类似,在运行 process之前在控制台上显示“Please press Enter to activate this console.”。只有用户按下 Enter 键以后才会执行 process。 |
wait | 告诉init,要等待相应的进程执行完以后才能继续执行。 |
once | 仅执行一次,而且不会等待 process 执行完成。 |
restart | 当init重启的时候才会执行 procee。 |
ctrlaltdel | 当按下ctrl+alt+del组合键才会执行 process。 |
shutdown | 关机的时候执行process。 |
- process :具体的动作,比如程序、脚本或命令等。
我们创建好文件后添加以下内容:
1 | etc/inittab |
- 第 2 行,系统启动以后运行 /etc/init.d/rcS 这个脚本文件。
- 第 3 行:将 console 作为控制台终端,也就是 ttymxc0。并且系统启动以后,在运行 process 之前在控制台上显示提示信息,只有用户按下 Enter 键以后才会执行 process 。
- 第 4 行:启的话运行/sbin/init。
- 第 5 行:按下 ctrl+alt+del 组合键的话就运行/sbin/reboot,所以ctrl+alt+del 组合键用于重启系统。
- 第 6 行:关机的时候执行/bin/umount,也就是卸载各个文件系统。
- 第 7 行:关机的时候执行/sbin/swapoff,也就是关闭交换分区。
7.4 根文件系统测试
我们直接在nfs共享目录中添加的相关文件,所以直接重启开发板就好啦。这一次就不会再有上边的哪个报错信息啦:
三、根文件系统的完善
1. busybox 中文字符支持
1.1 中文乱码?
我们是通过 nfs 挂载的根文件系统,我们可以在 ubuntu 的相关目录下创建一个中文命名的目录,例如:
1 | cd /home/hk/4nfs/imx6ull_rootfs |
然后我们会看到 ubuntu 下目录详情如下:
然而我们到 MobaXterm 中的串口终端,看一下根目录:
我们会发现并不支持中文而中中文目录显示为 ? 。老版本的 busybox 是支持中文的,但是不知道从哪个版本开始 busybox 中的 shell 命令对中文输入即显示做了限制,即使内核支持中文但在 shell 下也依然无法正确显示。这个时候就需要修改 busybox 源码了。我们先进入 busybox 源码顶层目录:
1 | ~/5ALPHA/busybox-1.22.1/_install |
1.2 printable_string.c 文件修改
首先我们需要修改 libbb/printable_string.c 文件,我们打开该文件后,会发现这里边只有一个 printable_string 函数,该函数中有这么几条语句:
通过这几个语句我们知道,当字符大于 0X7F 以后就跳出去了。如果支持 UNICODE 码的话,当字符大于 0X7F 就直接输出 ? 。我们将上边的两个 if 语句进行修改:
1 | /* 下边if出现在34行(将这个if注释掉) */ |
这两处修改主要是为了禁止字符大于 0X7F 以后 break 和输出 ? 。即:
1.3 unicode.c 文件修改
我们打开 libbb/unicode.c 文件,然后找到 unicode_conv_to_printable2 函数,在该函数中有这么几行:
1022 行表示当字符大于 0X7F 以后, *d++ 就为 ? , 1030 行和 1031 行表示,当字符大于 0X7F 以后, *d 也为 ? 。我们将其修改为以下内容:
1 | /* 下边的这个三目运算符语句出现在 1022 行*/ |
即:
1.4 busybox 配置
我们执行以下命令打开 busybox 的图形配置界面:
1 | make menuconfig |
然后我们需要使能 busybox 的 unicode 编码以支持中文,配置路径如下:
1 | Busybox Configuration |
然后退出,一定要记得保存。
1.5 重新制作根文件系统
按照前边的步骤重新制作根文件系统即可。
1.6 中文支持测试
还是之前的样子,在根文件系统新建一个卓面目录,再来看一看串口终端的情况:
2. 终端路径支持
上边的图是 busybox 生成的根文件系统默认的情况,可以做成下图这样吗?
2.1 PS1变量介绍
PS1命令是linux系统中的一个全局变量,用于定义用户命令行的字符显示。
我们看一下开发板中的 PS1 变量:
我们再看一下ubuntu中的 PS1 变量:
看到这里,是不是就有想法了呢?我们改变这个环境变量的值不就好了吗?或者直接把ubuntu中的复制过来就可以了嘛。
2.2 PS1变量说明
我们以ubuntu中的 PS1 的值为例,简单了解一下都是什么意思。
2.2.1 内置变量
1 | \d :# 代表日期,格式为weekday month date,例如:"Mon Aug 1" |
2.2.2 颜色设置
颜色代码格式:\[\e[F;Bm\]
(其中F表示字体颜色,B表示背景颜色,其实 e 也可以用 033):
字体代码 | 背景代码 | 颜色 |
---|---|---|
30 | 40 | 黑色 |
31 | 41 | 红色 |
32 | 42 | 绿色 |
33 | 43 | 黄色 |
34 | 44 | 蓝色 |
35 | 45 | 紫红色 |
36 | 46 | 青蓝色 |
37 | 47 | 白色 |
其实这里的语法格式跟 printf还有echo的颜色输出是一样的,都是使用的 ANSI 控制码。
2.3 PS1变量修改
我们在根文件系统的 /etc 目录中新建一个 profile 文件,输入以下内容:
1 | !/bin/sh |
然后重启系统,我们会看到以下情况:
【注意】 PS1 变量修改的时候注意在命令提示符后边需要多加一个空格。