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!"

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

相关推荐
A小辣椒16 小时前
TShark:Wireshark CLI 功能
linux
A小辣椒20 小时前
TShark:基础知识
linux
AlfredZhao1 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
大树882 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠2 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质3 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式