2026年正点原子开发板移植方案——从0开始的Rootfs之路(5)WSL + NFS 网络启动踩坑记:从挂载失败到成功启动的完整历程

2026年正点原子开发板移植方案------从0开始的Rootfs之路(5)WSL + NFS 网络启动踩坑记:从挂载失败到成功启动的完整历程

项目已经开源!尝试使用IMX-Forge给你的开发板跑新的Linux 7.0内核:https://github.com/Awesome-Embedded-Learning-Studio/imx-forge

其他教程也已经全部同步到仓库中!也可以访问静态网站:

https://awesome-embedded-learning-studio.github.io/imx-forge

获得更好的阅读体验

前言:为什么选择 NFS

在嵌入式 Linux 开发中,Rootfs 的部署方式有好几种:

  1. 烧录到 Flash:把 Rootfs 打包成镜像,烧录到 eMMC、SD 卡或 NAND Flash。缺点是每次修改都要重新烧录,开发效率低。
  2. UBI 文件系统:使用 UBI/UBIFS 管理原始 Flash 设备。适合量产阶段,但配置复杂。
  3. NFS 网络挂载:开发板通过网络从主机挂载 Rootfs。修改立即生效,无需重新烧录。

对于开发阶段,NFS 是最方便的选择------你可以在主机上修改文件、添加程序,开发板重启后立即看到效果。但是,NFS 的配置也是出了名的坑多,尤其是当你使用 WSL2 作为开发环境时,各种问题接踵而至。

这一章,我会分享我从 NFS 挂载失败到成功启动的完整排查过程,以及中间踩过的各种坑。

环境说明

  • 开发板:i.MX6ULL
  • 主机环境:WSL2(Ubuntu 22.04),Windows 11
  • 网络模式:WSL2 networkingMode=mirrored
  • 内核版本:Linux 6.12
  • BusyBox 版本:1.37.0

最终工作配置速查

在你被各种问题折磨得想砸键盘之前,先给你一个"最终答案"------这是我经过无数次尝试后验证可用的完整配置:

U-Boot bootargs 配置

bash 复制代码
setenv bootargs "console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.60.1:/home/charliechen/imx-forge/rootfs/nfs,vers=3,proto=tcp rw ip=192.168.60.200:192.168.60.1:192.168.60.1:255.255.255.0::eth0:off"
saveenv

参数解析

  • console=ttymxc0,115200:控制台设备和波特率
  • root=/dev/nfs:指定根文件系统是 NFS
  • nfsroot=<主机IP>:<rootfs路径>,vers=3,proto=tcp:NFS 服务器地址、路径、版本和协议
  • ip=<板子IP>:<主机IP>:<网关IP>:<子网掩码>::<网卡>:off:网络配置

/etc/exports 配置

bash 复制代码
/home/charliechen/imx-forge/rootfs/nfs  192.168.60.0/24(rw,sync,no_root_squash,no_subtree_check)

选项说明

  • rw:读写权限
  • sync:同步写入(数据更安全)
  • no_root_squash:不压缩 root 用户权限(客户端 root 用户就是服务端 root 用户)
  • no_subtree_check:不检查子目录(提高性能)

/etc/nfs.conf 端口固定

ini 复制代码
[mountd]
port=20048

[lockd]
port=32803
udp-port=32769

[statd]
port=32765

Windows 防火墙规则

powershell 复制代码
# 在管理员 PowerShell 中执行
New-NetFirewallRule -DisplayName 'NFS-TCP-111'      -Direction Inbound -Protocol TCP -LocalPort 111   -Action Allow -Profile Any
New-NetFirewallRule -DisplayName 'NFS-UDP-111'      -Direction Inbound -Protocol UDP -LocalPort 111   -Action Allow -Profile Any
New-NetFirewallRule -DisplayName 'NFS-TCP-2049'     -Direction Inbound -Protocol TCP -LocalPort 2049  -Action Allow -Profile Any
New-NetFirewallRule -DisplayName 'NFS-mountd-20048' -Direction Inbound -Protocol TCP -LocalPort 20048 -Action Allow -Profile Any
New-NetFirewallRule -DisplayName 'NFS-lockd-32803'  -Direction Inbound -Protocol TCP -LocalPort 32803 -Action Allow -Profile Any
New-NetFirewallRule -DisplayName 'NFS-statd-32765'  -Direction Inbound -Protocol TCP -LocalPort 32765 -Action Allow -Profile Any

