在售的 NVIDIA Jetson 内置 16 GB 的 eMMC,并已安装了
ubuntu 18.04 LTS
和
NVIDIA JetPack 4.6
,所以剩余的用户可用空间大约 2GB,这对将 NVIDIA Jetson 应用于一些项目的训练和部署是一个不小的阻碍。本教程会基于这样的处境,分别介绍不同型号 NVIDIA Jetson 的扩容过程,帮助开发者将系统转移到外部存储器设备而实现扩容的目的。
扩容的基本原理
装有系统的磁盘上面的第一个扇区被称为主引导记录(
MBR
),主引导记录存放了引导加载程序(
BootLoader
)、分区表以及固定标识“55AA”三个信息。在 Linux 的启动过程中,引导加载程序和内核(
Kernel
)会经历两个比较重要的阶段。
第一阶段:
引导加载程序会初始化(initrd)一个临时根文件系统(
ramfs
)。临时根文件系统中有启动时必要的驱动(drivers)、文件系统(fs)、网络(net)等配置程序。之后,引导加载程序的控制权就会转交给内核,以便内核能够取出这些程序,将其移动到内存(
RAM
)中运行,加载各个功能模块。
第二阶段:
内核借助临时根文件系统装载必要的功能模块之后,会释放该系统,并配置真正的根文件系统(
rootfs
)挂载到真正的根目录。
- 那么在上述的两个阶段中,内核借助临时根文件系统装载功能模块的部分(第一阶段)我们是不需要进行修改的,所以即使 NVIDIA Jetson 已经实现扩容,它仍会需要使用 eMMC。
- 我们需要改造的是第二个阶段,将根文件系统挂载到外部存储器中,从而实现扩容。
扩容注意事项
-
通过外部存储设备进行扩容的主要原理是将 rootfs 设置为在外部存储设备上。
-
此扩容方法将会对 Linux 内核级别的系统文件进行修改,您可能会遇到一些不容易解决的问题。您在根据此教程完成扩容操作时应当使用的是新的 NVIDIA Jetson 以及新的存储设备,不要尝试在设备中存放使用有价值的文件。如果一切未按预期,您可能会需要重新格式化存储设备甚至是 NVIDIA Jetson。对于最后的保留方案,我们会提供通过串口的方式尽可能帮助您恢复备份,但一切数据的丢失责任需要您自行承担。
-
此扩容过程与网上其他早期的扩容方法相比,不需要重新编译内核,节省了大约40分钟的安装时间。
通过载板上的 M.2 插槽和 SSD 扩容
SSD 又称为固态硬盘(Solid State Drives),常作为笔记本、台式机等的主要存储设备。它具有可靠性高、数据读写数率快等优势,这将会是 NVIDIA Jetson 扩容的最佳选择。此方法仅适用于 NVIDIA Jetson Xavier NX 系列。
软硬件要求
使用 SSD 进行扩容的方案需要尽量满足如下条件,这也是经过验证可以顺利完成扩容的基本要求。
|
软硬件要求 |
NVIDIA Jetson |
JetPack 版本 4.4 ~ 4.6 载板需含有 M.2 M-Key 插槽 |
SSD |
SSD 需为第四代扩展文件系统(Ext4) NVMe 协议的 M.2 M-Key 接口 推荐容量 ≤ 512 GB |
⚠️ 注意:
- 更新的 JetPack 版本是未经过扩容测试的,无法保证扩容的稳定性或成功性,请慎重按此教程进行扩容。
- SSD 需要注意接口需为 M.2 M-Key,否则无法与载板上的接口相匹配。
- 非第四代扩展文件系统(Ext4)的存储设备不能完成扩容操作。
扩容步骤
-
第 1 步:安装 SSD
请按照“硬件使用说明”中的步骤为 NVIDIA Jetson 安装好 SSD。
-
第 2 步:准备 SSD
使用快捷键
Ctrl+F
或点击左上角的 Ubuntu 图标搜索 Disks,打开 Ubuntu 18.04 自带的 Disks 工具。
在左侧选择你的 SSD,然后在右上角的菜单栏下选择“Format Disk”。
将你的 SSD 格式化成 GPT 格式。此时会弹出窗口让你确认并输入用户密码。
然后,我们点击中间的“+”,添加盘符。
点击“Next”。
请给你的 SSD 起一个名字,并在类型中选择第四代扩展文件系统(Ext4),最后点击“Create”即可。此时我们已经按照扩容的要求完成了 SSD 的前期准备。
-
第 3 步:将根目录构建到 SSD 上
使用 git 命令将我们需要使用的脚本文件下载到 NVIDIA Jetson 上。
$ git clone https://github.com/limengdu/rootOnNVMe.git $ cd rootOnNVMe/
然后执行如下命令,将 eMMC 中根目录的文件构建到 SSD 中,这个步骤的等待时间视您所使用的根目录大小所决定。
$ ./copy-rootfs-ssd.sh
-
第 4 步:配置环境,完成扩容
执行如下命令,完成 rootfs 的配置。
$ ./setup-service.sh
重新启动 NVIDIA Jetson,会发现 eMMC 已经变成了外部存储设备显示在主界面上,查看系统占用空间也发现变少了,扩容成功。
⚠️ 注意:
-
脚本文件中默认的 SSD 路径为
/dev/nvme0n1p1
,这也是 NVIDIA Jetson 默认分配的路径。如果您通过命令
sudo fdisk -l
查询到您的 SSD 路径与此不一致,请更改 rootOnNVMe 中文件 copy-rootfs-ssd.sh、data/setssdroot.service 和 data/setssdroot.sh 中所有
/dev/nvme0n1p1
的路径为您 SSD 所在的路径。
2. 上述扩容操作不会删除 eMMC 中原来的根目录内容,如果您不想从 SSD 启动系统,您可以拆除 SSD,系统依旧可以从 eMMC 中完成启动。
通过 USB 存储设备进行扩容
USB 存储设备例如 U 盘、移动硬盘等,作为生活中常见的外部存储器,广泛应用在各个领域,USB 的扩容方式也同样适用于 NVIDIA Jetson。此方法对能够使用的设备没有限制。
相较于通过 SSD 的方法进行扩容,通过 USB 存储设备进行扩容最大的优势是 USB 设备具有高度的便捷性,且移除设备比较简单。但是,即使是高速的 USB 3.0 接口,数据传输的数率也远不及标准的 PCIe 总线,所以从稳定性、可靠性和数据传输速度来讲,通过 SSD 扩容的方法会更胜一筹。
软硬件要求
使用 USB 进行扩容的方案需要尽量满足如下条件,这也是经过验证可以顺利完成扩容的基本要求。
|
软硬件要求 |
NVIDIA Jetson |
JetPack 版本 4.4 ~ 4.6 核心模组需为 Jetson Nano |
USB 存储设备 |
USB 存储设备需为第四代扩展文件系统(Ext4) USB 存储设备供电电流 ≤ 0.5 A |
⚠️ 注意:
- 更新的 JetPack 版本是未经过扩容测试的,无法保证扩容的稳定性或成功性,请慎重按此教程进行扩容。
- 大容量 USB 存储设备需要确保 NVIDIA Jetson 能够正常为其供电以维持正常工作,不建议使用 512 GB 以上容量的 USB 存储设备。供电不足可能会导致 NVIDIA Jetson 断电关机。
- 非第四代扩展文件系统(Ext4)的存储设备不能完成扩容操作。
扩容步骤
-
第 1 步:
准备必要的文件
使用 git 命令下载我们需要用于 NVIDIA Jetson 的脚本文件。
$ git clone https://github.com/limengdu/bootFromUSB.git $ cd bootFromUSB
-
第 2 步:准备 USB 存储设备
将 USB 存储设备连接到 NVIDIA Jetson。使用快捷键
Ctrl+F
或点击左上角的 Ubuntu 图标搜索 Disks,打开 Ubuntu 18.04 自带的 Disks 工具。
在左侧选择你的 USB 存储设备,然后在右上角的菜单栏下选择“Format Disk”。
将你的 USB 存储设备格式化成 GPT 格式。此时会弹出窗口让你确认并输入用户密码。
然后,我们点击中间的“+”,添加盘符。
点击“Next”。
请给你的 USB 存储设备起一个名字,并在类型中选择第四代扩展文件系统(Ext4),最后点击“Create”即可。此时我们已经按照扩容的要求完成了 USB 存储设备的前期准备。
-
第 3 步:挂载 USB 存储设备
按照第 2 步准备好的 USB 存储设备可以在 Disks 软件中看到显示的是未挂载的情况。
我们使用如下命令挂载该 USB 设备。
$ mkdir /media/USB/ $ sudo mount <USB Device Path> /media/USB/
其中,
<USB Device Path>
指的是 USB 存储设备的路径,此参数可以在 Disks 软件的
Device
中看到,也可以通过命令
sudo fdisk -l
查询得到。例如对于我的 USB 设备,我可以使用如下命令将 /dev/sda1 挂载到 /media/USB/ 中。
$ sudo mount /dev/sda1 /mnt
使用如下命令检查设备的挂载位置。
$ sudo findmnt -rno TARGET <USB Device Path>
对于我的 USB 设备,我需要使用的命令为:
$ sudo findmnt -rno TARGET /dev/sda1
-
第 4 步:复制系统到 USB 存储设备中
copyRootToUSB.sh
脚本会将 eMMC 整个系统的内容复制到 USB 存储设备上。当然,USB 存储器的存储空间应当比 eMMC 的存储空间大。
使用的命令如下:
usage: ./copyRootToUSB.sh [OPTIONS] -d | --directory Directory path to parent of kernel -v | --volume_label Label of Volume to lookup -p | --path Device Path to USB drive (e.g. /dev/sda1) -h | --help This message
一般来讲,对于常规的扩容需求,我们在参数
[OPTIONS]
中选择
-p
即可,后面需要添加 USB 设备的路径(如 /dev/sda1),这我们在第 3 步中已经得到。例如对于我的 USB 设备,我需要使用的完整命令为:
$ ./copyRootToUSB.sh -p /dev/sda1
此命令执行时间的长短取决于你的 eMMC 存储的文件大小。
-
第 5 步:备份及查询 USB 设备的 UUID
为了保证万无一失,我们需要查询一下 USB 设备的 UUID。
$ ./partUUID.sh
这个命令默认的操作路径为 sda1(/dev/sda1) ,但是您也可以确定其他 USB 设备的 UUID。规定 /dev/ 使用 -d 标志。例如:
$ ./diskUUID.sh -d sdb1
⚠️ 注意:
如果返回的 UUID 在格式和长度上不同于上面的示例,那么设备很可能没有格式化为 Ext4,请从第 2 步重新开始!
-
第 6 步:
修改引导配置以完成扩容
我们需要首先对引导配置文件进行备份。
$ sudo cp /boot/extlinux/extlinux.conf /boot/extlinux/extlinux.conf.bak
这一步的操作是 USB 设备扩容操作中最为重要也是最危险的一步。编辑 /boot/extlinux/extlinux.conf 文件和 /media/nvidia/boot/extlinux/extlinux.conf 文件,添加一个条目来指向新的 rootfs,指向的位置即为 USB 设备的路径,将它填入下方参数
<path>
中。路径信息已在第 3 步获得。
LABEL primary MENU LABEL primary kernel LINUX /boot/Image INITRD /boot/initrd APPEND ${cbootargs} quiet root=<path> rw rootwait rootfstype=ext4 console=ttyS0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0 sdhci_tegra.en_boot_part_access=1
对于我所使用的 USB 存储设备,修改完的 /boot/extlinux/extlinux.conf 和 /media/nvidia/boot/extlinux/extlinux.conf 文件内容如下:
TIMEOUT 30 DEFAULT primary MENU TITLE L4T boot options LABEL primary MENU LABEL primary kernel LINUX /boot/Image INITRD /boot/initrd APPEND ${cbootargs} quiet root=/dev/sda1 rw rootwait rootfstype=ext4 console=ttyS0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0 sdhci_tegra.en_boot_part_access=1 # APPEND ${cbootargs} quiet root=/dev/mmcblk0p1 rw rootwait rootfstype=ext4 console=ttyS0,115200n8 console=tty0 fbcon=map:0 net.ifnames=0 sdhci_tegra.en_boot_part_access=1 # When testing a custom kernel, it is recommended that you create a backup of # the original kernel and add a new entry to this file so that the device can # fallback to the original kernel. To do this: # # 1, Make a backup of the original kernel # sudo cp /boot/Image /boot/Image.backup # # 2, Copy your custom kernel into /boot/Image # # 3, Uncomment below menu setting lines for the original kernel # # 4, Reboot # LABEL backup # MENU LABEL backup kernel # LINUX /boot/Image.backup # INITRD /boot/initrd # APPEND ${cbootargs}
保存好文件,重新启动 NVIDIA Jetson,系统根目录将切换到 USB 存储设备,扩容完成。
通过串口控制台恢复系统备份
当您由于误操作,或者其他原因导致系统无法正常启动(常见的情况是开机一直循环重复出现英伟达的图标),那么您在扩容所做的备份将会起到重要的作用。我们理解您此刻焦急的心情,但请您保持耐心,遵循如下步骤让 NVIDIA Jetson 进入串口控制台,我们将操作 U-boot 以恢复您的备份。
材料准备
准备材料 |
描述 |
|
Ubuntu 主机 x1 |
|
无法进入系统的 NVIDIA Jetson x1 |
|
|
|
|
进入串口控制台步骤
-
第 1 步:将 UART 转 USB 模块连接到 NVIDIA Jetson
按照如下表格的接线指示,连接 NVIDIA Jetson 到 UART 转 USB 模块。
|
||
NVIDIA Jetson |
|
|
GND |
–> |
GND |
UART TXD |
–> |
RX |
UART RXD |
–> |
TX |
📢 小提示
- NVIDIA Jetson 和 UART 转 USB 模块的 VCC 接口不需要连接。
- 连接好线之后 NVIDIA Jetson 暂不需要通电,请先放在一旁准备好。
- 请拔掉扩容的外部存储器。
-
第 2 步:在 Ubuntu 主机上安装并启动 minicom
如果您的 Ubuntu 主机尚未安装 minicom,您可以通过如下命令将 minicom 安装到您的电脑上。
$ sudo apt-get install minicom
等待安装完成后,输入命令启动 minicom。
$ sudo minicom
-
第 3 步:准备配置 minicom
在 minicom 菜单栏中,我们打开串口并配置好串口,以便能够通过 minicom 获得 NVIDIA Jetson 的启动信息。在菜单栏中按下键盘 “
o
” 键进入配置界面。通过键盘上下方向键控制光标移动到 “
Serial port setup
”。
-
第 4 步:将
NVIDIA Jetson
连接到 Ubuntu 主机
此刻,我们新建一个命令行窗口,并在窗口中输入命令监控新设备的接入。
$ dmesg --follow
此时我们将 NVIDIA Jetson 上电,然后将连接好 NVIDIA Jetson 的 UART 转 USB 模块通过 USB 接口连接到 Ubuntu 主机。此时命令行窗口会展示新接入的设备名称,我们需要找到以 “
tty
” 开头的片段,把它记下来。
-
第 5 步:U-boot 操作
回到 minicom,将在第 4 步获得的设备名称填入“
Serial Device
”中。同时检查波特率是否配置为
115200
。
修改后,回车保存。选择 “Save setup as dfl” ,然后退出 minicom 界面。
重新输入命令
sudo minicom
,进入 minicom 之后,我们就可以在窗口中看到 NVIDIA Jetson 的启动信息。
我们可以通过返回的信息排查 NVIDIA Jetson 启动失败的原因,并通过使用命令
help
,来查看在 U-boot 系统下的所有可用命令。掌握这些命令的使用对解决问题是必要的,当然这也是困难的。
Tegra210 (P3450-0000) # help ? - alias for 'help' base - print or set address offset bdinfo - print Board Info structure blkcache - block cache diagnostics and control boot - boot default, i.e., run 'bootcmd' bootd - boot default, i.e., run 'bootcmd' bootefi - Boots an EFI payload from memory bootelf - Boot from an ELF image in memory booti - boot Linux kernel 'Image' format from memory bootm - boot application image from memory bootp - boot image via network using BOOTP/TFTP protocol bootvx - Boot vxWorks from an ELF image cmp - memory compare coninfo - print console devices and information cp - memory copy crc32 - checksum calculation dcache - enable or disable data cache dfu - Device Firmware Upgrade dhcp - boot image via network using DHCP/TFTP protocol dm - Driver model low level access echo - echo args to console editenv - edit environment variable enterrcm - reset Tegra and enter USB Recovery Mode env - environment handling commands exit - exit script ext2load - load binary file from a Ext2 filesystem ext2ls - list files in a directory (default /) ext4load - load binary file from a Ext4 filesystem ext4ls - list files in a directory (default /) ext4size - determine a file's size ext4write - create a file in the root directory false - do nothing, unsuccessfully fatinfo - print information about filesystem fatload - load binary file from a dos filesystem fatls - list files in a directory (default /) fatmkdir - create a directory fatrm - delete a file fatsize - determine a file's size fatwrite - write file into a dos filesystem fdt - flattened device tree utility commands fstype - Look up a filesystem type go - start application at address 'addr' gpio - query and control gpio pins gzwrite - unzip and write memory to block device help - print command description/usage i2c - I2C sub-system icache - enable or disable instruction cache imxtract - extract a part of a multi-image itest - return true/false on integer compare ln - Create a symbolic link load - load binary file from a filesystem loadb - load binary file over serial line (kermit mode) loads - load S-Record file over serial line loadx - load binary file over serial line (xmodem mode) loady - load binary file over serial line (ymodem mode) loop - infinite loop on address range ls - list files in a directory (default /) lzmadec - lzma uncompress a memory region md - memory display mii - MII utility commands mm - memory modify (auto-incrementing address) mmc - MMC sub system mmcinfo - display MMC info mw - memory write (fill) nm - memory modify (constant address) nvme - NVM Express sub-system part - disk partition related commands pci - list and access PCI Configuration Space ping - send ICMP ECHO_REQUEST to network host printenv - print environment variables pxe - commands to get and boot from pxe files reset - Perform RESET of the CPU run - run commands in an environment variable save - save file to a filesystem saveenv - save environment variables to persistent storage setenv - set environment variables sf - SPI flash sub-system showvar - print local hushshell variables size - determine a file's size sleep - delay execution for some time source - run script from memory sspi - SPI utility command sysboot - command to get and boot from syslinux files test - minimal test like /bin/sh tftpboot - boot image via network using TFTP protocol true - do nothing, successfully ums - Use the UMS [USB Mass Storage] unzip - unzip a memory region usb - USB sub-system usbboot - boot from USB device version - print monitor, compiler and linker version