TFTPNFS部署

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

337.3.10 TFTP 与 NFS 部署

本节没有编写 C 程序、驱动源码或设备树源码,因此标题下不添加臆造的 C 代码。视频实际操作的是 Ubuntu 虚拟机中的两个服务:

U-Boot 阶段:开发板 ──TFTP──> 虚拟机的 TFTP 目录(下载 zImage 和 DTB)
Linux 阶段:开发板 ──NFS ──> 虚拟机导出的根文件系统目录(挂载为 /)

服务端软件安装完成,只代表 Ubuntu 具备提供服务的能力;要让开发板真正启动,还必须继续完成网卡、IP、U-Boot 环境变量、内核启动参数以及根文件系统内容的配置。视频末尾也明确说明下一步才设置双网卡,因此本节不能被理解成“网络启动的全部工作已经完成”。

一、TFTP 和 NFS 的环境搭建

本课中的 Ubuntu 虚拟机是服务端,开发板是客户端。TFTP 和 NFS 虽然运行在同一台虚拟机上,但职责完全不同:

项目TFTPNFS
本课软件包tftp-hpa、tftpd-hpanfs-kernel-server、rpcbind
服务端目录视频为 /home/wfeng/tftpfile视频为 /home/wfeng/nfs
开发板访问阶段U-Boot 阶段Linux 内核及系统运行阶段
访问对象单个文件,如 zImage、DTB一整套目录树,即根文件系统
是否等同于“挂载”否,TFTP 是文件传输是,Linux 将导出目录挂载为文件系统

视频终端中的软件版本包括 tftpd-hpa 5.2+20150808-1ubuntu4、nfs-kernel-server 1:1.3.4-2.5ubuntu3.7 和 systemd 245.4-4ubuntu3.23,说明演示环境属于 Ubuntu 20.04(Focal)这一代。新版本 Ubuntu 的服务管理或默认配置可能变化,复现时应先查看本机版本:

# 查看 Ubuntu 发行版信息;不要只凭桌面外观判断版本。
lsb_release -a

# 查看两个服务端软件包是否已经安装以及具体版本。
dpkg -l tftpd-hpa nfs-kernel-server rpcbind

本课通过 MobaXterm 的 SSH 窗口操作虚拟机;SSH 连接地址在画面中是 192.168.31.230,它只是当时用于管理虚拟机的地址,不等于上一课开发板网络中的 TFTP/NFS 服务地址 192.168.2.220。双网卡环境中,一张网卡可以处于管理/上网网段,另一张处于开发板直连网段,同一台虚拟机拥有多个 IP 很正常。

1.1 TFTP 搭建

TFTP 部署的目标是让开发板 U-Boot 作为客户端,从 Ubuntu 虚拟机的指定目录下载内核和设备树。视频把步骤概括为:创建目录、设置权限、安装软件、修改配置、重启服务。

这里安装的两个包作用不同:

  • tftpd-hpa 中的字母 d 表示 daemon,提供 TFTP 服务端;这是开发板下载文件所必需的部分。
  • tftp-hpa 提供 TFTP 客户端,便于在 Ubuntu 本机或其他 Linux 主机测试服务;它不是服务端进程本身。

TFTP 没有用户登录和传输加密,应只在隔离、可信的开发网络中使用。服务器目录中不要放密码、私钥或无关文件。

1.1.1 配置 TFTP
1.1.1.1 创建服务目录

视频以用户 wfeng 登录,先回到主目录,再创建 tftpfile:

# 回到当前登录用户的主目录;视频中展开后是 /home/wfeng。
cd ~

# 创建用于存放内核和 DTB 的目录;mkdir 才是创建目录的命令。
mkdir tftpfile

# 列出目录,确认 tftpfile 已经存在;ls 只负责查看,不会创建文件或目录。
ls

目录名可以调整,但后面的 TFTP_DIRECTORY 必须写成同一个目录的绝对路径。~ 依赖当前用户和 shell 展开,系统服务配置中应写 /home/用户名/tftpfile 或其他明确路径。

视频随后执行:

# 这是视频原操作:让所有本机用户都拥有读、写、进入该目录的权限。
sudo chmod 777 tftpfile

777 不是笼统的“最高权限”,而是三组八进制权限:

  • 第一个 7 = 4 + 2 + 1:目录所有者可读、可写、可进入;
  • 第二个 7:同组用户拥有相同权限;
  • 第三个 7:其他所有本机用户也拥有相同权限;
  • 对目录而言,执行位 x 表示可以进入或穿过目录,不是“执行这个文件夹”。

给开发目录 777 是课程为了避免权限问题采用的宽松做法,不是 TFTP 下载的必要条件。开发板只执行下载(RRQ)时,TFTP 服务用户只需要能穿过父目录和读取目标文件;更合理的典型权限是目录 755、文件 644:

# 允许所有用户进入并读取目录清单,只有目录所有者可以增删文件。
chmod 755 "$HOME/tftpfile"

# zImage 和 DTB 放入目录后,使其可被 tftp 服务用户读取。
chmod 644 "$HOME/tftpfile/zImage" "$HOME/tftpfile/topeet_emmc_hdmi.dtb"

# 逐级检查 /home、用户主目录和 tftpfile 是否允许 tftp 用户穿过。
namei -l "$HOME/tftpfile/zImage"

若用户主目录权限为 700 或 750,即使 tftpfile 本身是 755,服务用户仍可能无法穿过父目录。可以有针对性地调整访问控制,或把服务目录放到 /srv/tftp、/var/lib/tftpboot 等不受个人主目录权限影响的位置,不应为省事把整个主目录改成 777。

1.1.1.2 安装服务端与客户端

视频安装命令为:

# 更新软件包索引;视频画面未执行,但新建环境通常应先更新索引。
sudo apt-get update

# 安装测试客户端 tftp-hpa 和服务端 tftpd-hpa。
sudo apt-get install tftp-hpa tftpd-hpa

安装输出中出现:

tftpd user (tftp) already exists, doing nothing.
tftpd-hpa directory (/home/wfeng/tftpfile) already exists, doing nothing.

这说明当前虚拟机曾配置过 tftp 用户和服务目录,安装程序只是发现它们已存在;不能据此认为一台全新虚拟机也会自动采用 /home/wfeng/tftpfile。用户名、目录和配置必须按自己的实际环境检查。

1.1.1.3 修改 /etc/default/tftpd-hpa

视频使用 Vim 打开配置文件:

# 以管理员权限编辑 tftpd-hpa 的发行版默认参数文件。
sudo vim /etc/default/tftpd-hpa

刚打开时,视频中的文件内容为:

# /etc/default/tftpd-hpa

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/home/wfeng/tftpfile"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure"

四个配置项的含义是:

配置项含义注意事项
TFTP_USERNAME服务端降低权限后使用的本地账户通常由软件包创建为 tftp,不要改成普通登录用户来规避权限问题
TFTP_DIRECTORY允许客户端访问的服务端根目录必须是实际存在的绝对路径;视频路径只适用于用户 wfeng
TFTP_ADDRESS监听地址和端口:69 表示在可用地址上监听标准 TFTP 初始请求端口 UDP 69
TFTP_OPTIONS传给 in.tftpd 的额外选项每个选项必须按命令行规则书写,不能随意拼接连字符

课件截图把最后一项写为:

TFTP_OPTIONS="-l-c-s"

视频实际编辑时还短暂出现过 -l-c--s,画面叠字指出“这里我多打了一个 -”。即使去掉多出的一个连字符,-l-c-s 也不是规范的短选项写法。三个独立选项应写为 -l -c -s,或者按短选项规则合并为 -lcs;不能在同一个参数内部重复插入选项前缀。

三个选项的真实作用是:

  • -l / --listen:让 in.tftpd 以独立监听模式运行;Ubuntu 的服务启动脚本通常已经负责传入监听参数,手工重复指定没有必要。
  • -c / --create:允许远端客户端上传并新建文件;它与 U-Boot 下载已有 zImage、DTB 无关。
  • -s / --secure:启动后把指定目录作为受限根目录,客户端只需请求根目录下的相对文件名。

对本课“开发板只下载已有文件”的用途,保留发行版原有配置更清楚:

TFTP_OPTIONS="--secure"

只有确实要让客户端上传并新建文件时,才考虑:

TFTP_OPTIONS="--secure --create"

同时还要给 tftp 服务用户最小必要写权限,并限制可访问网络。--create 不会修复普通文件权限问题,反而会扩大未经认证的写入面。Debian 的 tftpd-hpa 维护说明也明确指出,只有需要上传不存在的新文件时才添加 --create;各选项的定义见 Debian 官方 in.tftpd(8) 手册和 tftpd-hpa 软件包说明。

在 Vim 中修改后,先按 Esc 退出插入模式,再输入 :wq 并回车,表示保存并退出。wq 是 Vim 命令,不是 Linux shell 命令。

1.1.1.4 重启并验证 TFTP

视频使用兼容命令重启服务:

# 视频原命令;在 Ubuntu 20.04 中最终由 systemd 管理服务。
sudo service tftpd-hpa restart

命令立即回到提示符且没有输出,不足以证明配置正确。尤其是选项拼写错误时,必须主动检查状态和日志:

# 重启服务;现代 Ubuntu 推荐直接使用 systemctl。
sudo systemctl restart tftpd-hpa

