8 Buildroot 根文件系统构建

发布时间 2023-08-08 21:33:36作者: 烟儿公主

一、根文件系统简介

  根文件系统一般也叫做 rootfs,这个是属于 Linux 内核的一部分。

  根文件系统首先是一种文件系统,该文件系统不仅具有普通文件系统的存储数据文件的功能,但是相对于普通的文件系统,它的特殊之处在于,它是内核启动时所挂载(mount)的第一个文件系统,内核代码的映像文件保存在根文件系统中,系统引导启动程序会在根文件系统挂载之后从中把一些初始化脚本(如rcS,inittab)和服务加载到内存中去运行。我们要明白文件系统和内核是完全独立的两个部分。在嵌入式中移植的内核下载到开发板上,是没有办法真正的启动Linux操作系统的,会出现无法加载文件系统的错误。

 

二、buildroot

  Buildroot 是 Linux 平台上一个开源的嵌入式 Linux 系统自动构建框架,用交叉编译生成 Linux 系统。整个Buildroot 是由 Makefile 脚本和 Kconfig 配置文件构成的。

 

1. buildroot 源码下载

  官方网站:Buildroot - 让嵌入式 Linux 变得简单

  由于我的 Ubuntu 版本较低,所以用正点推荐的 2020.02.6 版本。

 

2. buildroot 构建根文件系统

① 配置 buildboot

  先把 buildroot-2020.02.6.tar.bz2 拷贝到 Ubuntu 的 linux/tool 路径下。解压命令如下:

tar -vxjf buildroot-2020.02.6.tar.bz2

  buildroot 也支持图形化配置:

make menuconfig

1、配置 Target options

  需要配置的项目和其对应的内容如下(=”号后面是配置项要选择的内容! ) 

-> Target Architecture = ARM (little endian)
-> Target Binary Format = ELF
-> Target Architecture Variant = cortex-A7
-> Target ABI = EABIhf
-> Floating point strategy = NEON/VFPv4
-> ARM instruction set = ARM

# 目标架构:ARM
# 目标二进制格式:ELF
# 目标架构变体:cortex-A7
# 目标ABI:EABIhf
# 浮点策略:NEON/VFPv4
# ARM指令集:ARM

 

 2、配置 Toolchain

  此配置项用于配置交叉编译工具链,也就是交叉编译器,这里设置为我们自己所使用的交叉编译器即可。 需要配置的项目和其对应的内容如下: 

Toolchain
    -> Toolchain type = External toolchain
    -> Toolchain = Custom toolchain // 用户自己的交叉编译器
    -> Toolchain origin = Pre-installed toolchain // 预装的编译器
    -> Toolchain path = /usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linuxgnueabihf
    -> Toolchain prefix = $(ARCH) -none-linux-gnueabihf 
    -> External toolchain gcc version = 9.x
    -> External toolchain kernel headers series = 4.20.x // 交叉编译器的 linux 版本号
    -> External toolchain C library = glibc/eglibc
    -> [*] Toolchain has SSP support? (NEW)
    -> [*] Toolchain has RPC support? (NEW) 
    > [*] Toolchain has C++ support? 
    -> [*] Enable MMU support (NEW)
    
# 工具链类型:外部工具链
# 工具链:自定义工具链
# 工具链来源:预安装的工具链
# 工具链路径:/usr/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linuxgnueabihf
# 工具链前缀:$(ARCH)-none-linux-gnueabihf
# 外部工具链gcc版本:9.x
# 外部工具链内核头文件系列:4.20.x
# 外部工具链C库:glibc/eglibc
# 工具链是否支持SSP(堆栈保护):选中
# 工具链是否支持RPC:选中
# 工具链是否支持C++:选中
# 是否启用MMU支持:选中

3、配置 System configuration 

  此选项用于设置一些系统配置,比如开发板名字、欢迎语、用户名、密码等。需要配置的项目和其对应的内容如下: 

System configuration
    -> System hostname = ATK-stm32mp1 // 平台名字
    -> System banner = Welcome Lxs // 欢迎语
    -> Init system = BusyBox // 使用 busybox
    -> /dev management = Dynamic using devtmpfs + mdev // 使用 mdev
    -> [*] Enable root login with password (NEW) // 使能登录密码
        -> Root password = 147258 // 登录密码

# 可以设置密码,但是每次重启都要输入密码很麻烦,所以我这里是失能的。

 4、配置 Filesystem images

  此选项配置我们最终制作的根文件系统为什么格式的,配置如下: 

