一、前言
在上一篇教程中,我们成功编译了Busybox根文件系统并且能够正常使用,但是大家应该也发现了我们构建的根文件系统存在许多问题,比如一些找不到文件的报错。并且在实际的产品中一般都是将根文件系统烧录到EMMC中,并不是像我们这样从网络挂载,那么本次教程就来教大家如何完善我们使用Busybox构建的根文件系统并且将其打包烧录到EMMC中,如果你准备好了就让我们开始吧!
二、谁适合本次教程
因为本次教程是对我们前面基于Busybox编译好的根文件系统的一个完善,所以,在阅读本次教程之前还请参考前面编译的教程,毕竟编译好了才能进行下面的操作嘛。本次教程使用的是正点原子的STM32MP157开发板,如果你的开发板和我不同也可以将本次教程作为参考。
三、资料的准备
本次教程中所使用的资料同样来自于正点原子提供的STM32MP157资料,下载的方式在交叉编译环境搭建时就已经讲过了,如果你还不知道如何下载资料,请看下面的教程:
STM32MP157编译环境搭建:[Linux]从零开始的STM32MP157交叉编译环境配置-CSDN博客
资料下载完成以后如下图所示:
本次教程会使用到“STM32CubeProgrammer”来为STM32MP157烧录系统,如果你之前没有安装请先安装好,如果你不知道如何使用可以考虑查看正点原子的官方文档教程。
四、Busybox根文件系统完善
之前我们也只是将根文件系统构建好了,但是在我们的根文件系统在启动时还有一行报错,如图所示:
现在我们就来分析并且解决这个报错,首先我们来看报错信息,这里为了方便我就直接将报错复制过来了:
can't run '/etc/init.d/rcS': No such file or directory
这里的报错大概意思就是在运行时没有找到位于“/etc/init.d”下的名为“rcS”的文件。
这里我们可以直接使用下面的命令在“/etc/init.d”中新建一个名为“rcS”的文件,因为使用了nfs进行文件挂载,所以不管我们是在开发板的终端中新建文件还是在Ubuntu的终端中新建文件其实都是一样的,最终文件都是同步的,但是为了方便我们还是在Ubuntu中进行,这里我们首先进入Ubuntu中用户目录下的linux目录再进入“nfs/rootfs”目录,如下图所示:
这里我们在rootfs目录下使用下面的命令来创建“init.d”文件:
mkdir -p ./etc/init.d/
然后使用下面的命令来创建“rcS”文件:
touch ./etc/init.d/rcS
创建完成以后使用下面的命令打开“rcS”文件:
nano ./etc/init.d/rcS
打开以后如图所示:
然后我们将下面的内容复制到“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
完成以后如图所示:
这里为了解决windows和Linux换行符不同的问题,我们使用下面的命令安装一下换行符的转换工具:
sudo apt install dos2unix
安装完成以后,我们直接使用下面的命令调用这个工具转换换行符:
dos2unix ./etc/init.d/rcS
完成以后如图所示:
然后我们使用下面的命令给予“rcS”文件访问权限避免文件无法读取的问题:
chmod 777 ./etc/init.d/rcS
当我们完成对“rcS”文件的配置后,我们就可以重启一下我们的开发板,重启以后,重新进入根文件系统又会发现被提示找不到“/etc”下的“fstab”文件:
这里我们直接使用下面的命令在“etc”目录下新建“fstab”文件:
touch ./etc/fstab
新建好以后,在“fstab”中写入下面的内容:
#<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
完成以后如图所示:
写入完成以后,我们这里同样使用换行符转换工具处理一下“fstab”文件:
dos2unix ./etc/fstab
完成以后,我们再次重启开发板,可以看到又提示不能创建文件:
这是Linux内核配置问题,后面会讲怎么修改。因为要动内核,可能比较麻烦,我们这里就先不进行操作了。这里我们还需要在“etc”目录下新建一个名为“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
这里我们同样使用换行符工具来转换一下换行符:
dos2unix ./etc/inittab
现在我们来解决一下上面提到的由于内核配置引发的问题,这里我们直接来到内核的源码目录中,如图所示:
在linux的源码目录下使用下面的命令打开图形化配置界面:
make menuconfig
图形化配置界面启动以后如图所示:
这里我们首先找到“Device Drivers ---> ”:
进入后找到“Generic Driver Options ---> ”:
进入后我们将“Support for uevent helper ”使能:
完成上面的步骤后我们将配置文件保存到原本的默认配置文件存放的地方:
./arch/arm/configs/stm32mp1_atk_defconfig
完成以后,我们使用我们之前定义的构建脚本重新编译内核:
./build
编译完成以后如图所示:
编译完成以后,我们将位于Linux源码目录下的“arch/arm/boot”目录下的镜像拷贝到tftp目录,毕竟我们的Linux内核是使用tftp来传输的。
完成以后,我们再次重启开发板,这次开发板会拉取我们处理过的LInux内核,可以看到现在已经不报错了:
说明现在我们的根文件系统已经可以正常工作并且没有错误了。至此根文件系统就完善好了。
五、Busybox根文件系统功能测试
在上面,我们已经将我们的根文件系统完善好了,现在我们就来对它的功能进行一些测试吧。
1.软件运行测试
为了确保我们根文件系统的各种链接库没有问题,我们可以自己写一个简单的程序来测试一下,这里我们首先在用户的目录下的linux目录中新建一个名为TEST的文件夹,如图所示:
我们在这个TEST文件夹中新建一个名为“hello.c”的文件,并且写入下面的代码:
#include <stdio.h>
int main(void)
{
while(1)
{
printf("hello world!\r\n");
sleep(2);
}
return 0;
}
这里的程序非常简单就不过多解释了。
写入完成以后如图所示:
然后我们使用下面的命令来编译我们写好的.c文件:
arm-none-linux-gnueabihf-gcc hello.c -o hello
这里编译可能出现警告,我们不用管:
这里我们可以使用file命令来查看一下我们这个文件的架构:
file hello
可以看到我们这里的文件架构是ARM的,毕竟我们是使用交叉编译器来编译的嘛:
然后我们在rootfs目录下新建一个名为“drivers”的目录,然后将我们编译出来的可执行文件复制到其中,后面如果要学习Linux驱动开发的话,也会将写好的驱动放在“drivers”目录中:
cp ./hello /home/chulingxiao/linux/nfs/rootfs/drivers/
然后使用下面的命令开放这个可执行文件的权限:
chmod 777 hello
然后我们在开发板一侧切换到dev目录,运行这个可执行文件,效果如图:
这里可以看到我们的程序正常运行,没有提示依赖库找不到之类的说明我们构建的根文件系统的依赖库是完整的。
2.中文字符测试
我们在编译Busybox之前已经对Busybox的源码进行了修改,保证了Busybox支持中文,现在我们来测试一下。这里我们直接在Ubuntu一侧直接使用下面的命令在rootfs目录中新建一个中文文件,使用下面的命令:
touch 中文测试.txt
完成后如图所示:
我们可以在这个文本文件中写入一些中文内容:
然后我们来到开发板一侧,看看文件会不会正常显示,这里可以看到,在开发板的终端中这个中文测试文件是正常显示的:
然后我们再使用“cat”命令将文件中的内容打印出来:
可以看到,在我们基于Busybox构建的根文件系统中,中文显示是正常的,说明我们的中文功能没有问题。
3.ping命令测试
因为我这里的联网方式并没有采用像正点原子教程中的那样使用路由器,所以肯定就无法ping通外网,我们这里就只ping我们的服务器即可,ping命令很简单,就不多说了:
可以看到,我们是可以正常Ping到我们的服务器的,说明我们的ping命令和网络都没有问题。
至此,对于Busybox根文件系统的基本测试就完成了。
六、Linux内核及根文件系统打包
在我们的实际产品或者项目中,我们的Linux内核和根文件系统是不可能通过网络拉取的,所以非常必要的就是我们需要将Linux内核与根文件系统像Uboot一样烧录到EMMC中,然后由Uboot来拉起。所以现在就来教大家如何将我们的Linux内核与根文件系统打包然后烧录到EMMC中。
1.Linux内核打包与烧录测试
这里我们先来打包Linux内核,之前我们构建完成的Linux内核其实就只有镜像和设备树文件两部分,这两部分被我们放在了tftp目录下以便Uboot可以直接拉取,我们可以直接找到:
这里的LInux镜像我们在上面已经替换成了我们新编译的镜像了,所以肯定是不会有问题的。
现在我们来进行打包,首先我们在用户目录下的linux目录下新建一个名为“bootfs”的目录:
mkdir bootfs
然后使用下面的命令进入这个名为“bootfs”的目录并且创建ext4磁盘:
cd bootfs
dd if=/dev/zero of=bootfs.ext4 bs=1M count=10
mkfs.ext4 -L bootfs bootfs.ext4
完成上面的步骤后,我们可以看到在“bootfs”目录中已经有以“ext4”结尾的文件了:
然后我们我们使用下面的命令新建一个文件并且挂载我们创建出来的ext4磁盘:
sudo mkdir /mnt/bootfs
sudo mount bootfs.ext4 /mnt/bootfs/
完成以后,我们将Linux系统镜像和设备树文件拷贝到这个挂载点中:
sudo cp /home/chulingxiao/linux/tftp/* /mnt/bootfs/
文件拷贝完成以后,我们使用下面的命令将这个ext4磁盘卸载:
sudo umount /mnt/bootfs
卸载完成以后,我们的Linux内核的打包就完成了,我们的Linux镜像和设备树文件都放在了这个ext4磁盘文件中,我们现在就可以将这个名为“bootfs.ext4”的文件烧录到开发板的EMMC中。
这里我们可以将原本烧录Uboot时使用的烧写脚本文件复制出来,相关的uboot与tf-a我们都沿用之前的。如图:
这里我们将文件夹的名字修改为“stm32mp157_bootfs”,将烧写脚本文件名修改为“bootfs.tsv”,如图所示:
这里我们直接将我们制作好的Linux内核打包文件复制过来,如图所示:
这里我们还需要修改一下tsv文件,因为这个文件之前是用作烧录Uboot,我们需要在它的最下面加上这样一行内容它才能烧录我们准备的.ext4文件:
P 0X21 boot System mmc1 0x00280000 bootfs.ext4
完成后如图所示:
#Opt Id Name Type Device Offset Binary
- 0x01 fsbl1-boot Binary none 0x0 tf-a-stm32mp157d-atk-serialboot.stm32
- 0x03 ssbl-boot Binary none 0x0 u-boot.stm32
P 0x04 fsbl1 Binary mmc1 boot1 tf-a-stm32mp157d-atk-trusted.stm32
P 0x05 fsbl2 Binary mmc1 boot2 tf-a-stm32mp157d-atk-trusted.stm32
P 0x06 ssbl Binary mmc1 0x00080000 u-boot.stm32
P 0X21 boot System mmc1 0x00280000 bootfs.ext4
这里我们使用"STM32CubeProgrammer"打开烧录脚本:
然后我们将开发板的拨码都拨到0,按下复位后刷新USB设备:
这些都是非常基础的操作,现在就不细讲了。
检测到USB后,我们链接后点击“Download”即可,随后就开始了下载:
出现下面这样的提示下载就已经完成了:
下载完成以后我们将开发板的拨码拨到原本的位置也就是用EMMC启动,我们再次复位开发板,然后进入Uboot中,我们这里还需要Uboot中配置环境变量来拉起Linux内核,这里我们直接使用下面的命令来配置相关的环境变量:
setenv bootcmd 'ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157d-atk.dtb;bootm c2000000 - c4000000'
然后使用下面的命令保存环境变量:
saveenv
保存完成以后,我们输入下面的命令来启动我们的Linux内核:
boot
输入了“boot”命令以后,可以看到我们的Uboot并没有再去网络拉取Linux内核了,而是从EMMC直接启动:
这也说明了我们的Linux内核打包是成功的。
至于最下面的报错也是因为内核没有找到根文件系统:
不过不影响,我们的根文件系统本来就没烧录进去嘛。
至此Linux内核的打包与烧录就完成了。
2.根文件系统的打包与烧录
烧录完Linux内核以后,现在我们可以来烧录根文件系统,首先还是对根文件系统的打包,这里我们同样的首先在用户目录下的linux目录下新建一个名为“rootfs”的文件夹:
然后使用下面的命令创建ext4磁盘:
cd rootfs
dd if=/dev/zero of=rootfs.ext4 bs=1M count=1024
mkfs.ext4 -L rootfs rootfs.ext4
完成以后如图所示:
这里我们同样新建一个文件夹来挂载我们新建的ext4磁盘:
sudo mkdir /mnt/rootfs
sudo mount rootfs.ext4 /mnt/rootfs/
完成后我们将我们原本在nfs目录下的rootfs目录下的所以文件都复制到我们挂载的目录中:
sudo cp /home/chulingxiao/linux/nfs/rootfs/* /mnt/rootfs/ -drf
拷贝完成以后,我们使用下面的命令卸载磁盘:
sudo umount /mnt/rootfs
这样我们的ext4磁盘制作就已经完成了:
现在我们来将这个ext4磁盘文件烧录到我们开发板的EMMC中,这里我们直接借用烧录Linux内核的脚本文件,我们将外层文件夹名字改为“stm32mp157_rootfs”,烧录脚本文件名字改为“rootfs.tsv”如下图所示:
这里我们同样需要将制作好的ext4文件复制过来,如图所示:
这里我们还需要在烧录脚本的最后一行加上下面的内容:
P 0X22 rootfs FileSystem mmc1 0x04280000 rootfs.ext4
完成后如图所示:
#Opt Id Name Type Device Offset Binary
- 0x01 fsbl1-boot Binary none 0x0 tf-a-stm32mp157d-atk-serialboot.stm32
- 0x03 ssbl-boot Binary none 0x0 u-boot.stm32
P 0x04 fsbl1 Binary mmc1 boot1 tf-a-stm32mp157d-atk-trusted.stm32
P 0x05 fsbl2 Binary mmc1 boot2 tf-a-stm32mp157d-atk-trusted.stm32
P 0x06 ssbl Binary mmc1 0x00080000 u-boot.stm32
P 0X21 boot System mmc1 0x00280000 bootfs.ext4
P 0X22 rootfs FileSystem mmc1 0x04280000 rootfs.ext4
这里我们同样使用“STM32CubeProgrammer”来进行烧录,烧录方式和上面烧录Linux内核是一样的,这里就不多说了,都是重复很多次的操作了。烧录完成以后我们再次进入Uboot中,使用下面的命令配置相关环境变量:
setenv bootargs 'console=ttySTM0,115200 root=/dev/mmcblk2p3 rootwait rw'
配置完成以后使用下面的命令保存环境变量:
saveenv
最后我们再次使用boot命令启动Linux内核,可以发现我们的内核和根文件系统都启动起来了:
至此,我们根文件系统的打包以及烧录就已经完成了。
七、结语
在本次教程中,教了大家如何完善自己的根文件系统以及对根文件系统功能的测试,最后我们还将我们构建好的Linux内核以及根文件系统打包并且烧录到EMMC中,本次教程涉及到的内容比较多,但是在之前的教程中都讲过,大家如果遇到了问题可以去翻阅之前的一些教程,那么最后,感谢大家的观看!