Rockchip RK3399 - busybox 1.36.0制作根文件系统制作

发布时间 2023-05-28 20:12:49作者: 大奥特曼打小怪兽

Mini2440之linux根文件系统yaffs2移植文章中我们已经介绍了根文件系统的概念以及制作过程,不过当时是制作的是yaffs2类型的根文件系统,适用于外部设备Nand Flash,这一节我们将制作ramdisk、ext4类型的根文件系统。制作流程基本是一致的。

一、 编译、安装、配置 busybox

1.1 下载源码

根文件系统是根据busybox来制作的。下载地址:https://busybox.net/downloads/

这里我们就以1.36.0版本为例进行编译安装介绍:

注意:一般你使用什么版本的交叉编译器来编译linux内核时,文件系统中的所有程序也要使用同样的交叉编译器来编译。

下载完成后解压:

root@zhengyang:/work/sambashare/rk3399# wget https://busybox.net/downloads/busybox-1.36.0.tar.bz2
tar
root@zhengyang:/work/sambashare/rk3399# tar -jxf busybox-1.36.0.tar.bz2

1.2 配置busybox

在路径busybox-1.36.0下运行如下命令,使用默认配置:

root@zhengyang:/work/sambashare/rk3399# cd busybox-1.36.0/
root@zhengyang:/work/sambashare/rk3399/busybox-1.36.0# make defconfig

然后进入图像化配置页面,修改配置 :

root@zhengyang:/work/sambashare/rk3399/busybox-1.36.0# make menuconfig

进入Busybox Settings,配置如下参数:

Settings --->
    (/usr/local/arm/12.2.1/bin/arm-linux-) Cross compile prefix
    ( -march=armv8-a) additional CFLAGS 
    [ ] Build static binary (no shared libs)
    [*] Build Shared libbusybox 

编译方式有两种:

  • 第一种是以静态方式编译,即生成的busybox不需要动态库的支持就能运行。这样做我们就不需要部署动态库了,缺点就是自己写的程序在这个根文件系统中是不能运行的,因为缺少动态库库的支持。
  • 第二种方式使用动态编译,这样的话我们就需要部署动态库了,在linux动态库文件是以so为后缀,而windows下文件是以dll为后缀。

这里我们指定了arm-linux-gcc  12.2.1编译器的路径,这里就不具体介绍编译器如何安装了。

继续配置:

Linux System Utilities --->
    [] nsenter
Coreutils --->
    [] sync
Miscellaneous Utilities  --->
    自己选择
Networking Utilities --->
    自己选择
Linux System Utilities --->
   [*] hexdump    

1.3 编译安装

运行命令:

root@zhengyang:/work/sambashare/rk3399/busybox-1.36.0# make

然后查看当前路径下是否存在busybox:

root@zhengyang:/work/sambashare/rk3399/busybox-1.36.0# ll busybox
-rwxr-xr-x 1 root root 1119768 May 19 19:28 busybox*

执行make install:

root@zhengyang:/work/sambashare/rk3399/busybox-1.36.0# make install

make install的目的就是将编译生成的可执行程序及其依赖的库文件、配置文件、头文件安装到当前系统中指定(一般都可以自己指定安装到哪个目录下,如果不指定一般都有个默认目录)的目录下,默认被安装到_install 目录下:

root@zhengyang:/work/sambashare/rk3399/busybox-1.36.0# ll _install/
总用量 92
drwxr-xr-x  6 root root  4096 May 19 19:29 ./
drwxr-xr-x 38 root root  4096 May 19 19:29 ../
drwxr-xr-x  2 root root  4096 May 19 19:29 bin/
drwxr-xr-x  2 root root  4096 May 19 19:29 lib64/
-rwxr-xr-x  1 root root 67392 May 19 19:28 linuxrc*
drwxr-xr-x  2 root root  4096 May 19 19:29 sbin/
drwxr-xr-x  4 root root  4096 May 19 19:29 usr/

里面有5个文件:bin、sbin、usr这三个目录里都是二进制命令工具,lib64里面是库文件,这还不足以构成 一个可用的根文件系统,必须进行其它完善工作,才能构建一个可用的根文件系统。

二、 构建根文件系统

新建一个目录用来存放制作的根文件系统,可以命名为rootfs。将利用BusyBox生成的二进制文件及目录,即_install目录下的所有文件及目录复制到rootfs目录下。

root@zhengyang:/work/sambashare/rk3399/busybox-1.36.0# mkdir ../rootfs
root@zhengyang:/work/sambashare/rk3399/busybox-1.36.0# cp -a _install/* ../rootfs/

2.1 添加库文件

