uboot环境变量设置
339.3.12 U-Boot 环境变量设置
本节没有编写 C 程序、驱动源码或设备树源码,因此标题下不添加臆造的 C 代码。本节要把上一课搭好的开发板专用网络真正用于网络启动:U-Boot 先通过 TFTP 把 Linux 内核 zImage 和设备树 topeet_emmc_hdmi.dtb 下载到 DDR,随后启动内核;Linux 内核再通过 NFS 把 Ubuntu 中的 ~/nfs 挂载为根文件系统。
Ubuntu/TFTP/NFS 服务器(192.168.2.220)
│
├── TFTP ──> U-Boot 将 zImage、DTB 下载到开发板 DDR
│
└── NFS ──> Linux 内核把 /home/wfeng/nfs 挂成根文件系统
│
└── 开发板(192.168.2.120)
必须先分清三个阶段:U-Boot 负责设置环境变量和下载启动文件;bootz 把控制权交给 Linux;NFS 根文件系统由已经启动的 Linux 内核挂载,不是 U-Boot 把整个根文件系统下载到内存。TFTP 成功只能证明启动文件可下载,不能证明 NFS 一定可用。
视频中的实验环境为 i.MX6ULL 开发板、512 MiB DDR、U-Boot 2016.03,网络设备输出为 FEC1;串口终端使用 MobaXterm、CH340 串口和 115200 波特率。下面的文件名、内存地址和网卡名都与这块板的课程配置绑定,不能不加核对地照搬到其他板卡。
一、U-Boot 网络配置
1.1 流程详解
开发板上电后,U-Boot 在自动启动前显示 3 秒倒计时。在倒计时归零前按任意键,进入 => 提示符;若不打断,U-Boot 会执行 bootcmd。U-Boot 官方文档也将 bootcmd 定义为启动延时结束后自动执行的命令,将 bootargs 定义为传给操作系统的命令行参数,见 U-Boot 环境变量说明。
=> ? # 列出当前构建实际支持的命令;help 具有相同用途
=> printenv # 打印全部环境变量
=> printenv ipaddr serverip netmask gatewayip ethaddr
# 只查看本节关心的网络变量,输出更容易核对
命令所在的终端必须分清:
| 提示符/界面 | 执行内容 | 本节典型命令 |
|---|---|---|
| Ubuntu Shell | 准备 TFTP 文件、解压根文件系统、管理 NFS 服务 | cp、tar、exportfs |
U-Boot 串口 => | 配置板卡、TFTP 下载、启动内核 | setenv、saveenv、tftp、bootz |
| Linux 启动日志/登录口 | 内核启动及根文件系统中的用户空间 | Welcome to topeet!、登录提示 |
1.2 修改网络相关环境变量
视频逐条设置的网络变量是:
=> setenv serverip 192.168.2.220
=> setenv gatewayip 192.168.2.1
=> setenv ipaddr 192.168.2.120
=> setenv netmask 255.255.255.0
=> setenv ethaddr 42:03:07:3B:15:5F
| 变量 | 视频中的值 | 准确含义与注意事项 |
|---|---|---|
serverip | 192.168.2.220 | TFTP/NFS 所在 Ubuntu 的地址;U-Boot 官方说明中,serverip 是 TFTP 服务器地址 |
ipaddr | 192.168.2.120 | U-Boot 阶段开发板自己的地址 |
netmask | 255.255.255.0 | 即 /24;使 .120、.220、.223 位于同一 192.168.2.0/24 子网 |
gatewayip | 192.168.2.1 | U-Boot 的网关变量;只有访问其他子网时才需要真实网关 |
ethaddr | 42:03:07:3B:15:5F | 开发板以太网 MAC 地址;必须在同一二层网络中唯一 |
上一课的拓扑是开发板、Windows 物理网卡和 Ubuntu 第二网卡处于同一子网,并没有证据表明 192.168.2.1 上存在路由器。因此,本实验的 TFTP/NFS 流量不经过网关,gatewayip 可以不设;若要跨网段,才应填入实际可达的路由器地址。
视频在修改前曾打印出另一条 ethaddr=08:07:03:A0:03:22,随后又将其改成 42:03:07:3B:15:5F。不要把课程 MAC 批量复制到多块板上:重复 MAC 会让交换机和主机的邻居表不断改变。若板卡已有厂家分配且有效的 MAC,应优先保留;若实验中必须自定义,要保证唯一。0x42 的最低位为 0,表示单播;次低位为 1,表示本地管理地址,但“本地管理”仍不等于可以重复使用。
设置后应立即核对,避免把变量名或地址输错:
=> printenv serverip gatewayip ipaddr netmask ethaddr
setenv 修改的是当前运行时环境,命令执行后已经可以用于本次网络操作;此时尚未持久化。
1.3 保存环境变量
正确命令是 saveenv,不是原笔记 OCR 识别出的“c5 env”:
=> saveenv
Saving Environment to MMC...
Writing to MMC(1)... done
画面中的存储介质是 MMC/eMMC,不是“emsa”;结束信息是 done,不是“down”。saveenv 将当前环境保存到板卡为 U-Boot 环境预留的非易失存储区,使断电后仍能恢复。官方 env 命令文档说明,env save 会把环境保存到持久化介质,saveenv 是兼容别名,见 U-Boot env 命令。
重启不是让变量“开始生效”的必要条件;变量在 setenv 后已作用于当前会话。这里重启的意义是验证保存结果,并让自动启动流程重新读取并执行新 bootcmd。
1.4 重启 U-Boot 网络设置
=> reset
reset 会立即复位开发板。重新出现倒计时后按任意键,再用 printenv 检查保存的变量是否仍在。若只是想继续手动测试网络,不需要为了每次改动都复位;如果已经设置了新的 bootcmd,复位后又没有打断倒计时,它会直接进入自动下载和启动流程。
不要把在终端软件里按 Ctrl+A、Ctrl+C 当成“进入 U-Boot 设置模式”的固定组合。本视频真正依赖的是板卡复位/执行 reset,并在 U-Boot 倒计时期间按键。Ctrl+C 在 U-Boot 网络命令持续重试时可用于中止当前命令。
1.5 网络测试
视频在 U-Boot 中测试了 Ubuntu、Windows 以及一个没有回应的地址:
=> ping 192.168.2.220
Using FEC1 device
host 192.168.2.220 is alive
=> ping 192.168.2.223
Using FEC1 device
host 192.168.2.223 is alive
=> ping 192.168.2.222
ARP Retry count exceeded; starting again
ping failed; host 192.168.2.222 is not alive
成功提示是 host ... is alive,不是 OCR 识别出的“all love love”。失败时 U-Boot 可能反复尝试 ARP,可按 Ctrl+C 中止。U-Boot 官方网络测试会以 host is alive 作为 ping 成功依据,见 U-Boot 网络测试说明。
Windows 主动 ping 192.168.2.120 在视频中没有收到响应,但这不能被解释成“失败反而证明网络已经通”。很多 U-Boot 构建只在执行网络命令时轮询和处理网络包,停在普通命令提示符时未必持续响应主机发来的 ICMP Echo。正确结论是:
- U-Boot 主动
pingUbuntu/Windows 成功,证明该次 ARP 与 ICMP 往返成功; - Windows 主动 ping U-Boot 失败,单凭这一项无法判定断线还是 U-Boot 未响应;
- 后面的 TFTP 实际下载成功,才进一步验证 U-Boot 到服务器的 UDP/TFTP 路径、服务和文件访问都可用。
1.6 修改 U-Boot 引导方式
视频要把默认从 eMMC 读取内核/DTB 的流程,改为“U-Boot 从 TFTP 取内核和 DTB,Linux 从 NFS 挂根文件系统”。画面中实际输入的两条命令如下:
=> setenv bootcmd 'tftp 80800000 zImage; tftp 83800000 topeet_emmc_hdmi.dtb; bootz 80800000 - 83800000'
=> setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs rw nfsroot=192.168.2.220:/home/wfeng/nfs ip=192.168.2.120:192.168.2.220:192.168.2.1:255.255.255.0::eth0:off'
bootcmd 中三条子命令按顺序执行:
tftp 80800000 zImage:从serverip下载内核到 DDR 地址0x80800000;tftp 83800000 topeet_emmc_hdmi.dtb:下载 eMMC+HDMI 版本的 DTB 到0x83800000;bootz 80800000 - 83800000:从第一个地址启动 zImage,-表示没有 initrd,第三个参数是设备树地址。
这里必须用 bootz,原笔记写成 bootm 是错误的。U-Boot 官方 bootz 文档明确说明它用于启动 zImage,并规定没有 initrd、但要传 DTB 时用 - 占据第二个参数。地址省略了 0x,但在 U-Boot 命令语境中作为十六进制地址使用。这些地址是课程板卡的既定内存布局;换板或换镜像必须根据该板的 U-Boot 配置、DDR 范围和镜像大小重新确认,避免镜像互相覆盖或落到不可用内存。
bootargs 是传给 Linux 内核的一整行启动参数:
| 字段 | 含义 |
|---|---|
console=ttymxc0,115200 | 将内核控制台放在 i.MX 的第 1 个 UART,波特率 115200 |
root=/dev/nfs | 告诉内核根文件系统来自 NFS;/dev/nfs 是选择 NFS 根的伪设备名,不是真实块设备 |
rw | 以可读写方式挂载根文件系统 |
nfsroot=192.168.2.220:/home/wfeng/nfs | NFS 服务器地址和服务端导出的根目录 |
ip=... | 配置 Linux 启动阶段的客户端 IP、服务器 IP、网关、掩码、主机名、网卡和自动配置方式 |
Linux 内核官方文档给出的格式是:
nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]
ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>
其中网关只在服务器位于不同子网时需要,完整解释见 Linux 内核 NFS 根文件系统文档。本课三端同属 /24,若 192.168.2.1 并不存在,更严谨的客户端参数应把网关字段留空;这会在服务器地址后连续出现两个冒号:
=> setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs rw nfsroot=192.168.2.220:/home/wfeng/nfs,v3,tcp ip=192.168.2.120:192.168.2.220::255.255.255.0::eth0:off'
这条改进写法还显式请求 NFSv3/TCP。视频中板卡原有默认环境的 netargs 本来就包含 nfsroot=${serverip}:${nfsroot},v3,tcp,说明该 BSP 预期使用 v3;显式协商客户端版本通常比为了旧客户端去扩大服务器的 NFSv2 暴露更清晰。若课程内核不识别 v3 简写,可依据它所配套的内核文档改用标准挂载选项 vers=3,tcp,不能只靠猜测反复改服务端。
1.7 文件系统、内核拷贝
视频中的内核源码树位于:
~/Linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga
在 Ubuntu Shell 中把编译产物放进上一课配置的 TFTP 根目录 ~/tftpfile:
# 进入 ARM 内核启动镜像所在目录。
cd ~/Linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga/arch/arm/boot
# 复制已编译的 zImage 到 TFTP 根目录。
cp zImage ~/tftpfile/
# 进入设备树编译产物目录。
cd dts
# 视频用通配符筛选 HDMI 版本;Linux 文件名区分大小写,应使用小写 hdmi。
ls *hdmi*.dtb
# eMMC 板选择 eMMC+HDMI 的 DTB;不要误选 NAND 版本。
cp topeet_emmc_hdmi.dtb ~/tftpfile/
# 核对 U-Boot 将要请求的两个精确文件名、大小和时间。
ls -lh ~/tftpfile/zImage ~/tftpfile/topeet_emmc_hdmi.dtb
画面中的筛选结果同时包含 topeet_emmc_hdmi.dtb 和 topeet_nand_hdmi.dtb,本课程是 eMMC 板,所以选择前者。原笔记的 /tftp/ 与画面不符;本课程实际 TFTP 根目录是 ~/tftpfile。bootcmd 中的文件名必须与该目录下的大小写完全一致。
二、U-Boot 环境变量设置
2.1 U-Boot 环境变量设置操作
在把命令写入 bootcmd 前,视频先做了逐步手动测试:
=> tftp 80800000 zImage
TFTP from server 192.168.2.220; our IP address is 192.168.2.120
Load address: 0x80800000
Bytes transferred = 7186568 (6da888 hex)
=> tftp 83800000 topeet_emmc_hdmi.dtb
TFTP from server 192.168.2.220; our IP address is 192.168.2.120
Load address: 0x83800000
Bytes transferred = 40551 (9e67 hex)
=> bootz 80800000 - 83800000
这组结果分别证明:服务器和网络可达;TFTP 根目录中存在两个文件;U-Boot 能把它们放到指定地址;bootz 开始运行 Linux。官方下载测试也将“完成下载,并校验得到的大小或 CRC”作为 TFTP 成功条件,见 U-Boot 网络测试文档。
第一次手动执行时并没有先设置有效的 bootargs,内核最终输出:
VFS: Cannot open root device "(null)" or unknown-block(0,0): error -6
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
这不是 bootz 或 TFTP 失败,而是内核没有得到有效的根文件系统位置。只有同时准备好 rootfs、导出 NFS 目录并传入正确 bootargs,内核才可能继续进入用户空间。
2.2 NFS 挂载失败问题分析
排障时要按启动阶段读最后一条明确错误,不能把所有故障都统称为“内核没起来”:
| 已出现的现象 | 已经成功的部分 | 下一步应查 |
|---|---|---|
Bytes transferred = ... | U-Boot 的网卡、TFTP 服务、文件名和下载地址基本可用 | bootz 参数、镜像匹配关系 |
Starting kernel ... 后出现 Linux 驱动日志 | U-Boot 已把控制权交给 Linux,内核能够执行 | 内核命令行、网络驱动、NFS |
root device "(null)" | 内核已运行 | bootargs 是否存在 root=/dev/nfs、nfsroot=、ip= |
Unable to mount root fs via NFS | 内核已尝试 NFS 根 | 服务版本、导出路径、权限、rootfs 内容、网络中断 |
视频开始时 ~/nfs 为空,这确实意味着还没有可用的根文件系统内容;但“目录为空”和“没有传 bootargs”是两个独立问题,不能用其中一个替代另一个。上一课还必须已经在 /etc/exports 中导出 /home/wfeng/nfs,并让配置真正生效。
2.3 U-Boot 默认环境变量
2.3.1 恢复默认环境的正确含义
视频演示了恢复默认环境的命令:
=> env default -a
=> saveenv
env default -a 会把 全部 环境变量在内存中恢复为 U-Boot 编译时默认值;saveenv 才把这一结果持久化。它会覆盖网络地址、MAC、bootcmd、bootargs 等自定义配置,不是普通“刷新”操作。执行前应先用 printenv 保存或抄录现有配置。若只想恢复某个变量,应先查当前 U-Boot 的 env default 帮助,不应无条件使用 -a。
2.3.2 准备课程 rootfs 压缩包
本小节标题沿用原笔记,但视频后半段实际还讲了 rootfs 的获取。课程从配套资料取得 rootfs.tar.bz2,再通过 Samba 或 FileZilla 复制到 Ubuntu 的 ~/nfs。传输工具只负责搬运文件,不能证明这个同名压缩包就是本板卡、该内核所需的版本;复制后应记录来源并校验哈希:
# 记录压缩包大小、时间以及 SHA-256,便于与课程发布值核对。
ls -lh ~/nfs/rootfs.tar.bz2
sha256sum ~/nfs/rootfs.tar.bz2
2.4 ping 虚拟机与 Windows
本标题沿用原始笔记;虚拟机与 Windows 的 ping 已在 1.5 节核准,本段视频画面实际主要在解压 rootfs。rootfs.tar.bz2 是 bzip2 压缩的 tar 归档,GNU tar 在很多版本中可由文件内容自动识别压缩格式,所以视频中的 tar -xvf 能工作;写出 j 更明确:
# 先只列目录,确认归档没有额外套一层错误目录,并检查路径是否异常。
tar -tjf ~/nfs/rootfs.tar.bz2 | head
# 进入 NFS 导出目录后,以 root 权限保留数字 UID/GID、权限和符号链接进行解压。
cd ~/nfs
sudo tar --numeric-owner -xvjpf rootfs.tar.bz2
# 根目录内容应直接出现 bin、etc、lib、linuxrc 等,而不是再嵌套一层 rootfs/。
ls -la
嵌入式根文件系统依赖文件所有者、执行权限、符号链接,某些归档还可能包含特殊设备节点,因此不能只看“文件解出来了”。压缩包应来自可信来源;在成功启动、哈希核对和备份完成前不要急着删除原包。若重新解压不同版本,最好先准备一个新的空导出目录,防止旧文件与新文件混合,造成更难判断的“半新半旧”系统。
2.5 最后一步配置
手动 TFTP 和 bootz 验证完成后,复位开发板、在倒计时期间按键回到 =>,再将已验证的启动链写入环境变量。视频原样命令是:
=> setenv bootcmd 'tftp 80800000 zImage; tftp 83800000 topeet_emmc_hdmi.dtb; bootz 80800000 - 83800000'
=> setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs rw nfsroot=192.168.2.220:/home/wfeng/nfs ip=192.168.2.120:192.168.2.220:192.168.2.1:255.255.255.0::eth0:off'
若采用本笔记前面建议的同子网、NFSv3/TCP 写法,则只替换第二条为:
=> setenv bootargs 'console=ttymxc0,115200 root=/dev/nfs rw nfsroot=192.168.2.220:/home/wfeng/nfs,v3,tcp ip=192.168.2.120:192.168.2.220::255.255.255.0::eth0:off'
粘贴长命令后不要立刻保存,先回读:
=> printenv bootcmd bootargs
重点逐字符检查 DTB 文件名、三处内存地址、NFS 路径、IP 字段中的冒号数量以及 ttymxc0 的大小写。终端自动换行只是显示效果,不代表变量中真的插入了换行。
2.6 设置环境变量并保存
确认无误后执行:
=> saveenv
Saving Environment to MMC...
Writing to MMC(1)... done
原笔记再次出现的 c5 env 仍是 OCR 错误。若 saveenv 报错,不能假定已经持久化;应检查 U-Boot 为环境配置的存储设备/分区以及 MMC 写入状态。保存成功后也可以再次 printenv bootcmd bootargs,留存串口记录,便于故障时确认板卡到底用了什么参数。
2.7 重置开机
=> reset
这次不要按键打断倒计时,U-Boot 会自动执行 bootcmd:每次从 ~/tftpfile 下载当前的 zImage 和 DTB,再启动 Linux;Linux 随后从 Ubuntu 的 ~/nfs 使用根文件系统。这正是驱动开发方便之处:
- 重新编译内核/设备树后,把新产物覆盖到 TFTP 根目录,下次启动即可下载;
- 修改 rootfs 中的应用、脚本或库后,开发板通过 NFS 直接看到服务端文件;
- 这并不意味着所有修改都能热生效:已加载的内核、DTB 和正在运行的进程不会因为服务端文件变化自动重载,相关修改仍需重启或重启进程;
- 覆盖启动文件前建议保留可回退版本和哈希,防止新镜像导致板卡无法启动。
2.8 内核无法启动问题分析
视频将问题与“课程原环境是 Ubuntu 16,而当前使用 Ubuntu 20”联系起来,这只是排查线索,不是完整因果关系。主机桌面版本本身不会直接决定板卡 Linux 能否执行;真正需要核对的是主机所带 NFS 服务端默认启用哪些协议版本、板端 4.1.15 内核支持并请求什么版本、导出规则是否生效,以及 rootfs 是否正确。
把故障写成“Ubuntu 20 不支持 NFSv2,只支持 3/4”也过于绝对。Ubuntu 20.04 的 nfs-utils/内核组合可能不默认提供某些旧协议,而不同更新和配置会改变实际启用集合;更新的 Ubuntu 中 NFSv2 又可能已经彻底移除。应查看运行中的事实,而不是只凭发行版名称:
# 查看内核 NFS 服务端当前启用/禁用的版本;+ 表示启用,- 表示禁用。
cat /proc/fs/nfsd/versions
# 查看 RPC 注册的 NFS、mountd 等服务和协议。
rpcinfo -p localhost
# 查看真正生效的导出规则。
sudo exportfs -v
# 查看服务状态和本次启动日志。
systemctl status nfs-kernel-server --no-pager
journalctl -u nfs-kernel-server -b --no-pager
Ubuntu 的 nfsstat 手册说明 nfsstat -s 可只显示服务端统计,并可按 v2/v3/v4 区分,见 Ubuntu 20.04 nfsstat(8)。启动开发板前后比较计数,还能判断请求是否真的抵达服务器。
2.9 U-Boot 通过 NFS 挂根文件系统时,加载完内核后卡死在这里
这个原题目的说法需要纠正:U-Boot 通过 TFTP 加载内核和 DTB;进入 Starting kernel ... 后,是 Linux 内核尝试通过 NFS 挂根文件系统。视频后续出现的关键日志是:
VFS: Unable to mount root fs via NFS, trying floppy.
VFS: Cannot open root device "nfs" or unknown-block(2,0): error -6
此时内核已经运行,故障位于 Linux 网络/NFS 根阶段。“卡死”也不够准确:画面最终进入 kernel panic,是内核明确终止启动,而不是无日志地停住。应向上查找同一段日志中的 IP-Config、网卡 link、NFS 服务器响应、超时和拒绝信息,再结合服务端日志定位。
2.10 U-Boot 通过 NFS 挂载根文件系统卡死解决办法
视频参考网上资料,在 Ubuntu 的 /etc/default/nfs-kernel-server 末尾加入:
RPCNFSDOPTS="--nfs-version 2,3,4 --debug --syslog"
然后重启 NFS 服务。这里应将它记录为视频尝试过的兼容性排查手段,而不是普适答案:--nfs-version 请求服务端提供指定版本,--debug 开启调试消息,--syslog 将消息写入系统日志。Ubuntu 的 rpc.nfsd 手册说明这些参数控制服务端提供的 NFS 版本和日志方式;同时,较新的实现已经只支持主要版本 3、4,说明硬编码开启 v2 并不具备长期兼容性,见 Ubuntu rpc.nfsd(8)。
修改后必须验证服务是否接受配置,而不是只看文件已保存:
sudo systemctl restart nfs-kernel-server
systemctl status nfs-kernel-server --no-pager
cat /proc/fs/nfsd/versions
sudo exportfs -v
若服务启动失败,应恢复错误参数并查看日志。长期保留 --debug 会增加日志量;问题定位完可以去掉。NFS/TFTP 都没有传输加密和强身份保护,本实验服务应只暴露在隔离的板卡开发网,不应直接开放到不可信网络。
2.11 问题解决
课程讲解把上述配置理解为“强制使用 NFSv2”,但该字符串实际上请求服务端尝试提供 2、3、4;能否真正启用仍取决于本机 rpc.nfsd 和内核。更稳妥的解决顺序是:
- 优先在客户端
nfsroot中显式使用本 BSP 默认环境已经采用的,v3,tcp; - 用
/proc/fs/nfsd/versions、rpcinfo -p和nfsstat -s核对服务端事实; - 用
exportfs -v确认/home/wfeng/nfs对开发板地址或板卡子网真正导出; - 从启动日志区分“无服务器响应”“被拒绝”“协议不支持”“路径不存在”和“rootfs 无法执行”;
- 只有证据表明板端只能发出 v2 请求、且服务端确实还能安全启用 v2 时,才把启用 v2 作为临时兼容措施。
Linux 官方 NFS 根文档允许在 nfsroot= 后附加标准 NFS 选项,因此在客户端固定版本和传输协议是有文档依据的。服务端配置不是本节唯一变量:视频在做完版本修改后仍然没有立即启动成功,不能声称这一步已经单独解决全部问题。
2.12 问题解决后重启开机
服务端配置、导出目录和 rootfs 都确认后,重启 NFS 服务,再复位板卡观察完整日志。真正证明 NFS 根已挂上的关键行是:
VFS: Mounted root (nfs filesystem) on device 0:14.
后续出现:
Welcome to topeet!
iTOP_imx6ul/ull login:
才说明内核已经执行根文件系统中的初始化程序并到达登录界面。视频中还出现过:
nfs: server 192.168.2.220 not responding, still trying
nfs: server 192.168.2.220 OK
两行合起来表示服务器一度超时、随后恢复响应;只截取第一行会误判为永久失败。仍应检查虚拟机负载、桥接网卡、线缆和服务端日志,避免把偶发恢复当成健康状态。
2.13 问题最终定位
视频最终发现存在两个同名 rootfs.tar.bz2,画面先展示了约 18 MB 的包,随后指向约 1.92 MB 的课程包并说明后者才是这次应使用的文件。替换并重新解压正确 rootfs 后,系统最终到达登录提示。因此本次实际排障至少包含两个相互独立的变量:NFS 协议/服务端配置,以及 rootfs 包选错。还要注意,错误的 rootfs 内容通常会导致挂载后的 init、动态库或用户空间启动失败,不能单独解释此前发生在协议层的“NFS 根无法挂载”;视频的先后变化能证明换包参与了解决过程,却不能证明它是每一条早期报错的唯一原因。不能把结论简单归纳成“只要打开 NFSv2”或“只要改权限”。
文件大小只能帮助识别本次画面中的两个包,不是通用真伪标准。可靠做法是使用课程发布位置、版本说明和 SHA-256 校验值;压缩包即使能解压,也可能属于另一块板、另一套 libc/ABI 或不同初始化系统。
成功进入登录界面后,视频日志仍出现了这些警告:
modprobe: can't change directory to '/lib/modules': No such file or directory
Starting network: ip: RTNETLINK answers: File exists FAIL
第一条表示 rootfs 中没有与当前内核匹配的 /lib/modules/<kernel-release>,需要把本次内核编译产生的模块安装到根文件系统,或确认系统根本不依赖可加载模块。第二条常见于内核已按 ip= 配好 eth0,用户空间启动脚本又重复添加地址/路由;它不等于 NFS 挂载失败,但应清理重复网络配置。画面中的蓝牙固件报错也属于外围功能问题,不能用“已到登录界面”掩盖。
最终建议按下表验收,而不是只看某一个提示:
| 检查点 | 正确证据 | 常见错误方向 |
|---|---|---|
| U-Boot 网络 | ping 服务器成功 | IP/掩码、桥接、MAC 冲突、线缆 |
| TFTP 内核 | 文件名、字节数和装载地址正确 | TFTP 根目录、大小写、错误镜像 |
bootz | 出现 Starting kernel ... 和 Linux 日志 | 地址重叠、内核/DTB 不匹配、错误命令 |
| Linux 静态网络 | IP-Config 显示 .120/.220 和正确掩码 | ip= 字段错位、错误网卡名/网关 |
| NFS 服务 | 请求到达,导出规则与协议匹配 | 服务未启动、版本不匹配、导出路径/权限 |
| NFS 根挂载 | VFS: Mounted root (nfs filesystem) | rootfs 路径、协议、服务器超时 |
| 用户空间 | 到达 login:,关键服务无致命错误 | 错 rootfs、init/动态库/权限/模块不匹配 |
这套分阶段证据链比“反复 reset 看能不能碰巧启动”更可靠:先确认上一个阶段已经完成,再只排查下一层,才能快速定位网络启动问题。