# 网桥网卡改为 Private(如果被识别为 Public 网络)
Set-NetConnectionProfile -InterfaceAlias "网桥" -NetworkCategory Private

NFS 服务端配置详解

安装 NFS 服务器

bash 复制代码
sudo apt update
sudo apt install nfs-kernel-server

配置 /etc/exports

/etc/exports 是 NFS 服务器的主配置文件,定义了哪些目录可以被哪些客户端挂载。

基本语法

复制代码
<导出目录>   <客户端1>(选项) <客户端2>(选项) ...

客户端地址格式

  • 192.168.1.100:单个 IP
  • 192.168.1.0/24:IP 网段(推荐)
  • *.example.com:域名通配符
  • *:所有客户端(不推荐,安全隐患)

常用选项

选项 作用 推荐值
ro 只读 -
rw 读写 开发阶段推荐
sync 同步写入(数据更安全) 推荐
async 异步写入(性能更好) 不推荐
no_root_squash 不压缩 root 权限 开发阶段推荐
root_squash 压缩 root 权限为 nfsnobody 生产环境推荐
no_subtree_check 禁用子树检查(提高性能) 推荐
subtree_check 启用子树检查 不推荐

配置示例

bash 复制代码
# /etc/exports
/home/charliechen/imx-forge/rootfs/nfs  192.168.60.0/24(rw,sync,no_root_squash,no_subtree_check)

配置完成后,应用更改:

bash 复制代码
sudo exportfs -ra

验证配置生效

bash 复制代码
sudo exportfs -v

输出示例:

复制代码
/home/charliechen/imx-forge/rootfs/nfs
	192.168.60.0/24(rw,wdelay,no_root_squash,no_subtree_check,sec=sys,rw,secure,no_root_squash,no_all_squash)

配置 /etc/nfs.conf 固定端口

这是 WSL2 环境下最关键的一步!因为 Windows 防火墙默认阻止所有端口,而 NFS 的 mountd、lockd、statd 服务默认使用随机端口,每次重启都会变化,根本无法提前配置防火墙规则。

编辑 /etc/nfs.conf

bash 复制代码
sudo nano /etc/nfs.conf

添加以下内容:

ini 复制代码
[nfsd]
debug=0
port=2049

[mountd]
debug=0
port=20048

[lockd]
port=32803
udp-port=32769

[statd]
debug=0
port=32765

重启 NFS 服务

bash 复制代码
sudo systemctl restart nfs-server

验证端口固定

bash 复制代码
rpcinfo -p localhost

输出示例:

复制代码
   program vers proto   port  service
    100000    4   tcp    111  portmapper
    100000    3   tcp    111  portmapper
    100000    2   tcp    111  portmapper
    100000    4   udp    111  portmapper
    100005    1   tcp  20048  mountd
    100005    3   tcp  20048  mountd
    100024    1   tcp  32765  status
    100021    1   tcp  32803  nlockmgr
    100003    3   tcp   2049  nfs

如果 mountdstatusnlockmgr 的端口固定了,说明配置成功。

Windows 防火墙配置

WSL2 mirrored 模式的特殊性

WSL2 有两种网络模式:

  1. NAT 模式(默认):WSL2 有自己的虚拟网络,端口通过 NAT 映射
  2. Mirrored 模式:WSL2 直接镜像主机的网络栈,看起来像是和主机在同一网络

mirrored 模式下,如果 firewall=true,Windows 防火墙规则会同步到 WSL2。这意味着如果 Windows 防火墙阻止了某个端口,WSL2 里的服务也无法从外部访问。