切换到rootfs路径下:

root@zhengyang:/work/sambashare/rk3399/busybox-1.36.0# cd ../rootfs

找到交叉编译工具里的动态库复制到lib目录下:

root@zhengyang:/work/sambashare/rk3399/rootfs# mkdir lib
root@zhengyang:/work/sambashare/rk3399/rootfs# cp -a  /usr/local/arm/12.2.1/aarch64-none-linux-gnu/libc/lib/*so* ./lib/
root@zhengyang:/work/sambashare/rk3399/rootfs# cp -a  /usr/local/arm/12.2.1/aarch64-none-linux-gnu/libc/lib64/*so* ./lib64/
root@zhengyang:/work/sambashare/rk3399/rootfs# cp -a  /usr/local/arm/12.2.1/aarch64-none-linux-gnu/libc/usr/lib/*so* ./lib/
root@zhengyang:/work/sambashare/rk3399/rootfs# cp -a  /usr/local/arm/12.2.1/aarch64-none-linux-gnu/libc/usr/lib64/*so* ./lib64/

-a保留权限,复制软链接本身,递归复制。

查看库文件:

root@zhengyang:/work/sambashare/rk3399/rootfs# ls ./lib
ld-linux-aarch64.so.1  libinproctrace.so
root@zhengyang:/work/sambashare/rk3399/rootfs# ls ./lib64/
libanl.so               libcrypt.so.1         libhwasan.so.0.0.0  libnss_db.so.2       libstdc++.so.6.0.30-gdb.py
libanl.so.1             libc.so               libitm.so           libnss_dns.so.2      libthread_db.so
libasan.so              libc.so.6             libitm.so.1         libnss_files.so.2    libthread_db.so.1
libasan.so.8            libdl.so.2            libitm.so.1.0.0     libnss_hesiod.so     libtsan.so
libasan.so.8.0.0        libgcc_s.so           liblsan.so          libnss_hesiod.so.2   libtsan.so.2
libatomic.so            libgcc_s.so.1         liblsan.so.0        libpcprofile.so      libtsan.so.2.0.0
libatomic.so.1          libgfortran.so        liblsan.so.0.0.0    libpthread.so.0      libubsan.so
libatomic.so.1.2.0      libgfortran.so.5      libmemusage.so      libresolv.a          libubsan.so.1
libBrokenLocale.so      libgfortran.so.5.0.0  libm.so             libresolv.so         libubsan.so.1.0.0
libBrokenLocale.so.1    libgomp.so            libm.so.6           libresolv.so.2       libutil.so.1
libbusybox.so.1.36.0    libgomp.so.1          libnsl.so.1         librt.so.1
libc_malloc_debug.so    libgomp.so.1.0.0      libnss_compat.so    libstdc++.so
libc_malloc_debug.so.0  libhwasan.so          libnss_compat.so.2  libstdc++.so.6
libcrypt.so             libhwasan.so.0        libnss_db.so        libstdc++.so.6.0.30

这里只是拷贝动态链接库。一般开发程序使用动态编译需要板子上动态库的支持才能运行,所以拷贝动态库。

2.2 构建etc目录

初始化配置脚本放在在/etc目录下,用于系统启动所需的初始化配置脚本。BusyBox提供了一些初始化范例脚本,在examples/bootfloppy/etc/目录下:

root@zhengyang:/work/sambashare/rk3399# cd busybox-1.36.0/
root@zhengyang:/work/sambashare/rk3399/busybox-1.36.0# ll examples/bootfloppy/etc/
总用量 24
drwxr-xr-x 3 root root 4096 Jan  3 22:17 ./
drwxr-xr-x 3 root root 4096 Jan  3 22:17 ../
-rw-r--r-- 1 root root   33 Jan  3 22:17 fstab
drwxr-xr-x 2 root root 4096 Jan  3 22:17 init.d/
-rw-r--r-- 1 root root  100 Jan  3 22:17 inittab
-rw-r--r-- 1 root root  133 Jan  3 22:17 profile

在rootfs目录,创建etc文件夹:

root@zhengyang:/work/sambashare/rk3399/rootfs# mkdir etc

将这些配置文件复制到新制作的根文件系统etc目录下。

root@zhengyang:/work/sambashare/rk3399/rootfs# cp -a ../busybox-1.36.0/examples/bootfloppy/etc/* ./etc
root@zhengyang:/work/sambashare/rk3399/rootfs# ll etc
总用量 24
drwxr-xr-x 3 root root 4096 May 19 20:24 ./
drwxr-xr-x 8 root root 4096 May 19 20:23 ../
-rw-r--r-- 1 root root   33 Jan  3 22:17 fstab
drwxr-xr-x 2 root root 4096 Jan  3 22:17 init.d/
-rw-r--r-- 1 root root  100 Jan  3 22:17 inittab
-rw-r--r-- 1 root root  133 Jan  3 22:17 profile
2.2.1  修改/etc/inittab文件

