TFTP加载内核设备树NFS挂载根文件系统演示
336.3.9 TFTP 加载内核、设备树与 NFS 挂载根文件系统演示
本节视频没有编写 C 程序、驱动源码或设备树源码,因此标题下不添加臆造的代码。课程演示的是一条适合开发阶段反复调试的启动链:
板载 eMMC 中的 U-Boot
│
├─ TFTP 下载 zImage 到内存
├─ TFTP 下载 DTB 到内存
└─ 启动 Linux,并传入网络与 NFS 根文件系统参数
│
└─ Linux 内核通过 NFS 把服务器目录挂载为 /
这里必须先分清两个动作:TFTP 是把内核和 DTB 文件下载进开发板内存,NFS 才是把服务器目录挂载为根文件系统。 视频里把三者笼统说成“远程挂载”,便于口头理解但技术上不严谨。
一、调试模式
本课程所说的“调试模式”,不是 JTAG 单步调试、KGDB 或 GDB 远程调试,而是开发阶段采用的网络加载/网络根文件系统启动方式。修改内核、设备树或根文件系统后,可以更新虚拟机里的文件,再让开发板重新启动或直接查看共享目录,避免每次都制作并烧写完整镜像。
这一方式主要解决以下问题:
- 完整烧写耗时较长,频繁擦写 eMMC 也没有必要;
- 内核和 DTB 经常变动时,只替换 TFTP 目录中的对应文件即可;
- 根文件系统位于开发主机,编辑文件、复制程序和查看结果更方便;
- 串口仍用于观察 U-Boot、内核和用户空间启动日志,网络用于传输启动文件和访问根文件系统。
但“免烧写”不等于开发板完全不依赖本地存储。本节右侧开发板仍先从板载 eMMC 启动 U-Boot;改为网络获取的是 Linux 内核、DTB 和根文件系统。若 U-Boot 本身损坏,或板卡启动开关没有进入可用的 U-Boot,后续 TFTP/NFS 流程仍无法进行。
还应先掌握常规烧写和本地启动。网络启动失败时,需要能够区分故障发生在 U-Boot、以太网链路、TFTP、内核参数、NFS 导出还是根文件系统用户空间,而不是把所有问题都归结为“网络不通”。
1.1 原理
本节对比了两块相同开发板的启动过程:左侧终端对应常规 eMMC 启动,右侧终端对应 TFTP + NFS 启动。两种方式的关键区别如下。
| 阶段 | 左侧:本地 eMMC 启动 | 右侧:网络调试启动 |
|---|---|---|
| U-Boot 来源 | 板载 eMMC | 板载 eMMC |
| Linux 内核来源 | 从 eMMC 读取到 RAM | 通过 TFTP 下载到 RAM |
| DTB 来源 | 从 eMMC 读取到 RAM | 通过 TFTP 下载到 RAM |
| 根文件系统 | Linux 从 eMMC 分区挂载 | Linux 通过 NFS 把服务器目录挂载为 / |
| 文件更新 | 通常要重新写入相应分区 | 替换服务器文件;内核/DTB 需重启后重新下载 |
常规启动也不应描述成“U-Boot 把 U-Boot、内核、设备树和根文件系统全部搬到内存”。更准确的理解是:
- SoC 的启动 ROM 先从选定介质装载前级引导代码,随后进入 U-Boot。
- U-Boot 把内核映像和 DTB 放到合适的内存地址,并把启动参数、DTB 地址等交给内核。
- 内核初始化存储或网络设备,然后挂载根文件系统。
- 普通 eMMC 根文件系统不会在启动前被整体复制进 RAM;NFS 根文件系统也不会被 U-Boot 整体下载进 RAM。文件内容是在运行时通过相应文件系统按需访问的。
U-Boot 官方文档说明,tftpboot 属于通过 TFTP 加载映像的网络启动命令,ipaddr 是开发板地址,serverip 是 TFTP 服务器地址;内核文件名和 DTB 文件名常分别关联 bootfile、fdtfile,装载地址则依具体板级启动脚本而定。参阅 U-Boot 环境变量官方说明。
1.1.1 实验流程
实验的完整数据路径不是“电脑把整个系统一次性推给板子”,而是两个阶段:
U-Boot 阶段:开发板作为 TFTP 客户端
开发板 ──请求 zImage、DTB──> Ubuntu 虚拟机中的 TFTP 服务
开发板 <──文件数据──────── Ubuntu 虚拟机
Linux 阶段:内核作为 NFS 客户端
开发板 Linux ──挂载并访问──> Ubuntu 虚拟机导出的根文件系统目录
因此,串口上连续出现的 # 是 U-Boot 的 TFTP 下载进度显示,只能证明正在传输某个文件;它不是 NFS 的“挂载符号”,也不是 Linux shell 的 root 提示符。启动完成后单独出现的 # 才通常是 root 用户所用的 shell 提示符。
1.1.1.1 实验操作
视频使用两个串口终端同时观察两块开发板:
- 左侧终端:开发板按常规方式从 eMMC 读取内核和 DTB,并使用本地根文件系统;
- 右侧终端:开发板的 U-Boot 通过板卡第二个以太网口联网,从虚拟机下载内核和 DTB,Linux 再使用 NFS 根文件系统;
- 手机画面中,下方开发板的
ETHERNET2接着黄色网线,对应右侧串口终端; - 两块板同时上电只是为了对比日志,不是网络启动必须同时使用两块板。
右侧板所需的基本条件包括:
- 开发板和运行 TFTP/NFS 服务的 Ubuntu 虚拟机网络可达;
- U-Boot 能使用正确的以太网控制器,本视频日志显示为
Using FEC1 device; - TFTP 服务目录中存在文件名完全匹配的
zImage和topeet_emmc_hdmi.dtb; - U-Boot 的客户端 IP、服务器 IP、文件名、装载地址和启动命令正确;
- 内核已内建所需网卡驱动、IP 自动配置和 NFS 根文件系统支持;若关键驱动只放在尚未挂载的根文件系统模块中,会形成“要先挂载才能加载驱动、要先有驱动才能挂载”的循环依赖;
- 内核命令行指向正确的 NFS 服务器和导出目录,服务器权限允许这块开发板访问。
本节只演示已经搭好的环境。安装服务、配置 /etc/exports、设置 U-Boot 环境变量等是后续课程内容,不能仅凭本节画面把预告命令当成已经执行并验证的完整配置步骤。
1.1.1.2 实验过程
两块开发板上电后,都先进入 U-Boot,再启动 Linux,但文件来源不同。
左侧终端的关键日志为:
reading zImage
7186760 bytes read in 181 ms (37.9 MiB/s)
Booting from mmc ...
reading topeet_emmc_hdmi.dtb
40551 bytes read in 17 ms (2.3 MiB/s)
Kernel image @ 0x80800000 [ 0x000000 - 0x6da948 ]
## Flattened Device Tree blob at 83000000
Booting using the fdt blob at 0x83000000
Using Device Tree in place at 83000000, end 8300ce66
Starting kernel ...
这些行表明内核和 DTB 从 MMC/eMMC 文件系统读入内存。日志中的 MiB/s 是二进制兆字节每秒,不是 Mb/s(兆比特每秒)。画面里还出现 Unable to read file boot.scr,但随后成功读取了 zImage 和 DTB,所以该提示在本次演示中触发了后续回退启动路径,并不等于启动已经失败。
右侧终端先下载 zImage:
Using FEC1 device
TFTP from server 192.168.2.220; our IP address is 192.168.2.120
Filename 'zImage'.
Load address: 0x80800000
Loading: ##################################################
2.4 MiB/s
done
Bytes transferred = 7186576 (6da890 hex)
随后再次使用 TFTP 下载 DTB:
Using FEC1 device
TFTP from server 192.168.2.220; our IP address is 192.168.2.120
Filename 'topeet_emmc_hdmi.dtb'.
Load address: 0x83800000
Loading: ###
3 MiB/s
done
Bytes transferred = 40611 (9ea3 hex)
Kernel image @ 0x80800000 [ 0x000000 - 0x6da890 ]
## Flattened Device Tree blob at 83800000
Booting using the fdt blob at 0x83800000
Using Device Tree in place at 83800000, end 8380cea2
Starting kernel ...
由日志可直接确认:
- TFTP 服务器为
192.168.2.220,开发板为192.168.2.120; - 内核装载到
0x80800000,DTB 装载到0x83800000; - 两次
Loading: ###...分别属于两个文件的传输; done和Bytes transferred说明相应传输完成;- DTB 的装载地址是这套板级启动配置的选择,不是所有开发板通用的固定地址;任意修改地址都可能与内核、U-Boot 或其他保留内存发生重叠。
进入 Linux 后,右侧终端又出现以下关键日志:
IP-Config: Complete:
device=eth0, ipaddr=192.168.2.120, mask=255.255.255.0, gw=192.168.2.1
bootserver=192.168.2.220, rootserver=192.168.2.220, rootpath=
VFS: Mounted root (nfs filesystem) on device 0:14.
devtmpfs: mounted
真正证明网络根文件系统已经生效的是 VFS: Mounted root (nfs filesystem),不是前面的 #。画面中的 rootpath= 一行没有显示出可辨认路径,所以不能仅凭这段日志反推服务器目录;视频随后在虚拟机中实际打开的是 ~/nfs,这才是本演示可见的目录证据。
Linux 内核官方文档规定,NFS 根启动通常通过下列内核参数描述:
root=/dev/nfs
nfsroot=<服务器IP>:<服务器导出目录>[,<NFS选项>]
ip=<客户端IP>:<服务器IP>:<网关>:<掩码>:<主机名>:<网卡>:<自动配置方式>
其中 root=/dev/nfs 是告诉内核使用 NFS 根的特殊标记,并不代表存在一块真实的 /dev/nfs 块设备;nfsroot= 指定服务器和目录;ip= 负责启动早期的网卡地址配置。具体字段和内核配置要求见 Linux 官方 NFS 根文件系统文档。本节视频没有展示完整 bootargs,因此笔记只说明参数职责,不臆造本板实际保存的参数字符串。
启动完成后:
- 左侧本地根文件系统直接进入
root@itop-imx6ull:~#; - 右侧网络根文件系统显示
iTOP_imx6ul/ull login:,视频使用用户名root和课程镜像设置的密码topeet登录;密码输入时终端不回显字符是正常安全行为; - 是否自动登录、主机名是什么、是否要求密码,取决于根文件系统中的用户、终端和启动服务配置,并不是 eMMC 与 NFS 协议本身强制规定的差异。
1.1.1.3 实验结果
视频进入名为 Ubuntu_test 的虚拟机,展示了两个服务端目录:
~/tftpfile/
├── zImage
└── topeet_emmc_hdmi.dtb
~/nfs/
├── bin/
├── dev/
├── etc/
├── lib/
├── linuxrc
├── mnt/
├── proc/
├── root/
├── sbin/
├── sys/
├── tmp/
├── usr/
└── var/
tftpfile 是 TFTP 服务器允许读取的目录,本次只放内核和 DTB;nfs 的内容具有完整 Linux 根文件系统结构,并通过 NFS 导出给开发板。两者恰好在同一台虚拟机上,但属于不同服务,目录也不能互换。
教师先把 mkdir 误输为 mkdie,终端因此提示命令不存在,随后执行了正确命令:
cd ~/nfs
mkdir wfeng
虚拟机中创建 wfeng 后,在已经启动的右侧开发板根目录 / 执行 ls,能够看到同名目录。这说明开发板的 / 与虚拟机导出的目录是同一套 NFS 文件视图,而不是启动时复制出来的一份静态快照。
更新不同内容时,生效方式也不同:
| 修改对象 | 放置位置 | 何时生效 | 注意事项 |
|---|---|---|---|
zImage | TFTP 目录 | 开发板重启并重新下载后 | 文件名、架构、DTB 与模块版本要匹配 |
.dtb | TFTP 目录 | 开发板重启并重新下载后 | 错误硬件描述可能导致串口、网卡或根挂载失败 |
| 根文件系统普通文件 | NFS 导出目录 | 通常可立即在开发板上看到 | 缓存、正在运行的进程或服务可能需要刷新/重启 |
| 用户程序 | NFS 导出目录 | 复制完成后可运行新文件 | 不要覆盖正在执行的二进制;先写临时文件再原子替换更稳妥 |
网络根文件系统便于调试,但也把虚拟机变成启动链的关键组成部分。暂停虚拟机、拔掉网线、修改导出权限或删除关键根文件,都可能让开发板启动失败或运行中的文件访问长时间等待。它适合可信的开发网络,不应未经安全设计直接暴露到不可信网络。
本节演示最终验证了以下结论:
- 板载 U-Boot 能通过 FEC1/第二网口访问
192.168.2.220; zImage与topeet_emmc_hdmi.dtb都通过 TFTP 成功进入指定内存地址;- 内核成功启动,并把 NFS 文件系统挂载为根目录;
- 在虚拟机
~/nfs新建目录后,开发板根目录立即可见,证明共享关系成立; - 该流程避免反复烧写,但内核或 DTB 更新后仍需重启,且 U-Boot 本身仍来自本地 eMMC。
1.2 TFTP
TFTP(Trivial File Transfer Protocol,简单文件传输协议)在本节只承担文件下载任务:U-Boot 按文件名从服务器读取 zImage 和 DTB,并把收到的字节放入指定 RAM 地址。TFTP 不理解什么是 Linux 内核或设备树,也不会把目录挂载到 Linux 的 /。
RFC 1350 规定 TFTP 运行在 UDP 之上,读/写请求最初发往服务器 UDP 69 端口,服务器随后选用自己的传输标识符(对应 UDP 端口)继续会话。因此,排查防火墙时不能只机械地认为所有数据始终经过 69 端口;69 主要用于初始请求。详见 RFC 1350:TFTP Protocol。
本节网络启动可按下列职责理解:
| 项目 | 作用 | 本视频中的值 |
|---|---|---|
| 开发板 IP | 标识 TFTP/NFS 客户端 | 192.168.2.120 |
| 服务器 IP | 找到虚拟机中的 TFTP/NFS 服务 | 192.168.2.220 |
| TFTP 文件名 | 服务器侧查找目标文件 | zImage、topeet_emmc_hdmi.dtb |
| 装载地址 | 文件在开发板 RAM 中的放置位置 | 0x80800000、0x83800000 |
| NFS 导出目录 | Linux 启动后作为根文件系统 | 视频可见为虚拟机 ~/nfs;实际导出路径须查服务器配置 |
“IP 正确”至少意味着客户端地址和服务器地址不冲突、掩码允许双方互达、虚拟机网卡确实接入该板卡网络,并且 Windows/Ubuntu 防火墙与服务权限允许相应流量。若两端位于同一子网,TFTP/NFS 本身不要求互联网连接;课程保留另一张网卡或 Wi-Fi,主要是为了虚拟机还能上网下载软件和资料。
1.2.1 TFTP 定义
TFTP 是为简单读写文件设计的轻量协议。根据 RFC 1350:
- 它建立在 UDP 数据报之上,不是 TCP 协议;说“属于 TCP/IP 协议族”可以,说“基于 TCP”则错误;
- 经典协议用读请求(RRQ)、写请求(WRQ)、数据(DATA)、确认(ACK)和错误(ERROR)报文完成传输;
- 经典数据块逐块确认,超时后可以重传;U-Boot 也可能协商 RFC 扩展选项来提高效率;
- 它只能进行简单文件读写,不能像常规 FTP 客户端那样列目录;
- 标准 TFTP 没有用户认证机制,也不提供加密和服务器身份校验。
所以 TFTP 的优势是实现简单、引导程序支持广;代价是功能和安全性有限。课程场景应把服务限制在隔离或可信的开发网段,只开放需要读取的目录,不要把可写 TFTP 根目录直接暴露到公共网络。
U-Boot 的下载成功判断可依次看:
Using FEC1 device:选中了预期网卡;TFTP from server ...:开始向指定服务器请求;Filename ...与Load address ...:文件名和内存地址符合预期;Loading: ###...:传输正在进行;done、Bytes transferred ...:传输完整结束。
若停在 T、重试或超时,应按“物理链路 → IP/掩码 → 虚拟机网卡模式 → 主机和虚拟机防火墙 → TFTP 服务状态 → 服务根目录与文件权限 → 文件名大小写”的顺序排查。Linux 文件名区分大小写,zImage 与 zimage 不是同一个文件。
1.2.2 FTP 定义
FTP(File Transfer Protocol)与 TFTP 都能传文件,但不能仅因名字相近就视为同一种协议。RFC 959 描述的 FTP 使用 TCP 控制连接,标准控制端口是 21,并另建数据连接;它支持登录、目录与多种文件操作。参阅 RFC 959:File Transfer Protocol。
| 比较项 | TFTP | FTP |
|---|---|---|
| 传输层 | UDP | TCP |
| 初始/控制端口 | 初始请求 UDP 69 | 控制连接 TCP 21 |
| 连接结构 | 简单请求与数据/确认报文 | 控制连接与独立数据连接 |
| 用户认证 | 标准协议没有 | 支持用户名、密码等登录过程 |
| 目录浏览 | 不支持 | 支持相关目录和文件操作 |
| 典型嵌入式用途 | U-Boot/PXE 等早期启动阶段下载小量启动文件 | 日常交互式文件管理与传输 |
| 本节是否使用 | 是 | 否 |
视频网页上出现 FTP 相关链接只是用于概念对比,不代表实验用 FTP 下载内核。FileZilla 等图形客户端还可能支持 FTP、FTPS 或 SFTP;其中 SFTP 是基于 SSH 的文件传输协议,也不等于 FTP 或 TFTP。实际配置时必须先确认服务器启用的协议,不能只凭客户端软件名称判断。
1.2.3 网络文件系统定义
NFS(Network File System,网络文件系统)允许客户端通过网络访问服务器导出的目录,并以文件系统形式挂载。本节中,Linux 内核把 NFS 服务器目录直接挂载为根目录 /,之后的 /bin、/etc、/lib、/root 等都来自这套网络根文件系统。
需要严格区分:
- TFTP 传输的是两个普通文件,传完以后内核和 DTB 位于开发板 RAM;
- NFS 提供的是一个持续使用的远程文件系统,开发板运行期间仍依赖网络和服务器;
~/nfs只是视频里服务器端用于存放根文件系统的目录名,并不是 NFS 协议规定的固定路径;- 服务器还必须在
/etc/exports等配置中导出实际绝对路径。仅仅创建一个名为nfs的目录不会自动提供 NFS 服务; - 客户端把这个导出目录挂为
/后,服务端目录里对系统关键文件的修改会直接影响正在运行的开发板。
NFS 根启动要求网卡驱动、IP 配置支持、NFS 客户端和根文件系统支持在挂载根目录之前就可用,通常需要把这些能力直接编进内核。Linux 官方文档明确说明:引导程序先把内核装入内存,内核再根据 root=/dev/nfs、nfsroot= 和 ip= 等参数完成网络配置与根挂载。因此,“U-Boot 挂载 NFS 根文件系统”不是本实验日志所反映的流程。
判断 NFS 根是否成功,优先看以下证据:
IP-Config: Complete
VFS: Mounted root (nfs filesystem) ...
若 TFTP 已成功但内核在根挂载处停住,应检查内核命令行、内核是否内建网卡/NFS 支持、网卡设备名、服务器导出路径、客户端访问范围、目录权限、防火墙以及 NFS 版本/挂载选项。TFTP 成功只说明 U-Boot 阶段的网络与 TFTP 服务可用,不能自动证明 Linux 阶段的网卡驱动和 NFS 配置正确。
1.3 硬件环境要求
视频所用 i.MX6ULL 开发板应把直连电脑的网线插到板上标注为 ETHERNET2 的第二网口;串口日志相应显示 FEC1。这是这块课程板卡及其当前 U-Boot/设备树配置的要求,不能推广成所有开发板的“网络启动永远用网口二”。换板卡、换 DTB 或换 U-Boot 配置后,应查原理图、丝印和对应板级文档确认 PHY 与控制器映射。
典型物理连接为:
开发板 ETHERNET2 ── 网线 ── 电脑物理网卡/USB 转以太网适配器
│
└─ 桥接或以其他合适方式交给 Ubuntu 虚拟机
电脑 Wi-Fi/另一张网卡 ── 局域网或互联网(可选,用于宿主机和虚拟机上网)
硬件和网络准备要点:
- 电脑有空闲 RJ45 网口时,可以直接连接开发板;现代千兆网卡一般支持自动 MDI/MDIX,无需专门交叉线;
- 笔记本没有网口,或唯一网口已被占用时,可增加 USB 转以太网适配器。应选择操作系统和虚拟机可识别、驱动稳定的型号,不必照搬视频中的商品和价格;
- 网线插好后检查两端 Link/Activity 指示灯;灯不亮时先排查供电、网线、端口、PHY 和复位,而不是先修改 TFTP 软件;
- 虚拟机中的板卡侧网卡应能直达开发板网段。课程后续采用双网卡:一张连接开发板,一张用于上网;本节只是预告,具体模式和配置以后续实操为准;
192.168.2.120和192.168.2.220是本演示地址,不是协议固定值。复现实验时可以使用其他私有网段,但 U-Boot、Linux 启动参数和服务器配置必须一致;- 直连同一网段通常不需要默认网关即可互访。若服务器和开发板跨网段,才必须确保网关与路由实际可达;
- 串口线仍必须连接,才能中断自动启动、查看 U-Boot 环境和定位早期失败。网线不能替代调试串口。
电气和可靠性方面还要注意:
- 以太网口通过磁性器件连接,不要把普通 GPIO、电源针脚或串口排针误当作网络接口;按板卡手册给开发板供电;
- 多个 USB 设备、串口模块和 USB 网卡同时使用时,避免由供电不足或 USB Hub 不稳定造成随机掉线;
- 修改 U-Boot 环境前先用
printenv保存原值。仅执行setenv/env set通常只改内存副本,执行saveenv/env save才会写入持久化存储;错误的bootcmd或网络参数可能导致下次自动启动失败; - TFTP 下载地址和 DTB 地址必须使用板级启动脚本已经验证的值,不应凭教程中的地址套用到其他内存布局。
在进入下一课配置前,可用以下清单核对环境,但本节不要提前执行或猜测未展示的完整命令:
- 串口能看到 U-Boot 提示符,且已备份当前环境变量;
- 第二网口/FEC1 链路灯正常;
- 虚拟机板卡侧网卡与开发板位于可互达网段;
- 虚拟机中的 TFTP 与 NFS 服务已安装、启动并限制在开发网络;
- TFTP 根目录包含名称正确的
zImage和topeet_emmc_hdmi.dtb; - NFS 导出目录确实包含可启动的根文件系统,而不只是空文件夹;
- 内核已内建早期网络、板载网卡和 NFS 根所需支持;
- U-Boot 启动参数中的服务器地址、客户端地址、文件名、装载地址和 NFS 路径彼此一致。
完成这些条件后,启动链才是“本地 U-Boot → TFTP 下载内核与 DTB → Linux 挂载 NFS 根文件系统”。任何一步失败,都应根据最后一条有效日志定位到对应阶段,而不是反复盲目烧写系统。