ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

RK3568嵌入式Linux开发:NFS rootfs挂载实战与调试效率提升

2026/9/19 8:58:19 拓冰建站 浏览量
RK3568嵌入式Linux开发:NFS rootfs挂载实战与调试效率提升 1. 为什么要在 RK3568 上折腾 NFS rootfs做过嵌入式 Linux 开发的人都有一个共识板子调试阶段最烦的事情不是写驱动而是每次改一行代码就要重新编译镜像、烧录 eMMC、重启验证。一个来回少则五六分钟多则十几分钟一天下来光烧录就耗掉小半天。NFS rootfs 就是来解决这个痛点的——根文件系统放在宿主机的一个目录里开发板通过网络挂载它你在宿主机上改什么板子重启后立刻生效连重新打包都省了。RK3568 这颗芯片这两年热度一直不低四核 A55 加 Mali-G52自带千兆网口和 PCIe跑 Linux 做工业网关、边缘计算盒子、EtherCAT 主站都很合适。正点原子的 RK3568 开发板用户量尤其大配套资料也全。但说实话官方文档里关于 NFS 挂载 rootfs 的部分写得比较简略很多细节——比如 uboot 阶段怎么传参、内核里哪些选项必须编进去、设备树里网口 PHY 怎么配——都得自己踩一遍才知道。这篇内容就是把我自己在 RK3568 上从零配通 NFS rootfs 的完整过程整理出来。从 uboot 环境变量设置、内核配置项勾选、设备树网口节点确认到宿主机 NFS 服务搭建、TFTP 传内核和设备树、最后挂载验证和常见问题排查每一步都给出具体命令和参数。适合正在用 RK3568 做驱动开发、内核裁剪或者应用调试的工程师参考也适合刚接触嵌入式 Linux 想搞明白启动流程的朋友。整个流程我在正点原子 RK3568 开发板上验证过配合千兆网口和常见交换机环境都能稳定跑起来。2. 整体方案设计与启动链路拆解2.1 NFS rootfs 到底解决了什么问题先把这个方案的本质说清楚。正常启动流程是BootROM 加载 ubootuboot 从存储介质eMMC/SD读取内核和设备树到内存内核启动后挂载存储介质上的根文件系统。而 NFS rootfs 方案里根文件系统不在本地存储上而是通过网络从宿主机挂载。这意味着什么你编译出来的 rootfs 目录直接就是板子运行时的根目录。改一个配置文件、换一个应用程序、调一个脚本宿主机上改完板子重启就生效。对于驱动调试阶段需要频繁修改/etc下配置、替换测试程序、查看日志的场景效率提升非常明显。代价也有板子必须联网启动依赖宿主机 NFS 服务网络不通就起不来。所以这个方案定位很明确——开发调试阶段用量产肯定还是烧到 eMMC 或 SD 卡里。2.2 启动链路里每个环节的分工RK3568 的启动链路比很多 MCU 复杂理清楚每个环节谁负责什么后面配的时候才不会乱。BootROM芯片上电后固化代码根据启动引脚选择从 eMMC、SD 卡还是其他介质加载 uboot。这一步我们改不了。ubootSPL proper初始化 DDR、时钟、基本外设然后负责把内核镜像和设备树从某处加载到内存。在 NFS 方案里内核和设备树通常通过 TFTP 从宿主机下载也可以从本地存储读。内核启动后初始化网络设备然后根据bootargs里的root参数决定根文件系统从哪挂。NFS 方案里就是root/dev/nfs。NFS 服务宿主机上运行导出 rootfs 目录等待板子挂载。关键点在于uboot 必须把正确的bootargs传给内核这里面包含 NFS 服务器 IP、板子自身 IP、挂载路径、NFS 版本等参数。参数错一个字符内核就挂不上根文件系统直接 panic。2.3 为什么选 TFTP NFS 组合而不是其他方式传输内核和设备树的方式有好几种从 eMMC 读、从 SD 卡读、通过 TFTP 下载、通过 USB 下载。调试阶段最常用的是 TFTP原因很简单——改完内核重新编译出Image和.dtb直接放到 TFTP 目录板子重启就加载新的不用烧录。NFS 负责 rootfsTFTP 负责 kernel 和 dtb两者配合就是嵌入式调试的黄金组合。也有用 NFS 同时传内核的但 uboot 阶段 NFS 支持不如 TFTP 成熟而且 TFTP 配置更简单所以主流做法还是分开。注意TFTP 和 NFS 用的是同一张网卡所以 uboot 阶段和内核阶段的网络配置必须一致否则会出现 uboot 能下载内核但内核挂不上 NFS 的诡异现象。3. 宿主机环境搭建与 NFS 服务配置3.1 安装 NFS 服务和 TFTP 服务宿主机我用的是 Ubuntu 20.04其他发行版命令略有差异但思路一样。先装包sudo apt update sudo apt install nfs-kernel-server tftp-hpa tftpd-hpanfs-kernel-server是 NFS 服务端tftpd-hpa是 TFTP 服务端。装完之后确认服务状态sudo systemctl status nfs-kernel-server sudo systemctl status tftpd-hpa如果没起来多半是配置文件有问题先别急往下配。3.2 配置 TFTP 目录和权限TFTP 默认目录在/var/lib/tftpboot我习惯改成自己工作目录下的tftpboot方便管理。编辑/etc/default/tftpd-hpaTFTP_USERNAMEtftp TFTP_DIRECTORY/home/yourname/tftpboot TFTP_ADDRESS0.0.0.0:69 TFTP_OPTIONS--secure --create--create允许上传文件调试时偶尔需要从板子回传东西。建目录、给权限mkdir -p /home/yourname/tftpboot chmod 777 /home/yourname/tftpboot sudo systemctl restart tftpd-hpa把编译好的Image和rk3568-xxx.dtb拷进去后面 uboot 就从这里下载。3.3 配置 NFS 导出目录NFS 导出配置在/etc/exports加一行/home/yourname/nfsroot *(rw,sync,no_root_squash,no_subtree_check)几个参数解释一下这些是实际配的时候容易搞错的地方rw读写权限调试阶段必须的否则板子上改不了文件。sync同步写入保证数据一致性调试时更安全。no_root_squash允许客户端 root 用户保持 root 权限。这个很关键因为板子启动时是以 root 身份挂载的如果 squash 了很多系统文件权限会出问题。no_subtree_check关闭子树检查提升兼容性避免一些奇怪的挂载失败。改完执行sudo exportfs -ra sudo systemctl restart nfs-kernel-server用showmount -e localhost确认导出列表里有你的目录。3.4 rootfs 目录的准备NFS 导出的目录里要放一个完整的根文件系统。可以直接用 SDK 编译出来的rootfs目录也可以用 Buildroot 或 Yocto 生成的。关键是目录结构要完整/bin、/sbin、/etc、/lib、/dev、/proc、/sys这些都得有。我一般会把 SDK 编译出的 rootfs 整个拷过来然后确认几个点/etc/inittab或 systemd 配置存在能正常启动 init。/lib/modules/$(uname -r)下的内核模块和当前内核版本匹配否则驱动加载会失败。/etc/fstab里不要有强制挂载本地 rootfs 的条目否则会卡住。实操心得第一次配的时候我直接拿了个不完整的 rootfs结果内核挂载成功后 init 起不来串口一直刷 Kernel panic - not syncing: No init found。后来发现是/sbin/init软链接断了。所以 rootfs 目录一定要用编译完整的别手动拼。4. 内核配置与设备树确认4.1 内核必须打开的配置项内核要支持 NFS rootfs几个配置项必须编进去。进入内核源码目录make menuconfigmake ARCHarm64 menuconfig需要确认的选项配置项位置作用CONFIG_NFS_FSFile systems - Network File SystemsNFS 客户端支持CONFIG_NFS_V3同上NFSv3 协议支持CONFIG_NFS_V4同上NFSv4 协议支持可选CONFIG_ROOT_NFS同上允许 NFS 作为根文件系统CONFIG_IP_PNPNetworking support - Networking options内核启动时自动配置 IPCONFIG_IP_PNP_DHCP同上支持 DHCP 获取 IPCONFIG_IP_PNP_BOOTP同上支持 BOOTPCONFIG_E1000E 或对应网卡驱动Device Drivers - Network device supportRK3568 千兆网口驱动CONFIG_ROOT_NFS是最关键的没有它内核根本不认root/dev/nfs。CONFIG_IP_PNP系列让内核在启动早期就能配好网络否则 NFS 挂载时网卡还没起来。RK3568 的千兆网口一般是 GMAC对应驱动是stmmac系列确认CONFIG_STMMAC_ETH和相关 PHY 驱动都编进去了。4.2 设备树里网口节点的确认设备树这块是很多人卡住的地方。RK3568 的 GMAC 节点在arch/arm64/boot/dts/rockchip/rk3568.dtsi里定义具体板级配置在对应的rk3568-xxx.dts里。需要确认的点PHY 地址和复位引脚不同板子 PHY 挂的 MDIO 地址可能不同复位 GPIO 也不一样。正点原子板子的配置一般在厂商提供的 dts 里已经写好了但如果你自己做的板子这里必须对着原理图改。时钟和电源域GMAC 需要正确的时钟源和电源域配置否则网口起不来。PHY 模式RGMII 还是 RMIIdelay 参数对不对。RK3568 一般用 RGMIITX/RX delay 配错会导致网口能识别但丢包严重。一个典型的 GMAC 节点长这样gmac1 { phy-mode rgmii; clock_in_out output; snps,reset-gpio gpio3 RK_PB7 GPIO_ACTIVE_LOW; snps,reset-active-low; snps,reset-delays-us 0 20000 100000; assigned-clocks cru SCLK_GMAC1_RX_TX; assigned-clock-parents cru SCLK_GMAC1_RGMII_SPEED; pinctrl-names default; pinctrl-0 gmac1m1_miim gmac1m1_tx_bus2 gmac1m1_rx_bus2 gmac1m1_rgmii_clk gmac1m1_rgmii_bus; phy-handle rgmii_phy1; status okay; };如果你遇到 uboot 阶段千兆网不通的问题八成是这里 delay 参数或者 PHY 复位时序不对。可以先在 uboot 里用ping命令测试通了再往下走。4.3 uboot 网络配置的确认uboot 阶段也要能通网因为内核和设备树是通过 TFTP 下载的。RK3568 的 uboot 里网络驱动配置在configs/rk3568_defconfig和相关板级文件里。确认几个点CONFIG_NET、CONFIG_CMD_NET、CONFIG_CMD_PING、CONFIG_CMD_TFTPBOOT都打开。GMAC 驱动在 uboot 里也要正确初始化PHY 地址和内核设备树里保持一致。如果 uboot 里网口不通先检查 PHY 复位和时钟再看 MDIO 读写是否正常。踩坑记录我遇到过 uboot 里ping不通但内核起来后网口正常的情况最后发现是 uboot 的 GMAC 驱动里 RGMII delay 配置和内核不一致。uboot 和内核的设备树是两套改的时候两边都要改别只改一边。5. uboot 环境变量设置与启动流程5.1 关键环境变量逐个说明uboot 环境变量决定了内核怎么启动、rootfs 从哪挂。RK3568 上一般通过串口进 uboot 命令行用setenv设置saveenv保存。核心变量setenv ipaddr 192.168.1.100 # 板子 IP setenv serverip 192.168.1.10 # 宿主机 IP setenv gatewayip 192.168.1.1 # 网关 setenv netmask 255.255.255.0 # 子网掩码 setenv hostname rk3568 # 主机名然后是启动相关setenv bootargs consolettyS2,1500000n8 root/dev/nfs rw nfsroot192.168.1.10:/home/yourname/nfsroot,nfsvers3 ip192.168.1.100:192.168.1.10:192.168.1.1:255.255.255.0::eth0:off这条bootargs是核心拆开看consolettyS2,1500000n8串口控制台RK3568 一般用 ttyS2波特率 1500000。root/dev/nfs告诉内核根文件系统用 NFS。rw根文件系统读写挂载。nfsroot服务器IP:导出路径,nfsvers3NFS 服务器地址和路径指定 NFSv3 协议。ip板子IP:服务器IP:网关:掩码::网卡:off内核启动时的网络配置off表示不用 DHCP/BOOTP。ip这个参数格式很容易写错中间的空字段用冒号占位一个都不能少。5.2 内核和设备树的加载命令设置好网络后用 TFTP 下载内核和设备树到内存setenv kernel_addr 0x02080000 setenv fdt_addr 0x0a100000 tftp ${kernel_addr} Image tftp ${fdt_addr} rk3568-atk.dtb booti ${kernel_addr} - ${fdt_addr}booti是 arm64 的启动命令参数依次是内核地址、initrd 地址没有就写-、设备树地址。也可以把这些写成一个bootcmd开机自动执行setenv bootcmd tftp ${kernel_addr} Image; tftp ${fdt_addr} rk3568-atk.dtb; booti ${kernel_addr} - ${fdt_addr} saveenv5.3 启动参数传递的验证方法设置完别急着重启先在 uboot 里验证网络ping 192.168.1.10通了再tftp下载测试。下载成功会显示文件大小和传输速率。启动后如果内核 panic重点看串口打印的bootargs那一行确认参数有没有被正确传递。有时候 uboot 里设了但没saveenv重启就丢了。注意nfsvers3建议显式指定。有些宿主机默认只开 NFSv4而内核如果没编 v4 支持就会挂载失败。指定 v3 兼容性最好。6. 挂载验证与常见问题排查6.1 正常启动的串口日志特征配通之后串口日志会依次出现这些关键信息[ 0.000000] Kernel command line: consolettyS2,1500000n8 root/dev/nfs rw nfsroot... [ 2.345678] stmmaceth fe010000.ethernet eth0: Link is Up - 1Gbps/Full [ 3.456789] IP-Config: Complete: [ 3.567890] deviceeth0, addr192.168.1.100, mask255.255.255.0 [ 4.567890] Root-NFS: Server returned error -13如果看到Root-NFS: Server returned error就是挂载失败错误码能帮你定位问题。-13一般是权限问题检查no_root_squash和目录权限。挂载成功后会出现[ 5.678901] VFS: Mounted root (nfs filesystem) on device 0:15. [ 5.789012] devtmpfs: mounted [ 5.890123] Run /sbin/init as init process看到Run /sbin/init就说明 rootfs 挂上了init 开始跑。6.2 常见问题速查表现象可能原因排查方法uboot ping 不通PHY 复位/时钟/delay 配置错检查设备树 GMAC 节点示波器看 PHY 时钟TFTP 下载超时防火墙拦截或服务没起systemctl status tftpd-hpa关防火墙内核 panic: No init foundrootfs 不完整或 init 缺失检查/sbin/init是否存在且可执行Root-NFS: Server returned error -13NFS 权限问题确认no_root_squash目录权限 777Root-NFS: Unable to mount网络不通或路径错内核起来后ifconfig看 IPping宿主机挂载成功但卡住fstab 或 init 配置问题检查/etc/fstab用init/bin/sh调试NFS 版本不匹配宿主机只开 v4内核只支持 v3显式指定nfsvers3或宿主机开 v36.3 几个容易忽略的细节防火墙Ubuntu 默认 ufw 可能开着NFS 和 TFTP 的端口会被拦。调试阶段直接sudo ufw disable或者放行 2049、69、111 端口。网段一致性板子 IP、宿主机 IP、网关必须在同一网段。我见过有人板子设 192.168.1.100宿主机是 192.168.0.10怎么都不通。NFS 服务重启后导出失效改完/etc/exports一定要exportfs -ra光重启服务有时候不生效。内核模块版本NFS rootfs 挂上后如果/lib/modules下的模块和当前内核版本不匹配驱动加载会失败。编译内核后记得同步更新 rootfs 里的模块。串口波特率RK3568 默认 1500000有些 USB 转串口线不支持这么高的波特率会乱码。换 FT232 或 CP2102 芯片的线。实操心得调试 NFS 挂载失败时最有效的办法是在bootargs里加init/bin/sh这样即使 init 起不来也能进 shell手动mount看看到底哪一步出问题。另外nfsrootdebug参数能让内核打印更详细的 NFS 挂载日志排查时很有用。7. 调试效率提升的几个实用技巧7.1 用脚本一键切换启动方式调试阶段经常要在 NFS 启动和本地启动之间切换。可以在 uboot 里存两套bootargs用脚本切换setenv bootargs_nfs consolettyS2,1500000n8 root/dev/nfs rw nfsroot... setenv bootargs_local consolettyS2,1500000n8 root/dev/mmcblk0p5 rw setenv boot_nfs setenv bootargs ${bootargs_nfs}; run bootcmd_common setenv boot_local setenv bootargs ${bootargs_local}; run bootcmd_common这样run boot_nfs和run boot_local一键切换不用每次重敲参数。7.2 宿主机上快速更新 rootfsNFS rootfs 最大的好处就是改完即生效。我一般会在宿主机上写个同步脚本把编译输出的新程序自动拷到 nfsroot 对应目录#!/bin/bash cp build/myapp /home/yourname/nfsroot/usr/bin/ cp -r config/* /home/yourname/nfsroot/etc/ echo rootfs updated板子上直接重启应用或者重启板子就能验证省去打包烧录的时间。7.3 网络抓包辅助排查遇到 NFS 挂载诡异问题时在宿主机上抓包看交互过程sudo tcpdump -i eth0 -nn port 2049 or port 111能看到板子有没有发出 NFS 请求、服务器有没有响应、卡在哪一步。比单纯看串口日志直观得多。7.4 关于 EtherCAT 等实时应用的补充有些朋友用 RK3568 跑 EtherCAT 主站比如 IgH 主站对网络实时性要求高。NFS rootfs 调试阶段用没问题但正式跑实时任务时建议还是切回本地 rootfs避免网络抖动影响实时性。调试阶段可以用 NFS 快速迭代应用逻辑验证稳定后再固化到 eMMC。另外如果板子要接 EtherCAT 从站注意网口分配——一个网口跑 NFS 调试另一个网口跑 EtherCAT别混用。RK3568 一般有两个 GMAC正好分开。8. 写在最后的一些个人体会这套 NFS rootfs 方案我从 RK3288 时代就开始用换到 RK3568 上整体思路没变但细节上坑更多——主要是 RK3568 的 GMAC 配置比老平台复杂uboot 和内核两套设备树容易改漏。我的建议是第一次配的时候别贪快uboot 阶段先把ping和tftp调通再进内核阶段调 NFS 挂载一层一层来出问题也好定位。还有一点NFS rootfs 虽然方便但别把它当成万能方案。它依赖网络启动速度比本地 rootfs 慢而且宿主机一关板子就起不来。所以我的习惯是驱动开发和内核裁剪阶段用 NFS应用联调和稳定性测试阶段切回本地 rootfs两套环境都保留按需切换。最后分享一个小技巧如果你的开发环境有多块 RK3568 板子可以给每块板子分配不同的 IP 和不同的 nfsroot 目录宿主机上同时导出多个目录这样多板并行调试互不干扰。配合前面说的 uboot 脚本切换效率能再上一个台阶。