-> Filesystem images
    -> [*] ext2/3/4 root filesystem // 如果是 EMMC 或 SD 卡的话就用 ext3/ext4
        -> ext2/3/4 variant = ext4 // 选择 ext4 格式
        -> exact size =1G // ext4 格式根文件系统 1GB(根据实际情况修改)
    -> [*] ubi image containing an ubifs root filesystem // 如果使用 NAND 的话就用 ubifs

  buildroot 可以直接制作出 ext4 格式的根文件系统,但是一般我们会自行往根文件系统里面添加很多其他的文件,所以产品开发完成以后需要自行打包根文件系统,然后烧写到开发板里面。不管是针对 EMMC ext4 格式的根文件系统还是针对 NAND ubi 格式的根文件系统,都要设置相应的大小,比如这里我们设置的 ex4 格式根文件系统大小为 1GB 

5、禁止编译 Linux 内核和 uboot

  buildroot 不仅仅能构建根文件系统,也可以编译 linux 内核和 uboot我们一般都不会使用 buildroot 下载的 linux 内核和 uboot,因为 buildroot 下载的 linux uboot官方源码,里面会缺少很多驱动文件,而且最新的 linux 内核和 uboot 会对编译器版本号有要求,可能导致编译失败。因此我们需要配置 buildroot,关闭 linux 内核和 uboot 的编译,只使用buildroot 来构建根文件系统,首先是禁止 Linux 内核的编译,配置如下:

-> Kernel
    -> [ ] Linux Kernel // 不要选择编译 Linux Kernel 选项!
    
    
-> Bootloaders
    -> [ ] U-Boot // 不要选择编译 U-Boot 选项!

6、编译 Target packages 

  此选项用于配置要选择的第三方库或软件、比如 alsa-utilsffmpegiperf 等工具,这里我们先只选择内核的模块加载相关软件,配置如下: 

-> Target packages
    -> System tools
        -> [*] kmod     // 使能内核模块相关命令

  先编译最基础的根文件系统,一步一步来。

7、保存配置项

  和 ubootkernel 一样,通过图形化界面配置好 buildroot 以后最好保存一下配置项,防止清除工程以后将配置项给删除掉。 buildroot 的默认配置项都保存在 configs 目录下,配置完成以后选择<Save>,然后输入要设置的配置项名字:

  如果以后重新配置 builroot 可以直接输入:

make stm32mp1_atk_defconfig

  保存完成后就可以看到  stm32mp1_atk_defconfig 这个文件。

 

② 编译 buildboot

  输入以下命令:

make -j8 //多线程编译

# "-j"参数可以指定同时执行的并发任务数。"-j8"表示同时进行8个并发编译任务。

  编译的时间是真的久,我直接FQ然后再编译快一点。

 

  将 rootfs.tar 拷贝到在 nfs 目录下的 rootfs文件夹中并解压,命令如下: 

cp rootfs.tar /home/alientek/linux/nfs/rootfs/ -f
cd ~
cd linux/nfs/rootfs/
tar -vxf rootfs.tar
rm rootfs.tar

  这就是使用 buildroot 编译出来的根文件系统,我们可以通过 nfs 挂载到开发板上,然后对其进行测试。

 

③ buildboot 测试根文件系统 

 测试方法是通过 nfs 挂载的方式,启动 uboot,修改 bootargs 环境变量,设置 nfsroot 目录为 Ubuntu 中的rootfs 目录,命令如下: 

setenv bootargs console=ttySTM0,115200 root=/dev/nfs nfsroot=192.168.1.105:/home/alientek/linux/nfs/rootfs,proto=tcp rw ip=192.168.1.106:192.168.1.105:192.168.1.1:255.255.255.0::eth0:off''

  这里有密码登录,需要取消,输入以下命令:

cd ~
vim linux/nfs/rootfs/etc/inittab

  修改成功后开发板 RESET 重复以下操作:

setenv bootargs console=ttySTM0,115200 root=/dev/nfs nfsroot=192.168.1.105:/home/alientek/linux/nfs/rootfs,proto=tcp rw ip=192.168.1.106:192.168.1.105:192.168.1.1:255.255.255.0::eth0:off''
boot

 

3. buildroot 下的 busybox 配置

① busybox 配置 

  buildroot 会自动下载 busybox 压缩包, buildroot 下载的源码压缩包都存放在/dl 目录下,在 dl 目录下就有一个叫做“busybox”的文件夹,此目录下保存着 busybox 压缩包,如下图所示:

  但是 buildroot 会将所有解压后的软件保存在 /output/build 目录中。

  进入 busybox 图形化配置界面:

cd ..            # 返回buildroot源码根目录下
make busybox-menuconfig

-> Settings
    -> Build static binary (no shared libs)