检查 WSL2 网络模式

在 Windows 用户目录下创建或编辑 .wslconfig 文件:

ini 复制代码
[wsl2]
networkingMode=mirrored
firewall=true

创建防火墙规则

管理员 PowerShell 中执行:

powershell 复制代码
# 基础 NFS 端口
New-NetFirewallRule -DisplayName 'NFS-TCP-111'      -Direction Inbound -Protocol TCP -LocalPort 111   -Action Allow -Profile Any
New-NetFirewallRule -DisplayName 'NFS-UDP-111'      -Direction Inbound -Protocol UDP -LocalPort 111   -Action Allow -Profile Any
New-NetFirewallRule -DisplayName 'NFS-TCP-2049'     -Direction Inbound -Protocol TCP -LocalPort 2049  -Action Allow -Profile Any

# mountd、lockd、statd 端口(需与 /etc/nfs.conf 一致)
New-NetFirewallRule -DisplayName 'NFS-mountd-20048' -Direction Inbound -Protocol TCP -LocalPort 20048 -Action Allow -Profile Any
New-NetFirewallRule -DisplayName 'NFS-lockd-32803'  -Direction Inbound -Protocol TCP -LocalPort 32803 -Action Allow -Profile Any
New-NetFirewallRule -DisplayName 'NFS-statd-32765'  -Direction Inbound -Protocol TCP -LocalPort 32765 -Action Allow -Profile Any

验证规则创建

powershell 复制代码
Get-NetFirewallRule | Where-Object {$_.DisplayName -like "NFS*"} | Format-Table -AutoSize

测试端口连通性

powershell 复制代码
Test-NetConnection -ComputerName 192.168.60.1 -Port 2049

如果 TcpTestSucceeded : True,说明端口已放行。

踩坑案例 1:bootargs 中 nfsroot 路径写错

现象

开发板启动后内核日志显示:

复制代码
IP-Config: Complete:
      device=eth0, hwaddr=02:aa:bb:cc:dd:ee, ipaddr=192.168.60.200, mask=255.255.255.0, gw=192.168.60.1
      host=192.168.60.200, domain=, nis-domain=(none)
      bootserver=192.168.60.1, rootserver=192.168.60.1, rootpath=
Root-NFS: No NFS server available

注意 rootpath= 是空的!

原因

bootargs 中的 nfsroot 参数格式错误,或者 IP 地址配置错误。

排查步骤

在 U-Boot 命令行中:

bash 复制代码
printenv bootargs
printenv ipaddr
printenv serverip

确认:

  1. nfsroot 中的 IP 和路径是否正确
  2. ip= 参数中的 IP 配置是否正确
  3. 网络是否通畅(可以 ping 主机)

解决方法

重新设置 bootargs,确保格式正确:

bash 复制代码
setenv bootargs "console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.60.1:/home/charliechen/imx-forge/rootfs/nfs,vers=3,proto=tcp rw ip=192.168.60.200:192.168.60.1:192.168.60.1:255.255.255.0::eth0:off"
saveenv

经验 :每次 setenv 后必须用 printenv bootargs 逐字核对,逗号前后不能有空格!

踩坑案例 2:bootargs 字符串被截断

现象

printenv bootargs 输出:

复制代码
bootargs=console=ttymxc0,115200 root=/dev/nfs nfsroot=192.168.60.1:/home/.../rootfs/ip=192.168.60.200:...

注意 /nfs,vers=3,proto=tcp rw 整段丢失,ip= 直接拼到路径后面!

原因

setenv 输入时漏掉了参数之间的空格,或者 nfsroot 选项格式错误。

解决方法

确保 nfsroot 的格式是:

复制代码
nfsroot=<IP>:<路径>,<选项>

选项之间用逗号分隔,不能有空格

正确示例:

bash 复制代码
nfsroot=192.168.60.1:/home/charliechen/imx-forge/rootfs/nfs,vers=3,proto=tcp

