NFS 配置全指南 —— 从踩坑到手动挂载的完整落地

目录

一、为什么要学习NFS?

二、NFS网络文件系统的配置

(1)Ubuntu环境安装NFS的服务端

(2)开发板挂载NFS系统,使得开发板与Ubuntu共享文件

(3)设置开发板开机自动挂载

(4)给开发板配置永久静态ip

(5)一次错误的尝试:等待random随机数熵池初始化完成后再执行

(6)最终可行路径:在random随机数熵池结束后再手动调用脚本


一、为什么要学习NFS?

在学习驱动开发之前,我一直在思考一个问题:我们在Ubuntu中写的驱动代码是怎么传输到开发板中的?前面一篇文章我们说它是用网络传输的文件,但具体是如何传输呢?它其实就是NFS网络文件系统。

其次,写驱动的本质是在给内核打补丁,但我们并没有完整的实操:用MFGtool将Linux系统烧录到开发板中。刚冒出这个问题的时候,我还会担心我们是不是必须得真的啃一遍U-Boot、根文件系统、内核烧录的过程,但突然联想到以前了解过的设备树知识---将板载资源与Linux内核进行解耦!于是我们每次只需要将写的驱动文件、修改的设备树替代开发板原来的即可,并不需要重新将内核、根文件系统这一切重头做。(这或许就是前人发明设备树的缘故)

不过,我们在编译阶段仍然是需要使用到Linux内核的一些文件去编译设备树的。所以我们必须在Ubuntu中有完整的正点原子内核源码。所以我们首先得将其拷贝到Ubuntu中即可。

常见的文件传输功能有TFTP和NFS,但是TFTP是一种服务,每次都需要手动敲命令才能传输,而NFS则是将一方的文件系统挂载在另一方文件系统下,只要更新就自动被另一侧感知到。如果频繁的修改文件,则NFS的高效、简便性则大大凸显。所以我们会选择NFS进行驱动开发。

本文就聚焦于NFS的配置、使用操作,为后续的驱动学习打下基础。

二、NFS网络文件系统的配置

NFS(网络文件系统)是嵌入式 Linux 驱动开发的刚需工具,它能让开发板直接读写 Ubuntu 主机的目录,彻底告别 "每次编译完手动拷贝文件到开发板" 的低效操作。

(1)Ubuntu环境安装NFS的服务端

cpp 复制代码
# 1. 安装NFS服务(国内源,自动替换为阿里云/清华源)
sudo apt update && sudo apt install -y nfs-kernel-server

# 2. 创建NFS共享目录(自定义路径,建议放在用户目录下,避免权限问题)
mkdir -p ~/nfs/rootfs
# 给目录赋予完全权限,避免开发板读写报错
sudo chmod 777 ~/nfs/rootfs
cpp 复制代码
#编辑 NFS 配置文件 /etc/exports,添加共享规则:
sudo vim /etc/exports

在该文件中写入权限以及路径:

cpp 复制代码
/home/hmy/nfs/rootfs *(rw,sync,no_root_squash,no_subtree_check)

最后在Ubuntu中重启NFS服务,看看配置是否生效。

cpp 复制代码
# 重启NFS服务,使配置生效
sudo systemctl restart nfs-kernel-server
# 设置开机自启
sudo systemctl enable nfs-kernel-server

# 本地验证(Ubuntu上执行,测试共享是否生效)
showmount -e localhost
# 正常输出:/home/xxx/nfs/rootfs *

(2)开发板挂载NFS系统,使得开发板与Ubuntu共享文件

在刚刚的NFS工作目录下创建一个 mytest.c 文件,在里面写入 hello world!

并在开发板侧测试是否能用NFS挂载文件夹(这里注意,NFS只能挂载文件夹,不能挂载单个文件)。

cpp 复制代码
mount -t nfs -o nolock,nfsvers=3 192.168.10.100:/home/hmy/nfs/rootfs /home/root/get

(3)设置开发板开机自动挂载

我们上述的操作已经验证了NFS共享文件的可行性,但是只是临时存在于本次开机状态下的,一旦断电、重启,上述的挂载操作都消失了。不过在Ubuntu端已经设置过开机自启动+NFS工作路径的配置,所以是永久化的。

于是我们现在对开发板进行设置开机自动挂载NFS系统。

cpp 复制代码
#在开发板命令行中输入这行代码,打开该文件
vi /etc/init.d/rcS
cpp 复制代码
#在打开的文件中最后一行写入这个
mount -t nfs -o nolock,nfsvers=3 192.168.10.100:/home/hmy/nfs/rootfs /home/root/get

这里其实是写错了,应该写下exec前面。但是当时由于是第一次使用,写错了。其实真正也不会出现下面的问题,而是直接卡住了内核,连命令行都进不去!

不过不用担心,这一种方法我们已经舍弃了,大家看看即可,作为一个错误示例。

经过查阅资料,发现是rcS脚本运行的太早了,很有可能网卡还没完成IP分配,网络还没连通,挂载命令就先执行了,所以执行失败,直接跳过。也有可能是我们之前在给开发板配置静态IP的时候,图方便没有给他配置到文件中,而是每次进入Linux系统后再手动配置的ip。而NFS是网络文件系统,他能共享文件的前提是能与Ubuntu系统网络通信,此时没有配置永久静态IP,NFS就无法运行。

(4)给开发板配置永久静态ip

依旧打开刚刚文件,由于/etc/init.d/rcS是执行的第一个用户态进程,由内核自动执行。我们通常也称他为开机脚本,一般要手动写的配置代码都会放到这里面去。写在exec之前即生效配置。