/etc/inittab文件是init进程解析的配置文件,通过这个配置文件决定执行哪个进程,何时执行。将文件修改为:

# 系统启动时
::sysinit:/etc/init.d/rcS

# 系统启动按下Enter键时
::askfirst:-/bin/sh

# 按下Ctrl+Alt+Del键时
::ctrlaltdel:/sbin/reboot

# 系统关机时
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r

# 系统重启时
::restart:/sbin/init

以上内容定义了系统启动时,关机时,重启时,按下Ctrl+Alt+Del键时执行的进程。

2.2.2 修改/etc/init.d/rcS文件
#!/bin/sh

# 挂载 /etc/fstab 中定义的所有文件系统
/bin/mount -a

# 挂载虚拟的devpts文件系统用于用于伪终端设备
/bin/mkdir -p /dev/pts
/bin/mount -t devpts devpts /dev/pts

# 使用mdev动态管理u盘和鼠标等热插拔设备
/bin/echo /sbin/mdev > /proc/sys/kernel/hotplug

# 扫描并创建节点
/sbin/mdev -s

修改init.d文件权限:

root@zhengyang:/work/sambashare/rk3399/rootfs#  chmod -R 777 etc/init.d/*
2.2.3  修改/etc/fstab 

/etc/fstab文件存放的是文件系统信息。在系统启动后执行/etc/init.d/rcS文件里/bin/mount -a命令时,自动挂载这些文件系统。

# <file system>    <mount point>    <type>    <options>    <dump>    <pass>     
proc                  /proc          proc     defaults       0         0
sysfs                 /sys           sysfs    defaults       0         0
tmpfs                 /tmp           tmpfs    defaults       0         0
tmpfs                 /dev           tmpfs    defaults       0         0

这里我们挂载的文件系统有三个proc、sysfs和tmpfs,在内核中proc和sysfs默认都支持,而tmpfs是没有支持的,我们需要添加tmpfs的支持。

2.2.4 修改/etc/profile文件

/etc/profile文件作用是设置环境变量,每个用户登录时都会运行它,将文件内容修改为:

# 主机名
export HOSTNAME=zy

# 用户名
export USER=root

# 用户目录
export HOME=/root

# 终端默认提示符
export PS1="[$USER@$HOSTNAME:\$PWD]\# "    

# 环境变量
export PATH=/bin:/sbin:/usr/bin:/usr/sbin

# 动态库路径
export LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH

2.3 构建dev目录

在rootfs目录,创建dev文件夹:

root@zhengyang:/work/sambashare/rk3399/rootfs# mkdir dev

创建终端文件:

root@zhengyang:/work/sambashare/rk3399/rootfs# sudo mknod dev/console c 5 1
root@zhengyang:/work/sambashare/rk3399/rootfs# sudo mknod dev/null c 1 3

2.4 构建其他文件

root@zhengyang:/work/sambashare/rk3399/rootfs# mkdir mnt proc tmp sys root
root@zhengyang:/work/sambashare/rk3399/rootfs# ll
总用量 124
drwxr-xr-x 14 root root  4096 May 19 20:33 ./
drwxr-xr-x 12 root root  4096 May 19 20:15 ../
drwxr-xr-x  2 root root  4096 May 19 19:29 bin/
drwxr-xr-x  2 root root  4096 May 19 20:33 dev/
drwxr-xr-x  3 root root  4096 May 19 20:32 etc/
drwxr-xr-x  2 root root  4096 May 19 20:20 lib/
drwxr-xr-x  2 root root  4096 May 19 20:21 lib64/
-rwxr-xr-x  1 root root 67392 May 19 19:28 linuxrc*
drwxr-xr-x  2 root root  4096 May 19 20:33 mnt/
drwxr-xr-x  2 root root  4096 May 19 20:33 proc/
drwxr-xr-x  2 root root  4096 May 19 20:33 root/
drwxr-xr-x  2 root root  4096 May 19 19:29 sbin/
drwxr-xr-x  2 root root  4096 May 19 20:33 sys/
drwxr-xr-x  2 root root  4096 May 19 20:33 tmp/
drwxr-xr-x  4 root root  4096 May 19 19:29 usr/

三、制作ext4根文件系统镜像

建立根文件系统挂载点:

root@zhengyang:/work/sambashare/rk3399# mkdir rk3399_rootfs

