Yuhang Zheng

使用chroot命令切换arm64的文件系统

N 人看过

在嵌入式系统的开发中,现在大多数的文件系统的打包方式都是使用mkfs.ext4命令来将文件系统目录打包成ext4格式的img镜像,如果有时想要在现成的文件系统中直接增加一些东西的话,我们可以直接操作打包好的镜像来增删文件,而不需要再次重新编译来再使用SDK重新走一遍完整的打包镜像的过程。

一般需求

我们直接使用qemu工具即可,在Ubuntu开发环境中使用以下命令安装qemu工具

sudo apt-get install qemu-user-static

首先可以先检查一下文件系统镜像是否是可以直接挂载的格式:

$ file ubuntu22.04_rootfs.img 
ubuntu22.04_rootfs.img: Linux rev 1.0 ext4 filesystem data, UUID=57f8f4bc-abf4-655f-bf67-946fc0f9f25b (extents) (large files)

这种就是raw ext4 image,即经常说的raw image。其特点是完整的ext4分区镜像(包含很多全零的无效填充区),可以直接使用mount进行挂载,因此比较大。

这种镜像的生成命令一般以下这种:

TARGET_ROOTFS_DIR=./rootfs
ROOTFSIMAGE=ubuntu22.04_rootfs.img
EXTRA_SIZE_MB=300
IMAGE_SIZE_MB=$(( $(du -sh -m ${TARGET_ROOTFS_DIR} | cut -f1) + ${EXTRA_SIZE_MB} ))

#计算了一下TARGET_ROOTFS_DIR实际占用的大小,并再次基础上额外预留出300M空间
echo "dd if=/dev/zero of=${ROOTFSIMAGE} bs=1M count=0 seek=${IMAGE_SIZE_MB}"
dd if=/dev/zero of=${ROOTFSIMAGE} bs=1M count=0 seek=${IMAGE_SIZE_MB}

echo "mkfs.ext4 -d ${TARGET_ROOTFS_DIR} ${ROOTFSIMAGE}"
mkfs.ext4 -d ${TARGET_ROOTFS_DIR} ${ROOTFSIMAGE}

还有一种文件系统镜像是如下的这种

$ file root.sparse.img
root.sparse.img: Android sparse image, version: 1.0, Total of 1833728 4096-byte output blocks in 55625 input chunks.

这种就是是sparse ext4 image,即经常说的simg。由于它将raw ext4进行稀疏描述,因此尺寸比较小(没有全零的无效填充区)

这种镜像的生成命令一般以下这种:

make_ext4fs -s -T -I -l 7510982656 $FBDIR/build/images/root.sparse.img $RFSDIR

-s 表示制作sparse ext4 image
-T 表示Unix时间戳,对root.sparse.img中的文件设置修改时间
-I 表示inode的大小
-l 表示最大的文件大小(也就是转为raw image的大小,该值受限于实际的分区大小);

我们可以通过simg2img和img2simg工具在以上两种格式之间进行转换。

simg2img root.sparse.img root.raw.img
img2simg root.raw.img root.sparse.img

确认完镜像格式为可以直接挂载的格式之后,我们将文件系统镜像直接挂载到某个路径下就可以

mkdir /mount_dir
sudo mount ubuntu22.04_rootfs.img /mount_dir

有的适合会遇到镜像挂载不上的情况,这个时候我们需要检查一下/dev/loop是否被用完了

$ ls /dev/loop*
/dev/loop0  /dev/loop1  /dev/loop2  /dev/loop3  /dev/loop4  /dev/loop5  /dev/loop6  /dev/loop7  /dev/loop-control

$ losetup -l
NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE                                       DIO LOG-SEC
/dev/loop1         0      0         1  1 /var/lib/snapd/snaps/core20_1974.snap             0     512
/dev/loop4         0      0         1  1 /var/lib/snapd/snaps/snapd_19457.snap (deleted)   0     512
/dev/loop2         0      0         1  1 /var/lib/snapd/snaps/lxd_24322.snap               0     512
/dev/loop0         0      0         1  1 /var/lib/snapd/snaps/snapd_19993.snap             0     512
/dev/loop5         0      0         1  1 /var/lib/snapd/snaps/core20_2015.snap             0     512
/dev/loop3         0      0         1  1 /var/lib/snapd/snaps/snapd_20092.snap             0     512

如果真的被用完了,并且没有可以释放的loop的话,可以通过以下命令来新建loop设备

方法一:使用mknod命令
sudo mknod -m 0660 /dev/loop11 b 7 11

    mknod命令用法:mknod [选项]… 名称 类型 [主设备号 次设备号]
    -m选项设置权限模式
    /dev/loop11是loop设备文件路径
    b选项指示创建(有缓冲的)区块特殊文件
    7是主设备号
    11是次设备号,与设备文件名相对应

