目录
(2)开发板挂载NFS系统,使得开发板与Ubuntu共享文件
(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!"
在脚本文件中输入以上代码即可。

