TFTP加载内核设备树NFS挂载根文件系统演示

Tutorial: 卓越工程师 Category: 嵌入式 Published: 2026-10-23 11:05:29 Views: 1 Likes: 0 Comments: 0

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、内核、设备树和根文件系统全部搬到内存”。更准确的理解是:

  1. SoC 的启动 ROM 先从选定介质装载前级引导代码,随后进入 U-Boot。
  2. U-Boot 把内核映像和 DTB 放到合适的内存地址,并把启动参数、DTB 地址等交给内核。
  3. 内核初始化存储或网络设备,然后挂载根文件系统。
  4. 普通 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 接着黄色网线,对应右侧串口终端;
  • 两块板同时上电只是为了对比日志,不是网络启动必须同时使用两块板。

右侧板所需的基本条件包括:

  1. 开发板和运行 TFTP/NFS 服务的 Ubuntu 虚拟机网络可达;
  2. U-Boot 能使用正确的以太网控制器,本视频日志显示为 Using FEC1 device;
  3. TFTP 服务目录中存在文件名完全匹配的 zImage 和 topeet_emmc_hdmi.dtb;
  4. U-Boot 的客户端 IP、服务器 IP、文件名、装载地址和启动命令正确;
  5. 内核已内建所需网卡驱动、IP 自动配置和 NFS 根文件系统支持;若关键驱动只放在尚未挂载的根文件系统模块中,会形成“要先挂载才能加载驱动、要先有驱动才能挂载”的循环依赖;
  6. 内核命令行指向正确的 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 文件视图,而不是启动时复制出来的一份静态快照。

更新不同内容时,生效方式也不同:

修改对象放置位置何时生效注意事项
zImageTFTP 目录开发板重启并重新下载后文件名、架构、DTB 与模块版本要匹配
.dtbTFTP 目录开发板重启并重新下载后错误硬件描述可能导致串口、网卡或根挂载失败
根文件系统普通文件NFS 导出目录通常可立即在开发板上看到缓存、正在运行的进程或服务可能需要刷新/重启
用户程序NFS 导出目录复制完成后可运行新文件不要覆盖正在执行的二进制;先写临时文件再原子替换更稳妥

网络根文件系统便于调试,但也把虚拟机变成启动链的关键组成部分。暂停虚拟机、拔掉网线、修改导出权限或删除关键根文件,都可能让开发板启动失败或运行中的文件访问长时间等待。它适合可信的开发网络,不应未经安全设计直接暴露到不可信网络。

本节演示最终验证了以下结论:

  1. 板载 U-Boot 能通过 FEC1/第二网口访问 192.168.2.220;
  2. zImage 与 topeet_emmc_hdmi.dtb 都通过 TFTP 成功进入指定内存地址;
  3. 内核成功启动,并把 NFS 文件系统挂载为根目录;
  4. 在虚拟机 ~/nfs 新建目录后,开发板根目录立即可见,证明共享关系成立;
  5. 该流程避免反复烧写,但内核或 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 的下载成功判断可依次看:

  1. Using FEC1 device:选中了预期网卡;
  2. TFTP from server ...:开始向指定服务器请求;
  3. Filename ... 与 Load address ...:文件名和内存地址符合预期;
  4. Loading: ###...:传输正在进行;
  5. 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。

比较项TFTPFTP
传输层UDPTCP
初始/控制端口初始请求 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 根文件系统”。任何一步失败,都应根据最后一条有效日志定位到对应阶段,而不是反复盲目烧写系统。