1. 为什么要在 RK3568 上折腾 NFS 根文件系统
做嵌入式 Linux 开发的同行大概都有过这种体验:每次改一行应用代码,就要重新编译、打包镜像、烧录 eMMC,然后等板子重启,一套流程下来少说五分钟。一天改二十次代码,两个小时就耗在烧录和等待上了。这种开发节奏,效率低得让人抓狂。
NFS rootfs 就是来解决这个问题的。它的核心思路很简单:板子启动时,内核不再从本地存储(eMMC、SD 卡)加载根文件系统,而是通过网络挂载宿主机上某个目录作为自己的根目录。这样一来,你在宿主机上编译好的程序,直接丢进那个目录,板子重启就能用,连拷贝都省了。改代码、编译、重启、验证,整个循环压缩到几十秒以内。
RK3568 是瑞芯微的一款四核 Cortex-A55 处理器,主频 2.0GHz,带 NPU、支持双千兆网口、PCIe、USB3.0 等一堆外设,在工业网关、边缘计算、车载终端这些场景里用得很多。正点原子的 RK3568 开发板是市面上比较常见的一款,资料相对齐全,适合拿来入门和做项目验证。但说实话,官方 SDK 默认的配置是面向量产的,NFS 调试环境需要自己从头搭一遍,中间涉及 U-Boot 环境变量、内核配置、网络参数、NFS 服务端配置等好几个环节,任何一个环节出问题,板子就卡在启动阶段,屏幕上要么一片黑,要么停在某个报错信息上不动。
我前后在 RK3568 上搭过三套 NFS 调试环境,踩过的坑包括但不限于:U-Boot 的bootargs里 IP 地址写错导致内核找不到 NFS 服务器、内核编译时忘了勾选 NFS 客户端支持、NFS 服务端版本和内核挂载参数不匹配、TFTP 传内核镜像时网络不通等等。每次排查都要花不少时间,因为启动阶段的报错信息往往很隐晦,有时候就是一行VFS: Unable to mount root fs,具体原因得靠经验去猜。
这篇内容就是把这些经验整理出来,从 U-Boot 环境变量配置、TFTP 服务搭建、内核配置、NFS 服务端配置到最终调试,按实际操作的顺序一步步讲清楚。适合正在用 RK3568 做开发、想搭 NFS 调试环境的同行参考,也适合其他嵌入式平台做类似配置时借鉴思路。整个流程我已经在正点原子 RK3568 开发板上完整验证过,按这个步骤走,基本可以一次跑通。
2. 搭建前的环境准备与网络拓扑确认
2.1 硬件连接与网络规划
在动手改任何配置之前,先把物理连接和网络规划理清楚。NFS rootfs 的本质是板子通过网络从宿主机拉取根文件系统,所以板子和宿主机之间必须网络互通。最稳妥的方式是用一根网线把板子的千兆网口和宿主机的网口直连,或者两者都接到同一个交换机/路由器上。
我推荐直连的方式,原因有两个:一是直连不依赖外部网络环境,在办公室、实验室、家里都能用;二是直连可以省去路由器 DHCP 分配的变量,IP 地址完全由自己控制,排查问题时少一个不确定因素。
网络规划建议如下:
| 角色 | IP 地址 | 说明 |
|---|---|---|
| 宿主机(Ubuntu) | 192.168.1.100 | NFS 服务端 + TFTP 服务端 |
| RK3568 开发板 | 192.168.1.200 | NFS 客户端 |
| 子网掩码 | 255.255.255.0 | 同一网段 |
| 网关 | 192.168.1.1 | 直连时可随意填,不影响 NFS 挂载 |
这里有个细节需要注意:宿主机的 IP 地址必须是静态的,不能是 DHCP 动态获取的。因为 U-Boot 的bootargs里写死了 NFS 服务器的 IP,如果宿主机 IP 变了,板子就挂载不上了。在 Ubuntu 上设置静态 IP 可以通过图形界面的网络设置,也可以直接改/etc/netplan/下的配置文件。我习惯用 netplan,配置示例如下:
network: version: 2 ethernets: enp3s0: addresses: - 192.168.1.100/24 dhcp4: no改完之后执行sudo netplan apply生效。注意网卡名称enp3s0要根据自己机器的实际情况改,用ip addr命令可以查看。
2.2 宿主机必备软件安装
宿主机上需要装两个服务:TFTP 和 NFS。TFTP 用来给板子传内核镜像和设备树文件,NFS 用来提供根文件系统。这两个服务在 Ubuntu 上安装都很简单:
sudo apt update sudo apt install tftp-hpa tftpd-hpa nfs-kernel-server -y装完之后先别急着配置,确认一下版本信息。NFS 服务端的版本会影响板子挂载时使用的协议版本,这个后面会详细讲。
dpkg -l | grep nfs正常情况下会看到nfs-kernel-server的版本号,Ubuntu 20.04 一般是 1.3.4 左右,Ubuntu 22.04 会更新一些。版本本身不是大问题,关键是配置时要明确指定支持的 NFS 协议版本。
2.3 确认板子的 U-Boot 支持网络启动
RK3568 的 U-Boot 默认是支持网络功能的,但不同厂家的 SDK 配置可能有差异。上电后进入 U-Boot 命令行,执行ping命令测试网络:
=> ping 192.168.1.100如果能看到host 192.168.1.100 is alive之类的回复,说明 U-Boot 的网络驱动工作正常。如果提示No ethernet found或者ping failed,那就要先排查网络驱动的问题。正点原子的 RK3568 板子用的是 Realtek 的千兆 PHY,U-Boot 里一般已经适配好了,但如果你用的是其他厂家的板子,可能需要确认设备树里 PHY 的配置是否正确。
有个同行遇到过fmql uboot 千兆网不通的问题,最后发现是 PHY 的复位引脚配置不对,设备树里改一下就好了。所以如果 ping 不通,先检查硬件连接,再检查 U-Boot 的设备树配置。
3. TFTP 服务端配置与内核镜像传输
3.1 TFTP 服务配置的细节
TFTP 服务的配置文件在/etc/default/tftpd-hpa,默认内容大概是这样的:
TFTP_USERNAME="tftp" TFTP_DIRECTORY="/var/lib/tftpboot" TFTP_ADDRESS=":69" TFTP_OPTIONS="--secure"我一般会把 TFTP 的根目录改到自己习惯用的路径,比如/home/username/tftpboot,这样拷贝文件方便。改完之后是这样的:
TFTP_USERNAME="tftp" TFTP_DIRECTORY="/home/username/tftpboot" TFTP_ADDRESS="0.0.0.0:69" TFTP_OPTIONS="--secure --create"这里有几个点要说明。TFTP_ADDRESS改成0.0.0.0:69表示监听所有网卡的 69 端口,默认的:69在某些系统上可能只监听 IPv6,导致板子连不上。--create选项允许上传文件,虽然我们主要用下载功能,但加上也无妨。
改完配置后创建目录并设置权限:
mkdir -p /home/username/tftpboot chmod 777 /home/username/tftpboot sudo systemctl restart tftpd-hpa sudo systemctl status tftpd-hpa状态显示active (running)就说明服务起来了。可以在 TFTP 目录里放一个测试文件,然后在宿主机上用tftp命令自己连自己测试一下:
echo "hello" > /home/username/tftpboot/test.txt tftp 192.168.1.100 tftp> get test.txt tftp> quit如果能下载下来,说明 TFTP 服务配置没问题。
3.2 内核镜像与设备树的准备
RK3568 的 SDK 编译完成后,会在kernel/arch/arm64/boot/目录下生成Image文件,在kernel/arch/arm64/boot/dts/rockchip/目录下生成.dtb文件。把这两个文件拷贝到 TFTP 目录:
cp kernel/arch/arm64/boot/Image /home/username/tftpboot/ cp kernel/arch/arm64/boot/dts/rockchip/rk3568-atk-evb.dtb /home/username/tftpboot/注意 dtb 文件名要根据自己板子的实际型号来,正点原子的 RK3568 开发板对应的 dtb 一般是rk3568-atk-evb.dtb或者类似的命名。如果不确定,可以在 SDK 的device/rockchip/rk3568/目录下找找配置文件中指定的 dtb 名称。
这里有个经验:每次重新编译内核后,记得把新的 Image 和 dtb 重新拷贝到 TFTP 目录。我有时候改完内核直接重启板子,忘了拷贝,结果板子跑的还是旧内核,排查半天才发现问题。后来我写了个小脚本,编译完自动拷贝:
#!/bin/bash cp kernel/arch/arm64/boot/Image /home/username/tftpboot/ cp kernel/arch/arm64/boot/dts/rockchip/rk3568-atk-evb.dtb /home/username/tftpboot/ echo "Copy done."放在 SDK 根目录下,编译完执行一下就行,省心不少。
3.3 U-Boot 中 TFTP 传输的验证
在 U-Boot 命令行里,先用ping确认网络通,然后用tftp命令拉取内核镜像:
=> tftp 0x02080000 Image地址0x02080000是 RK3568 上内核加载的常用地址,具体值可以参考 SDK 里的环境变量设置。传输成功后,U-Boot 会显示传输的文件大小和耗时。如果传输失败,常见原因有:
- TFTP 服务没启动或者防火墙挡住了 69 端口
- 宿主机 IP 和板子 IP 不在同一网段
- 文件名写错或者文件不在 TFTP 目录里
防火墙的问题在 Ubuntu 上比较常见,默认的 ufw 可能会拦截 TFTP 的流量。可以临时关闭防火墙测试:
sudo ufw disable如果关闭后能传输,那就需要添加规则允许 69 端口,而不是一直关着防火墙。
4. NFS 服务端配置与根文件系统准备
4.1 NFS 导出目录的配置
NFS 服务端的配置文件是/etc/exports,在里面添加一行:
/home/username/nfsroot 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)这一行的含义逐项解释一下:
/home/username/nfsroot:要导出的目录,也就是板子的根文件系统所在目录192.168.1.0/24:允许访问的网段,也可以写具体的 IPrw:读写权限,调试阶段需要写权限,方便在板子上直接改文件sync:同步写入,数据更安全,但性能略低。调试阶段建议用 sync,避免断电丢数据no_subtree_check:关闭子树检查,减少不必要的开销,提高兼容性no_root_squash:允许客户端的 root 用户保持 root 权限,这个很重要,否则板子上的 root 用户会被映射成匿名用户,很多操作会权限不足
改完 exports 文件后,执行:
sudo exportfs -ra sudo systemctl restart nfs-kernel-server可以用showmount -e localhost查看导出列表,确认配置生效。
4.2 根文件系统的制作与放置
RK3568 的 SDK 一般会提供 Buildroot 或 Yocto 构建的根文件系统,也可能提供 Ubuntu 的根文件系统。不管哪种,最终都需要得到一个完整的根文件系统目录,里面包含bin、sbin、lib、etc、usr等标准目录。
如果 SDK 提供的是.img镜像文件,需要先挂载或者解压出来:
mkdir /mnt/rootfs sudo mount -o loop rootfs.img /mnt/rootfs sudo cp -a /mnt/rootfs/* /home/username/nfsroot/ sudo umount /mnt/rootfs如果 SDK 直接提供了根文件系统的压缩包,解压到 NFS 目录即可:
sudo tar -xvf rootfs.tar.gz -C /home/username/nfsroot/放置完成后,检查一下关键目录是否存在:
ls /home/username/nfsroot/应该能看到bin、dev、etc、home、lib、proc、root、sbin、sys、tmp、usr、var这些目录。如果缺少dev、proc、sys这些目录,需要手动创建:
sudo mkdir -p /home/username/nfsroot/{dev,proc,sys,tmp} sudo chmod 1777 /home/username/nfsroot/tmptmp目录的权限 1777 表示所有用户可读写,但只有文件所有者能删除自己的文件,这是标准做法。
4.3 NFS 版本兼容性问题的处理
这是最容易踩坑的地方。不同版本的 NFS 协议在挂载参数上有差异,如果内核挂载时指定的版本和服务端支持的不一致,就会挂载失败。
Linux 内核支持 NFSv2、NFSv3、NFSv4,RK3568 的 BSP 内核一般默认支持 NFSv3 和 NFSv4。Ubuntu 的 nfs-kernel-server 默认同时支持 v3 和 v4。但有时候内核配置里只勾选了部分版本,导致挂载时报Protocol not supported。
在 U-Boot 的bootargs里,可以通过nfsvers参数指定 NFS 版本:
nfsroot=192.168.1.100:/home/username/nfsroot,nfsvers=3我建议先用nfsvers=3,因为 v3 的兼容性最好,配置也最简单。如果 v3 不行再试 v4。v4 的配置稍微复杂一些,涉及到fsid和nfsroot路径的映射问题。
另外,内核配置里需要确保以下选项被勾选:
CONFIG_NFS_FS=y CONFIG_NFS_V3=y CONFIG_NFS_V3_ACL=y CONFIG_NFS_V4=y CONFIG_ROOT_NFS=yCONFIG_ROOT_NFS=y这个选项特别关键,它允许内核直接挂载 NFS 作为根文件系统。如果这个没勾,内核启动时会报VFS: Cannot open root device "nfs" or unknown-block(0,255)。
5. 内核配置中与 NFS 启动相关的关键选项
5.1 网络驱动与根文件系统选项
RK3568 的内核配置里,和 NFS 启动直接相关的选项分布在几个不同的菜单里。用make menuconfig进入配置界面,需要确认以下选项:
网络设备支持:
Device Drivers ---> Network device support ---> Ethernet driver support ---> <*> Rockchip GMAC ethernet support这个选项确保内核启动后能驱动板子上的千兆网口。如果这个没勾,内核启动后网络不通,NFS 挂载自然失败。
网络文件系统:
File systems ---> Network File Systems ---> <*> NFS client support <*> NFS client support for NFS version 3 <*> NFS client support for NFS version 4 <*> Root file system on NFS这几项必须全部勾选。Root file system on NFS就是前面提到的CONFIG_ROOT_NFS,它是内核能够把 NFS 目录当作根文件系统的前提。
IP 自动配置:
Networking support ---> Networking options ---> <*> IP: kernel level autoconfiguration <*> IP: DHCP support <*> IP: BOOTP support如果bootargs里用的是ip=dhcp,那 DHCP 支持必须勾选。如果用的是静态 IP 配置,比如ip=192.168.1.200:192.168.1.100::255.255.255.0::eth0:off,那 BOOTP 支持可以不勾,但 IP 自动配置这一项还是要勾上。
5.2 内核命令行参数的传递机制
U-Boot 通过bootargs环境变量把参数传给内核,内核解析这些参数来决定根文件系统从哪里挂载。一个典型的 NFS 启动bootargs是这样的:
setenv bootargs 'console=ttyS2,1500000n8 root=/dev/nfs rw nfsroot=192.168.1.100:/home/username/nfsroot,nfsvers=3 ip=192.168.1.200:192.168.1.100:192.168.1.1:255.255.255.0::eth0:off'逐段解释:
console=ttyS2,1500000n8:指定串口控制台,RK3568 一般用 ttyS2,波特率 1500000root=/dev/nfs:告诉内核根文件系统是 NFSrw:根文件系统以读写方式挂载nfsroot=192.168.1.100:/home/username/nfsroot,nfsvers=3:NFS 服务器 IP 和导出路径,指定 NFS 版本为 3ip=192.168.1.200:192.168.1.100:192.168.1.1:255.255.255.0::eth0:off:板子的 IP、服务器 IP、网关、掩码、网卡名、自动配置关闭
这个ip=参数的格式是固定的:客户端IP:服务器IP:网关:掩码:主机名:网卡名:自动配置。中间的主机名可以留空,自动配置写off表示使用静态 IP。
5.3 设备树中网络相关的配置检查
RK3568 的设备树里,以太网节点的配置直接影响内核启动后网络能否正常工作。以正点原子的板子为例,设备树里一般会有这样的节点:
&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"; };这里有几个关键点:phy-mode要匹配实际的硬件连接方式(RGMII 或 RMII),snps,reset-gpio要对应正确的 GPIO 引脚,status必须是okay。如果这些配置有误,内核启动后网口不工作,NFS 挂载就会超时失败。
有个同行遇到过rk3568 uboot 千兆网不通的问题,最后发现是设备树里 PHY 的复位 GPIO 写错了,改过来就好了。所以如果 U-Boot 阶段 ping 不通,先查设备树里的网络节点配置。
6. U-Boot 环境变量配置与启动流程串联
6.1 环境变量的设置与保存
在 U-Boot 命令行里,需要设置几个关键的环境变量:
setenv ipaddr 192.168.1.200 setenv serverip 192.168.1.100 setenv gatewayip 192.168.1.1 setenv netmask 255.255.255.0 setenv bootargs 'console=ttyS2,1500000n8 root=/dev/nfs rw nfsroot=192.168.1.100:/home/username/nfsroot,nfsvers=3 ip=192.168.1.200:192.168.1.100:192.168.1.1:255.255.255.0::eth0:off' setenv bootcmd 'tftp 0x02080000 Image; tftp 0x0a100000 rk3568-atk-evb.dtb; booti 0x02080000 - 0x0a100000' saveenvbootcmd的含义是:先从 TFTP 服务器下载内核镜像到内存地址0x02080000,再下载设备树到0x0a100000,然后用booti命令启动内核,把设备树地址传给它。
saveenv把环境变量保存到存储介质里,下次上电自动生效。如果不执行saveenv,重启后设置就丢了。
6.2 启动流程的完整串联
设置好环境变量后,执行run bootcmd或者直接reset,板子就会按照以下流程启动:
- U-Boot 初始化网络,通过 TFTP 下载内核镜像和设备树到内存
- U-Boot 把
bootargs传递给内核,跳转到内核入口 - 内核启动,初始化网络驱动,根据
ip=参数配置网卡 IP - 内核根据
nfsroot=参数,向 NFS 服务器发起挂载请求 - 挂载成功后,内核执行根文件系统里的
/sbin/init,系统正常启动
整个流程中,任何一步出错都会导致启动失败。常见的报错和对应原因:
| 报错信息 | 可能原因 |
|---|---|
TFTP error: 'File not found' | 文件名写错或文件不在 TFTP 目录 |
VFS: Unable to mount root fs | NFS 服务未启动、IP 不对、导出路径不对 |
NFS: Protocol not supported | NFS 版本不匹配 |
IP-Config: Failed to open eth0 | 内核网络驱动未加载或设备树配置错误 |
Kernel panic - not syncing: VFS | 根文件系统挂载失败,检查以上所有项 |
6.3 调试过程中的实用技巧
调试 NFS 启动时,串口终端是唯一的信息来源。建议把串口的日志级别调高,让内核输出更多信息。可以在bootargs里加loglevel=8或者ignore_loglevel,这样能看到内核的详细日志。
另外,如果 NFS 挂载失败,内核会进入 panic 状态,板子就卡住了。这时候可以按复位键重启,回到 U-Boot 命令行,修改bootargs再试。不要每次都重新烧录,那样太浪费时间。
还有一个技巧:在宿主机上用tcpdump抓包,可以看到板子和 NFS 服务器之间的通信过程。如果板子发了挂载请求但服务器没响应,或者响应了但板子没收到,抓包一看就清楚了。
sudo tcpdump -i enp3s0 -n port 2049 or port 111NFS 使用 2049 端口,rpcbind 使用 111 端口。抓包能看到完整的 RPC 调用过程,对排查协议层面的问题很有帮助。
7. 常见故障的排查链路与修复方案
7.1 板子卡在 U-Boot 阶段无法下载内核
现象:执行run bootcmd后,TFTP 传输一直超时或者报错。
排查步骤:
- 在 U-Boot 里
ping 192.168.1.100,确认网络通不通 - 如果 ping 不通,检查网线连接、宿主机 IP、防火墙
- 如果 ping 通但 TFTP 失败,检查 TFTP 服务状态和目录权限
- 确认文件名大小写是否匹配,Linux 下文件名区分大小写
我遇到过一种情况:U-Boot 里 ping 得通,但 TFTP 就是传不了。后来发现是宿主机的防火墙只放行了 ICMP,没放行 TFTP 的 UDP 69 端口。关掉防火墙或者添加规则就好了。
7.2 内核启动后找不到 NFS 根文件系统
现象:内核启动日志显示VFS: Unable to mount root fs on unknown-block(0,255)。
排查步骤:
- 确认 NFS 服务端是否正常运行:
sudo systemctl status nfs-kernel-server - 确认导出目录是否正确:
showmount -e localhost - 确认
bootargs里的nfsroot路径和 IP 是否正确 - 确认内核配置里
CONFIG_ROOT_NFS是否勾选 - 确认板子和宿主机是否在同一网段
这个报错是最常见的,原因也最多。我建议按顺序逐项检查,不要跳步。有一次我排查了半天,最后发现是nfsroot路径里多了一个空格,导致内核解析参数时出错。这种低级错误在紧张调试时很容易犯。
7.3 挂载成功但启动过程中卡住
现象:NFS 挂载成功的日志已经打印出来了,但系统启动到某一步就卡住不动。
这种情况一般是根文件系统里的某个服务或者脚本有问题。可能的原因:
/etc/fstab里有挂载本地分区的条目,但 NFS 环境下这些分区不存在,导致挂载超时- 某个启动服务依赖网络,但网络配置还没完成
- 根文件系统里的库文件缺失或者版本不匹配
排查方法:在bootargs里加init=/bin/sh,让内核启动后直接进入 shell,不执行正常的 init 流程。这样可以手动检查文件系统,逐步排查问题。
setenv bootargs 'console=ttyS2,1500000n8 root=/dev/nfs rw nfsroot=192.168.1.100:/home/username/nfsroot,nfsvers=3 ip=192.168.1.200:192.168.1.100:192.168.1.1:255.255.255.0::eth0:off init=/bin/sh'进入 shell 后,可以手动挂载/proc、/sys,然后检查各个目录和文件。
7.4 触摸屏方向不对等外设问题
有同行问到rk3568 触摸竖屏改为横屏设备树修改的问题。这其实和 NFS 启动没有直接关系,但在 NFS 调试环境下改起来很方便,因为改完设备树重新编译 dtb,通过 TFTP 下载重启就行,不用重新烧录整个系统。
触摸屏方向一般在设备树的触摸节点里配置,比如:
>1x { status = "okay"; touchscreen-inverted-x; touchscreen-inverted-y; touchscreen-swapped-x-y; };具体加哪些属性取决于屏幕的安装方向和触摸 IC 的默认坐标系。touchscreen-swapped-x-y表示交换 X 和 Y 轴,touchscreen-inverted-x表示 X 轴反向。组合使用可以适配各种旋转角度。改完 dtb 后通过 TFTP 下载重启,立刻就能看到效果,这就是 NFS 调试环境的便利之处。
8. 调试环境下的开发效率提升实践
8.1 应用程序的快速迭代
NFS 根文件系统最大的好处就是应用程序的迭代速度。在宿主机上交叉编译好的程序,直接拷贝到 NFS 导出目录里,板子上就能运行,不需要重启,更不需要烧录。
比如你正在调试一个基于 RK3568 的 EtherCAT 主站程序,编译生成ethercat_master可执行文件后:
cp ethercat_master /home/username/nfsroot/usr/bin/然后在板子的串口终端里直接执行:
/usr/bin/ethercat_master改代码、编译、拷贝、运行,整个循环不到一分钟。如果每次都要烧录 eMMC,那效率完全没法比。
8.2 内核模块的动态加载
调试驱动时,经常需要反复修改内核模块。NFS 环境下,可以在宿主机上编译好.ko文件,拷贝到 NFS 目录,然后在板子上用insmod加载:
cp my_driver.ko /home/username/nfsroot/lib/modules/板子上:
insmod /lib/modules/my_driver.ko如果模块有问题导致内核崩溃,重启板子即可,不会影响 NFS 服务器上的文件。这种隔离性让调试驱动变得安全很多。
8.3 根文件系统的备份与恢复
NFS 根文件系统在宿主机上就是一个普通目录,备份和恢复都很简单。在调试过程中,如果改坏了某个配置文件导致系统起不来,可以直接在宿主机上修复,不需要通过板子操作。
我习惯在开始调试前先打个快照:
cp -a /home/username/nfsroot /home/username/nfsroot.bak如果改坏了,直接恢复:
rm -rf /home/username/nfsroot cp -a /home/username/nfsroot.bak /home/username/nfsroot这比在板子上用dd命令备份恢复快多了,而且不会因为板子起不来而束手无策。
8.4 多板子共用一个 NFS 根文件系统
如果你手上有多个 RK3568 板子,可以让它们共用同一个 NFS 根文件系统。只需要在 NFS 服务端的/etc/exports里允许整个网段访问,然后每个板子设置不同的 IP 地址即可。
但要注意,如果多个板子同时写同一个文件,可能会冲突。调试阶段一般不会遇到这个问题,但如果要同时跑多个板子做压力测试,最好还是给每个板子单独的根文件系统目录。
9. 一些容易忽略的细节与个人经验
9.1 串口终端的配置
RK3568 的调试串口是 ttyS2,波特率 1500000。这个波特率不是常见的 115200,有些 USB 转串口线可能不支持这么高的波特率。如果串口终端显示乱码,先检查波特率设置。我用的是 CH340 芯片的串口线,支持 1500000 波特率,工作正常。如果手头的线不支持,换一根 FT232 芯片的试试。
9.2 内核编译时的配置保存
每次make menuconfig改完配置后,记得保存到.config文件,并且备份一份。RK3568 的 SDK 里,内核配置文件一般在kernel/arch/arm64/configs/目录下,文件名类似rockchip_linux_defconfig。可以把改好的配置保存回去:
make savedefconfig cp defconfig arch/arm64/configs/rockchip_linux_defconfig这样下次重新编译时,配置不会丢。我有一次改完配置忘了保存,重新编译后 NFS 支持又没了,排查了半天才发现是配置没保存。
9.3 NFS 挂载超时时间的调整
默认的 NFS 挂载超时时间比较短,如果网络稍有延迟,可能会挂载失败。可以在bootargs里加nfsroot的timeo参数:
nfsroot=192.168.1.100:/home/username/nfsroot,nfsvers=3,timeo=600timeo=600表示超时时间为 60 秒(单位是 0.1 秒)。如果网络环境不太好,可以适当加大这个值。
9.4 关于 NFSv4 的额外配置
如果非要用 NFSv4,配置会稍微复杂一些。NFSv4 需要一个伪根文件系统,在/etc/exports里要这样写:
/home/username/nfsroot 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash,fsid=0)fsid=0表示这是 NFSv4 的根。然后bootargs里的nfsroot路径要相应调整:
nfsroot=192.168.1.100:/,nfsvers=4注意路径变成了/,因为fsid=0已经把导出目录映射为 NFSv4 的根了。这个细节很容易搞错,我当初就是从 v3 换到 v4 时没改路径,导致挂载失败。
9.5 调试完成后的收尾工作
NFS 调试环境搭好之后,开发效率确实高,但也有一些需要注意的地方。比如,NFS 根文件系统依赖网络,如果网络不稳定,板子可能会卡住。所以在做性能测试或者长时间运行测试时,最好还是烧录到 eMMC 里跑。
另外,NFS 根文件系统的读写速度受网络带宽限制,比本地存储慢不少。如果应用对 IO 性能要求高,NFS 环境可能不适合做性能评估,只适合做功能调试。
我在实际项目中的做法是:功能开发和调试阶段用 NFS 根文件系统,快速迭代;功能稳定后,烧录到 eMMC 做性能和稳定性测试。两者结合,既保证了开发效率,又保证了测试的准确性。
还有一点,NFS 服务端的导出目录权限要设置好。如果多人共用一台宿主机做 NFS 服务器,要避免互相干扰。可以给每个人分配不同的导出目录和 IP 段,在/etc/exports里分别配置。
最后分享一个我常用的小技巧:在宿主机的 NFS 导出目录里放一个sync.sh脚本,内容是根据当前编译产物自动更新根文件系统里的对应文件。比如:
#!/bin/bash cp /path/to/build/app /home/username/nfsroot/usr/bin/ cp /path/to/build/lib/*.so /home/username/nfsroot/usr/lib/ echo "Sync done at $(date)"每次编译完执行一下,省去手动拷贝的麻烦。这个脚本可以根据项目实际情况定制,把常用的拷贝操作都放进去,日积月累能省不少时间。