错误示例:

bash 复制代码
nfsroot=192.168.60.1:/home/charliechen/imx-forge/rootfs/nfs, vers=3, proto=tcp
#                                                   ^ 有空格!

踩坑案例 3:/etc/exports 文件不存在

现象

NFS 服务启动日志报错:

复制代码
exportfs: can't open /etc/exports for reading

重启后导出规则丢失,exportfs -v 输出为空。

原因

只用了 exportfs 命令临时导出,没有写入 /etc/exports 文件。

解决方法

必须把配置写入 /etc/exports 文件:

bash 复制代码
sudo bash -c 'cat > /etc/exports << EOF
/home/charliechen/imx-forge/rootfs/nfs  192.168.60.0/24(rw,sync,no_root_squash,no_subtree_check)
EOF'

然后应用配置:

bash 复制代码
sudo exportfs -ra
sudo systemctl restart nfs-server

验证

bash 复制代码
sudo exportfs -v

应该看到你的导出规则。

经验 :临时导出只在当前会话有效,重启后丢失。配置必须持久化到 /etc/exports

踩坑案例 4:WSL2 mirrored 模式下防火墙拦截 NFS

现象

  • 主机本地挂载 NFS 正常:sudo mount -t nfs 127.0.0.1:/home/.../nfs /mnt

  • 开发板连接超时

  • 内核日志在 IP 配置完成后停止输出:

    复制代码
    IP-Config: Complete: ...
    VFS: Mounted root (nfs filesystem).
    Freeing unused kernel memory: 1024K
    # 然后就没反应了...

原因

WSL2 networkingMode=mirrored + firewall=true 模式下,Windows 防火墙规则同步到 WSL,开发板发来的 NFS 请求被拦截。

排查步骤

1. 在 Windows PowerShell 中测试端口连通性

powershell 复制代码
Test-NetConnection -ComputerName 192.168.60.1 -Port 2049

如果输出:

复制代码
WARNING: TCP connect to (192.168.60.1 : 2049) failed

说明端口被拦截。

2. 检查 WSL2 防火墙日志(可选):

bash 复制代码
sudo dmesg | grep -i firewall

解决方法

按照前面的步骤创建 Windows 防火墙规则,确保端口 111、2049、20048、32803、32765 全部放行。

踩坑案例 5:mountd / lockd 使用动态随机端口

现象

放行 111 和 2049 后仍然挂载失败。

排查

bash 复制代码
rpcinfo -p | grep mountd

输出:

复制代码
    100005    1   tcp  46611  mountd
    100005    3   tcp  48315  mountd

端口每次重启都变,根本无法提前配置防火墙!

原因

NFS mountd、lockd、statd 默认使用随机端口。

解决方法

/etc/nfs.conf 中固定端口:

ini 复制代码
[mountd]
port=20048

[lockd]
port=32803
udp-port=32769

[statd]
port=32765

重启 NFS 服务:

bash 复制代码
sudo systemctl restart nfs-server

验证:

bash 复制代码
rpcinfo -p | grep mountd

输出:

复制代码
    100005    1   tcp  20048  mountd
    100005    3   tcp  20048  mountd

端口固定了!

然后在 Windows 防火墙中放行这些端口。

经验:WSL2 环境下必须固定 NFS 相关端口,否则防火墙配置无法生效。

踩坑案例 6:防火墙规则 Profile 为 Public 导致不生效

现象

防火墙规则已添加,Get-NetFirewallRule 可以查到,但端口仍然不通。

排查

powershell 复制代码
Get-NetConnectionProfile

输出:

复制代码
Name             : 网桥
InterfaceAlias   : 网桥
InterfaceIndex   : 27
NetworkCategory  : Public    ← 问题所在!
Domain           : False
IPv4Connectivity : Internet
IPv6Connectivity : Internet

原因

网桥网卡被 Windows 识别为「公用网络(Public)」,而新建防火墙规则默认只对「专用网络(Private)」生效。