cpp 复制代码
#在开发板命令行中输入这行代码,打开该文件
vi /etc/init.d/rcS
cpp 复制代码
ifconfig eth0 up

ifconfig eth0 192.168.10.50 netmask 255.255.255.0

sleep 3

不过这里得针对你自己到底使用的是eth0还是eth1,来选择配置的。比如我就规定自己的网线接在eth0不要动。

(5)一次错误的尝试:等待random随机数熵池初始化完成后再执行

但是经过测试发现每次Linux一进系统可以短暂的运行几秒钟,然后就卡死了。经过分析得知:Linux的网络子系统、网卡驱动、DHCP等等都依赖于随机数熵池。刚刚开机时候,熵池是空的,网络子系统属于半初始化状态,而我们在此时进行了rcS脚本中的指令,产生了崩溃!即命令行走不动,体现卡住的特点。

网卡初始化依赖于随机数熵池也能从下图看出来,在刚刚运行Linux系统的时候执行ip a查看eth1网卡的ip地址,发现没有ipv4,只有ipv6。而在随机数熵池初始化打印出来后再查看,就有了随机的ipv4地址了。

这个在Ubuntu的PC端一般很少见,这是因为桌面版由硬件随机数生成器,熵池初始化极快,一瞬间就完成了。而嵌入式ARM开发板i.MX6ULL没有这个硬件,完全依赖软件生成,初始化比较慢,所以才会出现崩溃卡死的现象。

将下面的代码拷贝到exec之前即可。这是往上找的工业级配置脚本。

cpp 复制代码
# -----------------------------------------------------------------------------
# 1. 等待随机数熵池初始化完成(监听内核日志,解决开机卡死)
timeout_cnt=0
while ! grep -q "nonblocking pool is initialized" /proc/kmsg; do
    sleep 0.5
    # 超时兜底:最多等10秒,避免死循环
    timeout_cnt=$((timeout_cnt+1))
    if [ $timeout_cnt -ge 20 ]; then
        echo ">>> Warning: Entropy pool timeout, proceed anyway"
        break
    fi
done
unset timeout_cnt
echo ">>> Entropy pool is ready!"

# 2. 启动eth1网卡,设置静态IP
ifconfig eth1 192.168.10.50 netmask 255.255.255.0 up
echo ">>> eth1 interface enabled"

# 3. 等待网卡IP完全就绪
while ! ifconfig eth1 | grep -q "inet addr:192.168.10.50"; do
    sleep 0.1
done
echo ">>> eth1 IP configured: 192.168.10.50"

# 4. 网络连通性校验(带重试)
echo ">>> Checking NFS server connectivity..."
ping_success=0
for i in 1 2 3; do
    ping -c 1 -W 1 192.168.10.100 > /dev/null 2>&1
    if [ $? -eq 0 ]; then
        ping_success=1
        break
    fi
    sleep 0.5
done

# 5. 挂载 NFS
if [ $ping_success -eq 1 ]; then
    echo ">>> NFS server reachable, mounting share..."
    mount -t nfs -o nolock,nfsvers=3,soft,timeo=10,retrans=2 192.168.10.100:/home/hmy/nfs/rootfs /home/root/get
    if [ $? -eq 0 ]; then
        echo ">>> NFS mount succeeded: /home/root/get"
    else
        echo ">>> NFS mount failed!"
    fi
else
    echo ">>> NFS server unreachable, skip mount"
fi
# -----------------------------------------------------------------------------

虽然不知道是什么原理,但是内核差一点就启动不了了,估计是rcS脚本陷入了死循环,但是我对于这个脚本又不熟悉,只能依靠AI的修改。所以我决定放弃这条路。

(6)最终可行路径:在random随机数熵池结束后再手动调用脚本

cpp 复制代码
#创建脚本文件
touch my_init.sh

#给脚本执行权限
chmod +x my_init.sh
cpp 复制代码
#!/bin/sh

ifconfig eth1 192.168.10.50 netmask 255.255.255.0 up
echo "IP IS OK:192.168.10.50"

mount -t nfs -o nolock,nfsvers=3,soft 192.168.10.100:/home/hmy/nfs/rootfs /home/root/get
echo "NFS IS OK!"

在脚本文件中输入以上代码即可。

相关推荐
SilentSamsara2 小时前
Shell 脚本进阶:从能跑到写得优雅
linux·运维·服务器·自动化·ssh·bash
xiaoshuaishuai82 小时前
C# 实现“superpowers进化
运维·服务器·windows·c#
孙同学_3 小时前
【项目篇】高并发服务器 - 从 Buffer 到 TcpServer 构建高并发服务器引擎
运维·服务器
SilentSamsara3 小时前
Linux磁盘与存储管理:分区、LVM 与 IO 性能全栈分析
linux·运维·服务器·ssh·shell
IMPYLH10 小时前
Linux 的 pinky 命令
linux·运维·服务器·bash
HelloWorld_SDK11 小时前
Docker安装OpenClaw
运维·docker·容器·openclaw
REDcker11 小时前
Linux iptables 与 Netfilter:原理、路径与运维要点
linux·运维·服务器
KKKlucifer13 小时前
零信任融合实践:国内堡垒机如何落地动态权限与实时阻断
运维
嵌入式×边缘AI:打怪升级日志13 小时前
Linux 驱动开发入门:从最简单的 hello 驱动到硬件交互
linux·驱动开发·交互