方法二:使用losetup -f命令
sudo losetup -f

    当还有未使用回环设备时,返回第一个未使用的设备。
    如果没有可使用的回环设备时,创建一个新的回环设备。
    需要注意的是,可能会创建新设备,所有要注意权限问题。

最后,将适合的qemu-aarch64-static二进制文件复制到chroot环境中:

sudo cp /usr/bin/qemu-aarch64-static /mount_dir/usr/bin/

现在,应该能够使用chroot进入到ARM64文件系统并运行/bin/bash了。

对文件系统进行修改完成之后,记得使用umount命令卸载掉挂载的文件系统镜像,这个时候ubuntu22.04_rootfs.img也已经同步的进行了修改。

sudo umount /mount_dir

进阶需求

如果只是简单的增删一些文件的话,以上的方法已经完全可以了,但是如果我们想要模拟更为接近于实际开发板中的情况,比如使用apt-get工具安装一些软件,或者使用/dev/下的某些设备进行一些操作,我们就需要去挂载一下真实宿主机的目录到qemu环境中

在宿主机环境中执行以下命令

chroot_dir=/mount_dir

# Mount the temporary API filesystems
mkdir -p ${chroot_dir}/{proc,sys,run,dev,dev/pts}
mount -t proc /proc ${chroot_dir}/proc
mount -t sysfs /sys ${chroot_dir}/sys
mount -o bind /dev ${chroot_dir}/dev
mount -o bind /dev/pts ${chroot_dir}/dev/pts

然后再使用上文中的方法直接chroot进去操作就可以了。

记得操作完成之后一定要umount掉上面我们挂载的东西,不然最后使用umount命令卸载掉挂载的文件系统镜像的时候可是会报错的哦。

# Umount the temporary API filesystems
umount -lf ${chroot_dir}/dev/pts 2> /dev/null || true
umount -lf ${chroot_dir}/* 2> /dev/null || true

rm ${chroot_dir}/dev/console
rm ${chroot_dir}/dev/full
rm ${chroot_dir}/dev/null
rm ${chroot_dir}/dev/ptmx
rm ${chroot_dir}/dev/random
rm ${chroot_dir}/dev/tty
rm ${chroot_dir}/dev/urandom
rm ${chroot_dir}/dev/zero

补充内容

有时候,我们在开发过程中需要使用友商的文件系统镜像来进行一些功能的验证,方法如下:

首先查看镜像的分区信息

$ fdisk -l  Orangepi5_1.1.4_ubuntu_jammy_desktop_gnome_linux5.10.110.img
Disk Orangepi5_1.1.4_ubuntu_jammy_desktop_gnome_linux5.10.110.img: 8.44 GiB, 9055502336 bytes, 17686528 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: CEDF4F9F-2FAF-CE45-85F4-1CB36BB907AB

Device                                                          Start      End  Sectors  Size Type
Orangepi5_1.1.4_ubuntu_jammy_desktop_gnome_linux5.10.110.img1   61440  2158591  2097152    1G Linux extended boot
Orangepi5_1.1.4_ubuntu_jammy_desktop_gnome_linux5.10.110.img2 2158592 17686494 15527903  7.4G Linux filesystem

可以看到从镜像的2158592个sectors开始开始是文件系统信息,而一个sectors是512 bytes,所以提取方法如下:

dd if=Orangepi5_1.1.4_ubuntu_jammy_desktop_gnome_linux5.10.110.img of=rootfs.img bs=512 skip=2158592

最后我们就得到了rootfs.img镜像,也就是文件系统的镜像。

还有一种是efi格式的镜像,查看镜像的分区信息

$ fdisk -l ubuntu-22.04-preinstalled-desktop-arm64-roc-rk3588s-pc.img
Disk ubuntu-22.04-preinstalled-desktop-arm64-roc-rk3588s-pc.img:7.1 GiB,7625244672 字节,14893056 个扇区
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:gpt
磁盘标识符:FC29FAA6-D260-4387-9629-D23D07ACCB74

设备                                                         起点     末尾     扇区  大小 类型
ubuntu-22.04-preinstalled-desktop-arm64-roc-rk3588s-pc.img1 32768 14891007 14858240  7.1G EFI 系统

可以看到从镜像的32768个sectors开始开始是文件系统信息,而一个sectors是512 bytes,所以提取方法如下:

dd if=ubuntu-22.04-preinstalled-desktop-arm64-roc-rk3588s-pc.img of=rootfs.img bs=512 skip=32768

得到的这个镜像也可以使用上面的方法来直接挂载进行软件的安装和文件的增删,但是有时会提示空间不足的情况,这个时候我们可以使用 qemu-img 给rootfs.img增加空间

在尾部追加空间

qemu-img resize rootfs.img +7G #调整大小

然后再拓展分区和文件系统

e2fsck -f rootfs.img #检查文件系统
resize2fs rootfs.img #增大未加载的文件系统大小

这样就有比较大的空间来让我们进行操作了