解决方法

方法 1:把网卡改为 Private

powershell 复制代码
Set-NetConnectionProfile -InterfaceAlias "网桥" -NetworkCategory Private

方法 2:防火墙规则覆盖所有 Profile

powershell 复制代码
Get-NetFirewallRule | Where-Object {$_.DisplayName -like "NFS*"} | Set-NetFirewallRule -Profile Any

验证

powershell 复制代码
Get-NetFirewallRule -DisplayName "NFS-TCP-2049" | Select-Object DisplayName, Profile

输出:

复制代码
DisplayName      Profile
-----------      -------
NFS-TCP-2049    Any

经验:WSL2 mirrored 模式下,网桥可能被识别为 Public 网络,防火墙规则需要覆盖所有 Profile。

NFS 挂载完整流程图

理解 NFS 挂载的完整流程有助于快速定位问题:

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                         开发板上电                                    │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│  U-Boot 阶段                                                         │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │ 1. 配置网络:ipaddr, serverip                                │   │
│  │ 2. TFTP 下载内核:tftp 0x82000000 zImage                    │   │
│  │ 3. TFTP 下载设备树:tftp 0x83000000 imx6ull-14x14-evk.dtb  │   │
│  │ 4. 设置 bootargs:包含 nfsroot 和 ip 参数                    │   │
│  │ 5. 启动内核:bootz 0x82000000 - 0x83000000                  │   │
│  └─────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│  内核启动阶段                                                         │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │ 1. 解析 bootargs 中的 ip 参数,配置 eth0 网卡                │   │
│  │ 2. 输出:IP-Config: Complete: ...                           │   │
│  │ 3. 发起 NFS 挂载请求                                        │   │
│  └─────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│  NFS 挂载阶段                                                         │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │ Step 1: 连接 rpcbind (TCP 111)                              │   │
│  │         └─ 获取 mountd 端口 (20048)                         │   │
│  │                                                              │   │
│  │ Step 2: 连接 mountd (TCP 20048)                             │   │
│  │         └─ 获取 NFS 文件句柄                                │   │
│  │                                                              │   │
│  │ Step 3: 连接 nfsd (TCP 2049)                                │   │
│  │         └─ 挂载根文件系统                                    │   │
│  │                                                              │   │
│  │ 成功:VFS: Mounted root (nfs filesystem)                    │   │
│  │ 失败:Root-NFS: No NFS server available                     │   │
│  └─────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│  启动 /sbin/init                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │ 1. 执行 /etc/inittab 中的 sysinit 动作                       │   │
│  │ 2. 运行 /etc/init.d/rcS 初始化脚本                          │   │
│  │ 3. 挂载 /proc, /sys, /tmp 等                                │   │
│  │ 4. 运行 mdev -s 创建设备节点                                 │   │
│  │ 5. 启动 getty / sh                                          │   │
│  └─────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│  系统就绪,出现 shell 提示符                                          │
│  Welcome to i.MX6ULL Embedded Linux!                                 │
│  / #                                                                  │
└─────────────────────────────────────────────────────────────────────┘

排查工具速查

主机侧命令

