使用USB存储启动Jetson¶
备注
我现在尚未采购合适的USB外接存储,虽然可以使用普通的USB移动硬盘来提高Jetson的存储性能(SD卡的性能实在太差了)。但是,我依然推荐采用固态存储设备,例如SSD移动硬盘,不过,为了配合移动设备的高效小巧,我非常推荐采用 NVMe SSD 存储通过 M.2 转接 USB方式的U盘。
默认情况下,Jetston Nano和 Raspberry Pi Atlas 一样,使用TF卡作为存储。但是由于SD/TF卡的读写性能很弱,使得系统性能无法充分发挥。所以,和 树莓派4 USB存储启动Ubuntu Server 20.04 相似,我也把Jetson Nano的操作系统迁移到移动硬盘上运行(通过USB 3.0接口)。
我的实践是采用USB移动HDD硬盘,虽然性能普通,但是比TF卡运行性能要好很多。 - 为了节约成本,我仅使用了非常普通的TF卡,所以读写性能极弱。
Jetson Nano – Run From USB Drive 在GitHub上开源了 JetsonHacksNano / rootOnUSB 的脚本,可以方便我们将NVIDIA的Jetson Nano启动rootfs修改成USB设备。
和 树莓派4 USB存储启动Ubuntu Server 20.04 有所不同,Jetson Nano没有提供从外接存储启动的设置,启动分为两步:
boot loader从所支持关键连接设备,也就是TF卡启动一个最小支持的内存镜像。这个步骤是通过
initrd
(initial ramdisk) 实现,将一个临时文件系统加载到内存来启动Linux内核。这个步骤我们不做修改,所以我们通过USB存储启动Jetson依然需要TF卡,关键修改是第二步。默认情况下,bootloader从TF卡加载好Linux内核以后,就会配置rootfs指向TF卡中的文件系统,这才是完整的文件系统,也就是我们平时使用操作系统最主要访问的文件系统,对运行性能影响极大。我们改造的就是这步,通过修改配置,将rootfs修改到USB外接磁盘设备上,通过外接磁盘设备来加速Linux文件系统性能。
需要注意的是,要在上述第二步中实现读写USB外接存储,不仅需要Linux内核内建USB驱动(默认已经build in),而且需要USB设备的firmware。很不幸,USB设备的firmware默认没有加载,所以就无法在启动过程中挂载尚未初始化的USB控制器的外接存储设备上的root文件系统。我们需要重新编译Linux内核来把关键的USB firmware编译到内核中。在 Jetson Nano Developer Forum帖子解析了构建initramfs方法 ,实现将USB firmware添加到initramfs的步骤。
rootOnUSB脚本¶
JetsonHacks提供了 JetsonHacksNano / rootOnUSB 脚本方便完成整个迁移过程。通过以下方式获取:
git clone https://github.com/JetsonHacksNano/rootOnUSB
cd rootOnUSB
备注
为了能够完整系统掌握这个迁移过程,我的实践操作是通过分析rootOnUSB脚本,通过独立的命令行操作来完成整个步骤。所以本文看上去比较繁琐,但是更容易理解原理。如果你只是需要完成操作,则可以直接执行脚本即可。
构建支持USB的initramfs¶
备注
这步可以通过脚本完成:
./addUSBToInitramfs.sh
创建一个名为
usb-firmware
脚本如下:if [ "$1" = "prereqs" ]; then exit 0; fi . /usr/share/initramfs-tools/hook-functions copy_file firmware /lib/firmware/tegra21x_xusb_firmware
这里 /usr/share/initramfs-tools/hook-functions
是initramfs-tools的hook功能脚本,由操作系统提供。这个 usb-firmware
脚本复制到 /etc/initramfs-tools/hooks
目录下,然后通过 /usr/share/initramfs-tools/hook-functions
提供的脚本函数 copy_file
来复制firmware,再通过 mkinitramfs
来构建镜像。
执行命令:
cp usb-firmware /etc/initramfs-tools/hooks cd /etc/initramfs-tools/hooks mkinitramfs -o /boot/initrd-xusb.img
这里有3个报错:
Warning: couldn't identify filesystem type for fsck hook, ignoring.
/sbin/ldconfig.real: Warning: ignoring configuration file that cannot be opened: /etc/ld.so.conf.d/aarch64-linux-gnu_EGL.conf: No such file or directory
/sbin/ldconfig.real: Warning: ignoring configuration file that cannot be opened: /etc/ld.so.conf.d/aarch64-linux-gnu_GL.conf: No such file or directory
我检查了一下,实际上配置文件存在:
/etc/ld.so.conf.d/aarch64-linux-gnu_EGL.conf -> /etc/alternatives/aarch64-linux-gnu_egl_conf
/etc/ld.so.conf.d/aarch64-linux-gnu_GL.conf -> /etc/alternatives/aarch64-linux-gnu_gl_conf
不过都是软链接,最终分别链接到:
/usr/lib/aarch64-linux-gnu/tegra-egl/ld.so.conf
/usr/lib/aarch64-linux-gnu/tegra/ld.so.conf
所以通过以下命令先复制成实际文件,处理完以后再恢复之前的软链接:
unlink /etc/ld.so.conf.d/aarch64-linux-gnu_EGL.conf
cp /usr/lib/aarch64-linux-gnu/tegra-egl/ld.so.conf /etc/ld.so.conf.d/aarch64-linux-gnu_EGL.conf
unlink /etc/ld.so.conf.d/aarch64-linux-gnu_GL.conf
cp /usr/lib/aarch64-linux-gnu/tegra/ld.so.conf /etc/ld.so.conf.d/aarch64-linux-gnu_GL.conf
上述 Warning: couldn't identify filesystem type for fsck hook
是因为 mkinitramfs
是通过 /etc/fstab
来检测文件系统的。 参考 UPDATE-INITRAMFS FAILS TO INCLUDE FSCK IN INITRD 我检查了 /etc/fstab
内容是:
/dev/root / ext4 defaults 0 1
实际上并没有设备 /dev/root
,Jetson Nano挂载根文件系统没有使用这个配置,通过 cat /proc/mounts
可以看到根挂载是:
/dev/mmcblk0p1 / ext4 rw,relatime,data=ordered 0 0
所以我暂时修改 /etc/fstab
如下:
/dev/mmcblk0p1 / ext4 defaults 0 1
再次执行就不再报错:
mkinitramfs -o /boot/initrd-xusb.img
完成以后,恢复 /etc/fstab
配置,然后执行以下命令恢复文件软链接:
rm -f /etc/ld.so.conf.d/aarch64-linux-gnu_EGL.conf
ln -s /etc/alternatives/aarch64-linux-gnu_egl_conf /etc/ld.so.conf.d/aarch64-linux-gnu_EGL.conf
rm -f /etc/ld.so.conf.d/aarch64-linux-gnu_GL.conf
ln -s /etc/alternatives/aarch64-linux-gnu_gl_conf /etc/ld.so.conf.d/aarch64-linux-gnu_GL.conf
USB移动硬盘准备¶
格式化移动硬盘
USB移动硬盘需要建立一个 ext4
分区,用于存储rootfs。注意,该分区必须是ext4文件系统。我的移动磁盘在Linux下识别名字是 /dev/sda
,你的设备名字可能不同:
fdisk /dev/sda
我划分了 128G 给 /dev/sda1
用于操作系统:
Disk /dev/sda: 465.8 GiB, 500107862016 bytes, 976773168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 33553920 bytes
Disklabel type: dos
Disk identifier: 0x5e878358
Device Boot Start End Sectors Size Id Type
/dev/sda1 2048 268437503 268435456 128G 83 Linux
Filesystem/RAID signature on partition 1 will be wiped.
创建ext4文件系统:
mkfs.ext4 /dev/sda1
复制Jetson操作系统¶
备注
简单执行脚本:
./copyRootToUSB.sh -p /dev/sda1
或者像我一样用命令行完成,见下文。
将移动硬盘连接到Jetson Nano上,然后执行命令:
sudo mount /dev/sda1 /mnt
检查挂载:
sudo findmnt -rno TARGET /dev/sda1
可以看到输出信息:
/media/78961b21-c52a-4aa2-ac83-c5fc757f6666
/mnt
同步数据:
sudo rsync -axHAWX --numeric-ids --info=progress2 --exclude=/proc / /mnt
rsync
参数解析:
-a
备注
注意,由于Jetson Nano当前只有一个TF卡,所以首次插入移动硬盘识别为 /dev/sda
设备,所以上述命令我的目标磁盘分区是 /dev/sda1
修改启动配置¶
Jetson Nano启动配置是 /boot/extlinux/extlinux.conf
,其中有一个入口就是指向 rootfs ,我们需要修改成移动硬盘:
cp /boot/extlinux/extlinux.conf /boot/extlinux/extlinux.conf.bak
检查移动硬盘分区的UUID,这个UUID需要配置到启动配置中:
find /dev/disk/by-uuid -lname '*/'sda1 -printf %f
输出类似:
78961b21-c52a-4aa2-ac83-c5fc757f6666
也可以通过