生成rk3399_ext4.img容器,容器大小300m,具体需要的大小可以通过du build -h查看我们根文件系统大小调整;

root@zhengyang:/work/sambashare/rk3399# dd if=/dev/zero of=rk3399_ext4.img bs=1M count=300
记录了300+0 的读入
记录了300+0 的写出
314572800字节(315 MB,300 MiB)已复制,0.166846 s,1.9 GB/s
root@zhengyang:/work/sambashare/rk3399# ll rk3399_ext4.img
-rw-r--r-- 1 root root 314572800 May 25 21:39 rk3399_ext4.img
root@zhengyang:/work/sambashare/rk3399# mkfs.ext4 rk3399_ext4.img

将容器挂载到挂载根文件系统:

root@zhengyang:/work/sambashare/rk3399# mount rk3399_ext4.img rk3399_rootfs

对文件系统进行操作,将制作的文件系统复制到挂载点:

root@zhengyang:/work/sambashare/rk3399# cp ./rootfs/* ./rk3399_rootfs/ -af

使用df命令可以查看是否已经挂载:

root@zhengyang:/work/sambashare/rk3399# df rk3399_rootfs/
文件系统        1K-块  已用   可用 已用% 挂载点
/dev/loop12    280880 99872 159504   39% /work/sambashare/rk3399/rk3399_rootfs

卸载根文件系统:

root@zhengyang:/work/sambashare/rk3399# umount rk3399_rootfs
root@zhengyang:/work/sambashare/rk3399# ll rk3399_rootfs/
总用量 8
drwxr-xr-x  2 root root 4096 May 25 21:47 ./
drwxr-xr-x 14 root root 4096 May 25 21:47 ../

此时我们已经得到了ext4根文件系统镜像rk3399_ext4.img:

root@zhengyang:/work/sambashare/rk3399# ll rk3399_ext4.img
-rw-r--r-- 1 root root 314572800 May 25 21:54 rk3399_ext4.img

四、制作ramdist根文件系统镜像

前面我们已经制作了ext4文件文件系统,至于ramdisk,就是用内存模拟了一个块设备(比如内核设备节点/dev/ram0),这个模拟的“块设备”是需要一个文件系统(比如ext4或者其它文件系统)来组织数据的。

这里为了减少文件系统的大小,我们对rk3399_ext4.img文件系统进行镜像压缩,压缩后为 ramdisk.gz。

root@zhengyang:/work/sambashare/rk3399# gzip -v9 rk3399_ext4.img
rk3399_ext4.img:         91.2% -- replaced with rk3399_ext4.img.gz
root@zhengyang:/work/sambashare/rk3399# mv rk3399_ext4.img.gz ramdisk.gz

如果要使用ramdisk根文件系统,除了在make menuconfig阶段配置的RAM块设备驱动的支持,还需要:

  • 在uboot启动阶段将ramdisk.gz加载到内存某个位置;这个比较简单,无论是通过tftp下载,还是从eMMC等外部存储设备加载到内存都可以;
  • 在uboot启动命令行中指定根文件系统的位置;修改uboot启动参数bootargs中的root属性为root=/dev/ram0,表示根目录挂载点为/dev/ram0块设备;
  • 告诉内核ramdisk.gz在内存的位置;这个有两种方式,下面具体介绍;

4.1 配置内核启动参数bootargs

假设ramdisk.gz文件被加载到内存指定位置0x42000000。修改bootargs加入如下配置:

 initrd=0x42000000,0x14000000

initrd参数格式为:地址,长度,这里设备ram地址为0x42000000起始,只要是在内核DDR物理地址空间内。长度这里只要比ramdisk.gz压缩包大小大就可以了;

4.2 bootm指定ramdisk加载地址

bootm命令支持多个参数:

bootm <Legacy uImage addr> <ramdisk addr>  <dtb addr>

其中第二个参数就是ramdisk.gz文件加载到内存中的地址,和加载uIamge时一样,bootm加载ramdisk时,也需要判断ramdisk.gz的64字节数据头。所以用这种方法向内核传递ramdisk时,ramdisk.gz需要通过mkimage命令给ramdisk.gz加上64字节头。

在命令行使用uboot根目录tools/mkimage工具编译即可:

mkimage -A arm -O linux -C gzip -T ramdisk -d ramdisk.gz ramdisk_1.gz

执行完成后生成ramdisk_1.gz压缩包,将其加载到内存地址0x42000000,使用bootm命令启动。

参考文章

[1]  嵌入式ARM64 Linux内核编译及根文件系统构建

[2]  Mini2440之linux根文件系统yaffs2移植