命令 作用
sudo exportfs -v 查看当前 NFS 导出规则
`sudo ss -tnlp grep -E '2049
rpcinfo -p localhost 查看所有 RPC 服务端口
sudo mount -t nfs -o vers=3,proto=tcp <IP>:<路径> /mnt 本地验证挂载
sudo tail -f /var/log/syslog 查看 NFS 服务日志

Windows PowerShell 命令

命令 作用
Get-NetConnectionProfile 查看网卡网络类别
`Get-NetFirewallRule Where-Object {$_.DisplayName -like "NFS*"}`
Test-NetConnection -ComputerName <IP> -Port 2049 测试端口连通性
Set-NetConnectionProfile -InterfaceAlias "网桥" -NetworkCategory Private 改变网络类别

U-Boot 命令

命令 作用
printenv bootargs 查看 bootargs 配置
printenv ipaddr 查看开发板 IP
printenv serverip 查看主机 IP
ping <主机IP> 测试网络连通性

内核启动日志关键字

关键字 含义
IP-Config: Complete 网络配置成功
Root-NFS: No NFS server available NFS 服务器无法访问
VFS: Mounted root (nfs filesystem) NFS 挂载成功
Kernel panic - not syncing: No init found 找不到 /sbin/init

常见错误信息汇总

NFS 服务器侧

错误信息 原因 解决方法
exportfs: can't open /etc/exports /etc/exports 不存在或格式错误 检查文件语法
fsid 0: no export entry 导出路径不匹配 检查 /etc/exports 中的路径
refused mount request from 192.168.60.200 客户端 IP 未授权 检查 /etc/exports 中的客户端范围

开发板侧

错误信息 原因 解决方法
Root-NFS: No NFS server available 网络不通或防火墙拦截 检查网络、防火墙
NFS: server 192.168.60.1 not responding NFS 服务未启动或端口未放行 检查 NFS 服务状态、防火墙
nfs: server 192.168.60.1 OK 成功! -
VFS: Mounted root (nfs filesystem) on device 0:15 成功! -

验证 NFS 挂载成功

当你看到内核日志输出以下内容时,恭喜你,NFS 挂载成功了!

复制代码
IP-Config: Complete:
      device=eth0, hwaddr=02:aa:bb:cc:dd:ee, ipaddr=192.168.60.200, mask=255.255.255.0, gw=192.168.60.1
      host=192.168.60.200, domain=, nis-domain=(none)
      bootserver=192.168.60.1, rootserver=192.168.60.1, rootpath=/home/charliechen/imx-forge/rootfs/nfs
Looking up port of RPC 100003/2 on 192.168.60.1
Looking up port of RPC 100005/2 on 192.168.60.1
VFS: Mounted root (nfs filesystem).
Freeing unused kernel memory: 1024K

Welcome to i.MX6ULL Embedded Linux!

/ #

看到 / # 提示符,说明你已经成功进入了开发板的 shell!

下一步:应用集成

现在 Rootfs 已经通过 NFS 成功挂载,系统可以正常启动了。但是,这个 Rootfs 还是最小化的,只有 BusyBox 提供的基本命令。实际应用中,我们还需要添加更多的命令、库文件、自定义程序等。

下一章,我们将讲解:

  • 如何添加更多常用命令和工具
  • 静态链接 vs 动态链接的选择
  • 如何处理库文件依赖
  • 如何添加自定义应用程序
  • 如何配置系统启动服务

准备好了吗?让我们继续完善这个 Rootfs!

相关推荐
头疼的程序员2 小时前
计算机网络:自顶向下方法(第七版)第七章 学习分享(四)
网络·学习·计算机网络
如雨随行20202 小时前
【Vim】学习笔记(8)tips-2
笔记·学习·vim
Wu Junwu2 小时前
Linux启动过程中的initramfs和rootfs详解
linux·根文件系统·内核编译
tiantianuser2 小时前
RDMA设计63:怎么进行网络嗅探功能测试
网络·fpga开发·rdma·高速传输·cmac·roce v2
HyperAI超神经2 小时前
在线教程丨华中科大与小红书 hi lab开源dots.mocr,SOTA级OCR模型完美还原文档结构,图形也能转 SVG
人工智能·深度学习·学习·机器学习·gpu·orc·vllm
zzzsde2 小时前
【Linux】库的制作与使用(1):库的概念及动静态库
linux·运维·服务器
软件资深者2 小时前
iVentoy 完整使用教程:一根网线批量装系统,增强版 PXE 服务器一键部署
运维·服务器·网络·网络备份·网刻·网络安装系统
可乐鸡翅好好吃2 小时前
BLE服务和Freertos的任务(Task)、函数有什么区别
网络·单片机·嵌入式硬件
2601_949814692 小时前
ubuntu 安装 Redis
linux·redis·ubuntu