# 不要选择这个

  继续配置:

-> Settings
    -> vi-style line editing commands
# 选择这个

  继续配置:

-> Linux Module Utilities
    -> Simplified modutils
# 不要选择这个

 

  继续配置:

-> Linux System Utilities
    -> mdev (17 kb)     # 确保下面的全部选中,默认都是选中的

 

  使能 busybox unicode 编码以支持中文:

-> Settings
    -> Support Unicode      # 选中
        -> Check $LC_ALL, $LC_CTYPE and $LANG environment variables     # 选中

 

 配置完成后还需要让busybox 支持中文:

cd /linux/tools/buildroot-2020.02.6/output/build/busybox-1.31.1
vim libbb/printable_string.c

  修改 31~32 行和 45行:

  修改  busybox-1.32.0/libbb/unicode.c  内容,修改 1022 行和 1031 行:

 

② 编译 busybox 

cd /linux/tools/buildroot-2020.02.6    # 进入 buildroot 源码目录下
make show-targets    # 当前 buildroot 所有 packages

  如果我们想单独编译并安装 busybox 的话执行下面命令即可:

make busybox    # 编译 busybox

  编译完成以后重新编译 buildroot,主要是对其进行打包,输入如下命令: 

make -j8

  编译完成后查看 output/images 目录下的 rootfs.tar 创建时间是否是刚刚编译的,如果不是就删掉 rootfs.tar,然后重新执行“sudo make”重新编译一下即可。 

 

4. buildroot 第三方软件和库的配置 

① 使能 VSFTPD 服务

   在开发板上搭建 FTP 服务器,这样可以使用 FileZilla 软件直接向开发板拷文件或把开发板文件拷贝到电脑中。

cd /linux/tools/buildroot-2020.02.6
make menuconfig
-> Target packages
    -> Networking applications
        -> [*] vsftpd

 

② 使能 SSH

  有时候需要远程登录开发板,这个时候就可以通过网络登录,要用到 SSH 服务。

  保存在 configs/stm32mp1_atk_defconfig。

  之后重新编译 buildroot ,用 make -j8 。将 ouput/images/rootfs.tar 拷贝到 nfs目录下的 rootfs 目录中,然后重新解压。 命令如下:

tar -vxf rootfs.tar

  解压完成以后就可以使用 FTP SSH 等相关的软件了,由于 FTP SSH 都是通过网络进行数据传输的,因此需要先配置网络,如果是通过 nfs 挂载的根文件系统,那么网络已经初始化完成了,因此可以直接使用。如果是烧写到 EMMC 里面的,那么就需要先配置网络相关功能。 

 

4. buildroot 根文件系统测试

① 软件运行测试

  编译的应用软件一般都使用动态库,使用动态库的话应用软件体积就很小,但是得提供库文件,库文件我们已经添加到了根文件系统中。我们编写一个小小的测试软件来测试一下库文件是否工作正常,在根文件系统下创建一个名为“drivers”的文件夹,以后我们学习 Linux 驱动的时候就把所有的实验文件放到这个文件夹里面。 

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    while(1)
    {
        printf("hello world!\r\n");
        sleep(2);
    }
    return 0;
}

  sleep 相当于 Linux 的延时函数,单位为秒,所以 sleep(2)就是延时 2 秒。因为我们要在 ARM 芯片上运行,需要用交叉编译器编译:

arm-none-linux-gnueabihf-gcc hello.c -o hello

  可以使用 file 命令查看文件类型和编码格式:

file hello

  hello 是个 32 位的 LSB 可执行文件, ARM 架构的,并且是动态链接的。将拷贝到 rootfs/dervers 目录下。

  终止 hello 运行按下 CTRL+C,./hello &”就是让 hello 在后台运行。在后台运行的软件可以使用“kill -9 pid(进程 ID)”命令来关闭掉,首先使用“ps”命令查看要关闭的软件 PID 是多少, ps 命令用于查看所有当前正在运行的进程,并且会给出进程的 PID

  首先先让 hello 在后台运行,然后输入 ps 查看 PID 是多少,然后再用 kill -9 命令关闭。

./hello &
ps
kill -9 205

  因为 hello 在不断的输出“hello world”所以我们的输入看起来会被打断,其实是没有的,因为我们是输入,而 hello 是输出。在数据流上是没有打断的,只是显示在 SecureCRT 上就好像被打断了。

 

② 中文字符测试

  在 ubuntu 中向在 rootfs 目录新建一个名为“中文测试”的文件夹,然后在 MobaXterm 下查看中文名能不能显示正确。输入“ls”命令:

setenv bootargs console=ttySTM0,115200 root=/dev/nfs nfsroot=192.168.1.105:/home/alientek/linux/nfs/rootfs,proto=tcp rw ip=192.168.1.106:192.168.1.105:192.168.1.1:255.255.255.0::eth0:off''

 

 

③  开机自启动测试

  进入根文件系统的时候会运行/etc/init.d/rcS 这个 shell 脚本,因此我们可以在这个脚本里面添加自启动相关内容。添加完成以后的/etc/init.d/rcS 文件内容如下: 

#!/bin/sh

PATH=/sbin:/bin:/usr/sbin:/usr/bin
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/lib:/usr/lib
runlevel=S
umask 022
export PATH LD_LIBRARY_PATH runlevel

mount -a
mkdir /dev/pts
mount -t devpts devpts /dev/pts

echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

# 开机自启动
cd /drivers        # 进入 drivers 目录,因为要启动的软件存放在 drivers 目录下
./hello &          # 以后台方式执行 hello 这个软件
cd /               # 退出 drivers 目录,进入到根目录下

 

 

外网连接测试

 测试一下看开发板能不能上网,rootfs 中新建文件/etc/resolv.conf,然后在里面输入如下内容: 

nameserver 114.114.114.114
nameserver 192.168.1.1

  nameserver 表示这是个域名服务器,设置了两个域名服务器地址:114.114.114.114 192.168.1.1。之后重启开发板。

 

 

⑤ depmod 测试

  Linux 驱动的时候需要使用此命令分析模块的依赖性,此命令需要在 busybox 中使能,路径如下: 

-> Linux Module Utilities
    -> [*] depmod

  然后再重新编译,一般将驱动模块放到 lib/modules/5.4.31 目录下,既然当前根文件系统不存在这个目录,那么就手动创建此目录,命令如下: 

mkdir /lib/modules/5.4.31 -p

 

  系统会自动使用这三个文件。

 

⑥ vsftpd 测试

  首先需要对vsftpd 进行配置,打开/etc/vsftpd.conf 文件,将下面两行前面的“#”去掉: 

  接下来把文件的所属用户改为 root,输入以下命令:

chown root:root /etc/vsftpd.conf

  我们新建一个用户完成 FTP 登录。

  打开  FileZilla,设置新站点。

  连接就可以完成与开发版的 vsftpd 连接。

 

⑦ sshd 测试

 SSHD 不需要进行配置,只需要创建一个登陆用户即可。我们需要修改/var/empty目录所属用户以及用户组,输入如下命令: 

chown root:root /var/empty/

  同样的,也可以在 ubuntu 下通过 ssh 命令登录开发板,输入如下命令: 

ssh luoxuesong@192.168.1.106

# 其中 luoxuesong 是登录账号名字,192.168.1.106 是我们自己给开发板设的 IP 地址。用户名和 IP 地址用 @ 连接。
# 输入 exit 退出 SSH 会话

 

⑧ 创建自启动文件

  默认情况下 buildroot 构建的根文件系统中 rcS 文件内容如下:

  rcS 默认会在/etc/init.d 目录下查找所有以‘S’开头的脚本,然后依次执行这些脚本。所以我们可以自己创建一个以‘S’开头的自启动脚本文件,比如我创建一个名为 Sautorun 的自启动文件,命令如下: 

cd /etc/init.d/ 
touch Sautorun 
chmod 777 Sautorun 

  设置好以后重启开发板,此时 Sautorun 脚本就会被 rcS 调用,进而运行 test 软件。 

 

⑨ 显示路径

  使用 buildroot 构建的根文件系统启动以后会发现,输入命令的时候命令行前面一直都是“#”,如果我们进入到某个目录的话前面并不会显示当前目录路径。

这个是跟 PS1 环境变量有关,它是用于设置命令提示符格式,格式如下: 

PS1 = ‘命令列表’

命令列表中可选的参数如下:
\!:显示该命令的历史记录编号。
\#:显示当前命令的命令编号。
\$:显示$符作为提示符,如果用户是 root 的话,则显示#号。
\\:显示反斜杠。
\d:显示当前日期。
\h:显示主机名。
\n:打印新行。
\nnn:显示 nnn 的八进制值。
\s:显示当前运行的 shell 的名字。
\t:显示当前时间。
\u:显示当前用户的用户名。
\W:显示当前工作目录的名字。
\w:显示当前工作目录的路径

  我们打开 /etc/profile 文件,文件内容如下所示: 

export PATH="/bin:/sbin:/usr/bin:/usr/sbin"