# 查看服务是否处于 active (running);--no-pager 防止进入翻页器。
sudo systemctl status tftpd-hpa --no-pager

# 若启动失败,查看最近 30 行该服务日志。
sudo journalctl -u tftpd-hpa -n 30 --no-pager

# 确认有进程监听 UDP 69;-l 显示监听,-u 只看 UDP,-n 不解析名称,-p 显示进程。
sudo ss -lunp | grep ':69'

把 zImage 和 DTB 放入 TFTP_DIRECTORY 后,还应从客户端实际下载一次。若同机测试成功而 U-Boot 失败,继续检查虚拟机板卡侧网卡、开发板和服务器 IP、文件名大小写、防火墙以及 U-Boot 选择的以太网口。TFTP 初始请求使用 UDP 69,后续数据会话使用服务器选出的传输端口,不能简单地把它当成只存在一条 TCP 69 连接。

1.2 NFS 搭建

NFS 服务端把 Ubuntu 中的一个目录导出给网络客户端。本课程后续会让 Linux 内核把这个目录挂载为根目录 /,所以该目录最终必须包含 /bin、/etc、/lib、/sbin、/usr、linuxrc 或 init 等完整根文件系统内容。创建一个空的 nfs 目录只完成了容器准备,空目录本身不能启动 Linux 用户空间。

1.2.1 安装应用

视频安装命令为:

# 安装内核态 NFS 服务端配套工具和 RPC 端口映射服务。
sudo apt-get install nfs-kernel-server rpcbind
  • nfs-kernel-server 提供 NFS 服务端管理工具、导出表处理及相关 systemd 服务;实际文件服务由 Linux 内核的 NFS server 支持完成。
  • rpcbind 维护 RPC 程序号与网络端口的映射,NFSv3 和 mountd 等服务通常会用到。NFSv4 对它的依赖方式不同,但本课程的旧内核/根文件系统环境保留该包更有兼容性。

安装过程中,画面曾出现:

Job for nfs-server.service canceled.
Creating config file /etc/exports with new version

软件包仍继续完成安装。安装时服务尚没有有效导出项,或者依赖服务正在重新配置,都可能使首次自动启动没有完成;因此不能只看安装命令返回,还要在写好 /etc/exports 后重新加载导出表并检查服务状态。

Ubuntu 官方文档给出的服务端必需包是 nfs-kernel-server,并说明导出目录应写入 /etc/exports。不同 Ubuntu 版本的 NFS 配置文件和 systemd 单元可能不同,详见 Ubuntu Server 官方 NFS 文档。

1.2.2 创建 NFS 文件(实际是目录)

原笔记把这一节误写成“使用 ls 创建 NFS 文件”和“使用 make 命令”。视频实际执行的是:

# 回到当前用户主目录,避免把 nfs 建到不确定的当前路径。
cd ~

# 创建名为 nfs 的目录;命令是 mkdir,不是 make、make r 或 ls。
mkdir nfs

# 查看主目录,确认 nfs 目录已出现。
ls

nfs 在这里是目录名,不是一个名为 nfs 的普通文件,也不是 NFS 服务的配置文件。目录叫什么可以自行决定,关键是 /etc/exports 和后续 U-Boot 的 nfsroot= 参数必须指向同一个实际绝对路径。

正式作为网络根文件系统前,还需把已经制作好的根文件系统完整复制或解包到这里,并保留所有者、权限、符号链接和特殊文件。随意使用普通图形复制可能丢失这些元数据;本节没有演示根文件系统部署命令,因此不能凭空补写某个压缩包名称或复制流程。

1.2.3 修改文件

视频编辑的是 NFS 导出表文件,而不是“打开文件夹”:

# 以管理员权限编辑 NFS 服务端导出规则。
sudo vim /etc/exports

视频要求在文件末尾新增且不要以 # 开头:

/home/wfeng/nfs *(rw,sync,no_root_squash)

这行的语法是“服务端目录 + 客户端范围 + 导出选项”:

字段含义风险或限制
/home/wfeng/nfs服务端要导出的绝对目录必须按自己的用户名和实际目录修改
*允许任意可到达服务器的客户端申请挂载范围过宽,只适合隔离实验网,不应直接用于办公网或公网
rw允许客户端读写开发板 rootfs 需要写 /var、/tmp 等目录时通常需要
sync数据提交到稳定存储后再回复写请求更重视一致性,性能可能低于 async
no_root_squash不把客户端 UID 0 映射为匿名用户客户端 root 可按服务器 root 身份改动导出目录,风险很高

# 在 /etc/exports 中表示注释起始符。若在配置行前加 #,这一整行不会生效。客户端说明与圆括号之间不能留空格;官方 exports(5) 的语法要求选项列表紧跟对应客户端,例如 192.168.2.120(rw,sync),不能写成 192.168.2.120 (rw,sync)。