if [ "$PS1" ]; then
	if [ "`id -u`" -eq 0 ]; then
		export PS1='# '
	else
		export PS1='$ '
	fi
fi

export EDITOR='/bin/vi'

# Source configuration files from /etc/profile.d
for i in /etc/profile.d/*.sh ; do
	if [ -r "$i" ]; then
		. $i
	fi
done
unset i

# 3~9 行是设置 P1 环境变量,但是不建议修改,因为当我们重新编译了 buildroot 并解压后这个文件内容就会被替换。
# 14~17 行是 etc/profile 文件会遍历 etc/profile.d 目录下的 .sh 脚本文件,然后执行。所以我们可以自定义 .sh 脚本文件。

  在/etc/profile.d 目录下新建一个名为“myprofile.sh”的 shell 脚本文件,并且给予此文件可执行权限,命令如下: 

cd /etc/profile.d/ 
touch myprofile.sh 
chmod 777 myprofile.sh 

  最后在 myprofile.sh 里面添加如下代码:

#!/bin/sh

PS1='[\u@\h]:\w$ '
export PS1

  之后重启开发板,之后我们就可以看见当前路径和用户。

 

 

⑩ 使能 sysfs debug 目录

  后续调试驱动的时候我们可能要用到/sys/kernel/debug 目录,默认我们没有挂载 debugfs 文件系统,所以/sys/kernel/debug 目录下没有任何文件,挂载方法很简单。 输入以下代码:

mount -t debugfs none /sys/kernel/debug

# 修改完重启

 

 

5. 烧写根文件系统到 EMMC  

① 根文件系统打包

1、新建 ext4 格式磁盘

  输入如下命令创建 ext4 磁盘: 

cd ~
mkdir /home/alientek/linux/rootfs
cd /home/alientek/linux/rootfs   
dd if=/dev/zero of=rootfs.ext4 bs=1M count=1024 # dd 命令创建一个名为 rootfs.ext4 的磁盘,由于根文件系统比较大,因此 count设置为 1024,也就是根文件系统总空间为 1GB
mkfs.ext4 -L rootfs rootfs.ext4 

# "mkfs.ext4":这是用于创建Ext4文件系统的命令。
# "-L rootfs":这个选项用于设置文件系统的标签,将文件系统的标签设置为"rootfs"。
# "rootfs.ext4":指定要格式化的文件系统所在的设备或文件。在这个例子中,我们使用"rootfs.ext4"作为文件系统。

 

2、将系统镜像拷贝到 ext4 磁盘

  首先创建一个目录用来挂载前面制作制作出来的 rootfs.ext4:

sudo mkdir /mnt/rootfs

  然后使用 mount 命令将 rootfs.ext4 挂载到 /mnt/rootfs 目录下:

cd /home/alientek/linux/rootfs
mount rootfs.ext4 /mnt/rootfs/

  挂载成功以后就将/home/alientek/linux/nfs/rootfs 目录下的所有根文件系统文件拷贝到/mnt/bootfs 目录下,命令如下: 

cd /home/alientek/linux/nfs/rootfs/
sudo cp * /mnt/rootfs/ -drf

  umount 卸载 /mnt/rootfs:

sudo umount /mnt/rootfs

 

② 烧写到 EMMC

  根文件系统 rootfs.ext4 烧写到开发板的 EMMC里面,使用 STM32CubeProgrammer 软件完成此操作,并修改 Flashlayout。

 

③ EMMC 启动测试

 烧写完成后,用 uboot 命令行,设置 bootcmd 和 bootargs 两个环境变量:

setenv bootcmd 'ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157datk.dtb;bootm c2000000 - c4000000'
setenv bootargs 'console=ttySTM0,115200 root=/dev/mmcblk1p3 rootwait rw'
saveenv
boot

  SOIO WIFI:SDIO(Secure Digital Input Output)是一种用于在设备之间传输数据的接口标准,而SDIO WIFI则指通过SDIO接口连接的无线网络适配器。SDIO WIFI模块是一种将WIFI功能集成到SDIO接口上的设备,它允许设备通过SDIO接口连接到无线网络。这种设备通常用于便携式设备、物联网设备和嵌入式系统,提供了通过WIFI无线网络进行通信和连接的能力。

  正点原子出厂系统使能了 SDIO WIFI,而 SDIO WIFI 也是 SDIO 接口,如果使能了 SDIO WIFI 那么 SDIO WIFI 所使用的那个 SDIO 接口就变成了 mmcblk1,而 EMMC 所使用的 SDIO 接口就会变为 mmcblk2 这也解释了为什么根文件系统是存放在 mmcblk2的第 3 个分区,而这里设置的是 mmcblk1。