课程写法同时使用 * 和 no_root_squash,意味着网络中任意能访问 NFS 服务的主机都可能以 root 权限修改这套根文件系统。为了让开发板的 root 用户能够维护网络根目录,实验环境可能确实需要 no_root_squash,但应至少把客户端限制为开发板实际 IP。例如沿用上一课开发板地址时,可以写成:

/home/wfeng/nfs 192.168.2.120(rw,sync,no_subtree_check,no_root_squash)

这里的 192.168.2.120 必须替换成开发板在 Linux 阶段真正使用的地址;若地址会变化,可限制为专用板卡子网,而不是无条件使用 *。新增的 no_subtree_check 关闭对子目录导出的子树一致性检查,适合文件频繁重命名的开发根目录,但它不是视频原配置,是否使用应按自己的导出布局决定。

Ubuntu 官方文档特别提醒:no_root_squash 允许客户端 root 修改 root 所有的服务端文件,在多用户环境中可能造成安全问题;还建议把允许的主机声明写得尽可能具体。参阅 Ubuntu Server NFS 配置说明和 Ubuntu exports(5) 官方手册。

保存 /etc/exports 后,视频执行旧式 init 脚本:

# 视频原命令;Ubuntu 20.04 的脚本最终转交给 systemd。
sudo /etc/init.d/nfs-kernel-server restart

画面返回:

Restarting nfs-kernel-server (via systemctl): nfs-kernel-server.service.

这正说明 /etc/init.d/ 命令只是兼容入口。更清晰的现代操作和验证流程是:

# 重新读取 /etc/exports,并详细显示重新导出的项目;不必为每次改规则都先重启整台虚拟机。
sudo exportfs -rav

# 重启 NFS 服务,使相关守护进程重新读取配置。
sudo systemctl restart nfs-kernel-server

# 检查服务是否成功运行;不要把“命令执行完”当成“服务一定正常”。
sudo systemctl status nfs-kernel-server --no-pager

# 显示当前实际生效的导出目录及其选项。
sudo exportfs -v

# 查看失败或警告信息,尤其关注 exports 语法、目录不存在和权限错误。
sudo journalctl -u nfs-kernel-server -n 50 --no-pager

Ubuntu 的 exportfs 手册说明,修改 /etc/exports 后可以执行 exportfs -ra,也可以重启 NFS 服务;exportfs -v 则用于查看有效选项。参阅 Ubuntu Focal exportfs(8) 手册。

1.2.3.1 从配置完成到可启动还缺什么

完成本课命令后,应逐项确认:

  • tftpd-hpa 与 nfs-kernel-server 都是 active 状态;
  • TFTP 根目录里已经放入文件名完全匹配的 zImage 和 DTB,服务用户能够读取;
  • sudo exportfs -v 能看到实际 NFS 导出规则,而不只是 /etc/exports 中有一行文字;
  • NFS 导出目录中已经部署完整根文件系统,并保留权限、所有者和符号链接;
  • 虚拟机板卡侧网卡与开发板互通,TFTP/NFS 绑定或防火墙规则允许该接口;
  • U-Boot 的 serverip、ipaddr、TFTP 文件名和装载地址正确;
  • Linux 启动参数中的 root=/dev/nfs、nfsroot=、ip= 与这里的服务器地址和导出目录一致;
  • 内核已经内建早期网卡、IP 自动配置和 NFS 根文件系统支持。

常见故障可按阶段判断:

现象优先检查
systemctl restart tftpd-hpa 失败TFTP_OPTIONS 拼写、目录是否存在、UDP 69 是否被占用、日志
U-Boot 报 TFTP error: File not found文件名大小写、TFTP 根目录、文件读权限、是否误带服务端绝对路径
U-Boot TFTP 一直超时物理链路、网卡模式、IP/掩码、防火墙、服务监听地址
exportfs -ra 报语法错误客户端与括号之间的空格、路径、逗号和括号是否正确
TFTP 成功但内核无法挂载 NFS 根bootargs、NFS 导出范围、rpcbind/NFS 服务、内核网卡和 NFS 支持
NFS 挂载后出现 Permission denied服务端路径权限、UID/GID、root_squash、父目录执行权限
已挂载但用户空间启动失败根文件系统内容、init/linuxrc、动态链接器、库、权限和架构是否匹配

最后要记住:本课搭建的是虚拟机服务端,不是通过这几条命令自动“搭建上网卡”。视频最后提到的双网卡模式、开发板串口、U-Boot 网络参数和启动命令属于后续步骤。只有服务端、网络链路、引导参数和根文件系统四部分全部匹配,上一课展示的“TFTP 下载内核与 DTB + NFS 挂载根文件系统”才能稳定复现。