ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

NFS /etc/exports 参数默认值与优先级:嵌入式开发避坑指南

NFS /etc/exports 参数默认值与优先级:嵌入式开发避坑指南 很多人配置/etc/exports的时候有个习惯写完共享目录和允许的主机网段顺手保存然后就去客户端挂载了。你问他中间那几个括号里的参数是什么他可能想都不想回你一句默认的就行。但问题恰恰出在这个默认上。我是在拿 RK3568 开发板做 NFS 根文件系统启动的时候被狠狠教育过一次的明明/etc/exports里写了共享板子却反复报只读、报Operation not permitted甚至直接VFS: Unable to mount root fs。后来用exportfs -v一看一堆默认参数悄悄生效和我以为的完全不是一回事。这篇就把 NFS/etc/exports的参数默认值和优先级一次讲透适合正在搭 NFS 的嵌入式开发、Linux 服务器运维以及想在 Ubuntu 上快速搭 NFS 共享的人。1. 先把这一行拆开读exports 文件的解析顺序与匹配逻辑/etc/exports每行看起来简单拆开却有三个部分导出目录、主机说明符、括号里的选项列表。语法骨架是这样的导出目录 主机1(选项1,选项2) 主机2(选项3,选项4) 主机3这里最容易踩的第一个坑就是选项只对它前面的那个主机说明符生效。看个例子/home/tftp 192.168.1.100(rw,sync) 192.168.1.0/24(ro)192.168.1.100拿到rw,sync但整个网段里的其他机器拿到的是ro。如果你写成了下面这样那所有机器都只能读/home/tftp 192.168.1.100 192.168.1.0/24(rw,sync)注意192.168.1.100后面没有括号它就是一个裸主机说明符所有选项默认。而括号里的rw,sync只挂在后面的网段上。这种无意识的缩进错误比参数配错更隐蔽。1.1 主机说明符的顺序和通配符陷阱主机说明符支持好几种写法单个 IP、主机名、*.example.com这种通配符域名、192.168.1.0/24网段、以及最粗暴的*。我见过不少人想当然地认为越具体的匹配优先级越高。这句话在这里不成立。NFS 导出匹配遵循的是exports(5)里一个很容易被忽略的规则如果一台客户端同时被同一导出行里的多个主机说明符匹配按先出现者优先而不是按谁更具体。举个例子/home/nfs *(rw,sync,no_root_squash) 192.168.1.10(rw,sync,no_root_squash,no_subtree_check)当192.168.1.10这台机器来访问时它先匹配到了第一个*后面那个专门给它写的主机说明符根本不生效。如果你想给某个 IP 特殊待遇必须把它放在*前面/home/nfs 192.168.1.10(rw,sync,no_root_squash,no_subtree_check) *(rw,sync,no_root_squash)这个顺序问题在嵌入式调试里特别常见。板子的 IP 是固定的你本来想给它额外的insecure或no_subtree_check结果因为通配符排在前面半天没生效。1.2 括号和逗号的边界问题与同目录多行选项列表还有一些书写细节出错率也很高。第一括号前面不要加空格。/home/nfs 192.168.1.0/24 (rw,sync)会被解析成两个主机说明符一个裸 IP 网段和一个括号为开头的异常项轻则行为诡异重则导出失败。第二同一个主机说明符内不要同时写互相冲突的选项比如ro,rw。虽然多数发行版会取后一个但 man 手册里明确说这种行为未定义不同 nfs-utils 版本的解析结果可能都不一样属于典型的自我埋坑。第三同一个共享目录尽量只导出一行。如果你写了/home/nfs 192.168.1.0/24(rw,sync) /home/nfs 192.168.2.0/24(ro)不同版本的内核和 exportfs 工具对同一目录多次导出的处理方式并不一致有的会合并选项有的会直接覆盖。规范做法是把所有主机说明符放在同一行里靠顺序控制匹配结果。2. 不写选项时 NFS 悄悄给你的默认值这是本文最核心的部分。很多人以为没写就是没限制实际上 Linux NFS 服务的默认值保守得很甚至可以说有点坑。我把常用参数和默认值整理成了下面这张表都是基于现代 Linux 发行版nfs-utils 较新版本的行为参数默认值不指定时的实际效果备注ro/rwro只读客户端就算用rw参数挂载也只能读需要写操作时务必显式写rwsync/asyncsync现代版本写入请求实时落到服务端存储极老版本可能是async别赌默认root_squash/no_root_squashroot_squash客户端 root 被压缩成匿名用户 nobody嵌入式 NFS 根文件系统必须显式关掉all_squash/no_all_squashno_all_squash普通用户按各自 uid 映射安全要求高才用all_squashanonuid/anongid-2显示为 nobody/65534匿名用户映射到的 uid/gid需要固定权限时可显式指定secure/insecuresecure强制客户端源端口小于 1024某些板卡 U-Boot 网络栈会因端口问题失败wdelay/no_wdelaywdelay合并多个写请求延迟落盘提升吞吐小文件交互场景可显式no_wdelaysubtree_check/no_subtree_check历史上为subtree_check每次访问检查文件是否仍在导出子目录内NFSv4 下建议显式no_subtree_checksecsecsys使用 AUTH_SYS 明文身份无加密敏感数据场景需要换krb5pfsid自动生成NFSv4 伪文件系统根节点标识多目录 NFSv4 挂载时建议显式规划2.1 读写与同步ro、rw、sync、async先说读写。/etc/exports中如果不写ro也不写rw默认是只读。这个默认值坑了非常多的人。你挂载目录、列出文件一切正常一写文件就报Read-only file system。服务端exportfs -v一看显示ro从头到尾没人写过这个单词它就是默认值。再说同步。现代 nfs-utils 默认sync即 NFS 写入请求返回成功时数据已经写入服务端存储。async则允许先返回成功、稍后再落盘性能高一些但服务端异常断电时丢数据的概率也大。嵌入式开发场景我强烈建议显式写sync因为开发板经常直接断电重启async的窗口期可能导致根文件系统数据损坏。2.2 权限与匿名映射root_squash、all_squash、anonuidroot_squash是第二个大坑。它的作用是当客户端以 root 访问 NFS 时服务端把 root 映射成匿名用户nobodyuid 通常为 65534。这样客户端 root 在共享目录里就没有超管权限了。这种设计本身是保护服务端的好机制但如果你在调试一块 RK3568 开发板板子上的根文件系统通过 NFS 挂载所有初始化进程都是 root 身份运行遇到root_squash就会表现出一堆诡异问题touch一个文件提示Permission deniedchmod不生效服务起不来。开发阶段最常见的操作就是在/etc/exports里显式加no_root_squash。all_squash更极端它把所有客户端用户都映射成匿名用户。通常只有在共享公共目录、不想让任何人保留 uid 时才用。anonuid/anongid则负责自定义这个匿名用户到底是谁比如你希望所有映射后的文件都属于www-data就可以写all_squash,anonuid33,anongid33。2.3 端口、子目录、文件句柄secure、subtree_check、fsidsecure这个默认值在嵌入式场景里很让人头疼。它的含义是客户端连接 NFS 服务时源端口必须小于 1024。传统上这是出于安全考虑但很多开发板早期启动阶段的 NFS 客户端、某些 U-Boot 下的网络实现、或者 NAT 转换过的流量源端口根本不满足条件结果就是挂载被静默拒绝。遇到挂不上、但服务端日志和网络连通性都正常的情况可以先试试显式加insecure。subtree_check的默认行为在历史上是开启的。它检查客户端访问的文件是否真的在导出目录的子目录里。对于根文件系统这种目录树非常庞大的共享开启它反而会带来性能和兼容性上的困扰NFSv4 场景尤其不友好。现在的实践几乎都是显式加no_subtree_check。fsid是 NFSv4 伪文件系统的关键参数。NFSv4 不再像 v3 那样直接暴露绝对路径而是通过伪文件系统把导出的多个目录串起来。如果你导出多个目录且用 NFSv4 挂载经常需要设置fsid0来指定伪文件系统根否则客户端mount -t nfs4会报No such file or directory典型的目录明明导出了却挂不上的坑。3. 参数优先级从文件行序到客户端挂载的三层规则标题既然叫优先级对照表这里就得把谁压过谁讲清楚。我按实际生效链路把优先级拆成三层。3.1 同一条内多主机说明符先到先得这一层前面已经详细展开过结论就是同一导出行内多个主机说明符按出现顺序匹配先匹配到的生效与具体程度无关。记住把特殊主机放前面通配符放后面就能规避大多数问题。3.2/etc/exports文件、exportfs 命令行与内核导出的优先级exportfs命令是另一个修改导出参数的入口。它有几种典型用法exportfs -ra # 重新读取 /etc/exports 并全量应用 exportfs -v # 查看当前内核中实际生效的导出列表和选项 exportfs -u 主机:/目录 # 撤销某个导出 exportfs -o ro 主机:/目录 # 临时以指定选项导出当exportfs -o手动指定的选项与/etc/exports文件冲突时命令行的优先级更高。但这种优先级是有代价的临时导出只存在于当前运行的内核导出表中重启 NFS 服务或重启机器后就没了别指望它持久化。一个常见的错误是用exportfs -o rw临时导出后忘了加其他选项结果临时目录的权限和原配置不一致服务重启后莫名其妙配置失效。另一件值得注意的事是exportfs -ra之后/etc/exports里同一目录的旧导出会被更新覆盖。如果你中途用exportfs -o手动加过额外选项重读配置文件后这些选项可能会被文件里的配置重置。所以排查问题时永远以操作后的exportfs -v实际输出为准而不是以你记忆中刚才设过什么为准。3.3 客户端 mount 选项与服务器导出选项取更严格值这一层最容易被误读。很多人以为只要服务端rw客户端想怎么挂都行或者以为服务端no_root_squash客户端就一定能拿到 root。实际情况是最终行为要看服务端导出选项和客户端挂载选项两者中更严格的那个服务端导出客户端挂载最终效果rwrw可写rwro只读客户端ro生效rorw只读服务端ro生效客户端无法绕过root_squash客户端 rootroot 被压成 nobody客户端无法绕过no_root_squash客户端 root保留 root 权限也就是说root_squash是服务端行为客户端哪怕在mount命令里写再多参数也无法关闭它反过来说服务端rw但客户端挂载时写了ro那客户端这边就是只读的。很多明明导出了 rw 却写不进文件的排查最后都落在客户端mount -o ro这种低级失误上。我调 RK3568 的时候还遇到过一种情况同一块板子用 U-Boot 里nfsroot...,rw启动时能写进入系统后用mount -t nfs手动挂载却只读。查了半天就是 U-Boot 传进去的mount选项和我手动敲的不一样一个带rw一个没带。所以排查读写问题时先两边各执行一次mount看实际选项再判断是谁限制了你。4. RK3568 挂 NFS 根文件系统这几个参数建议每次都显式写这部分结合具体场景给一套能直接抄走的配置。假设场景是一台 Ubuntu 主机导出/home/nfs/rootfs作为 RK3568 开发板的根文件系统板子从内核启动阶段就通过 NFS 挂载根目录。4.1 从 Ubuntu 主机开始的一整套配置先在主机上安装 NFS 服务sudo apt install nfs-kernel-server sudo mkdir -p /home/nfs/rootfs然后把要导出的目录写进/etc/exports。我调试嵌入式板卡时长期使用的一行配置是/home/nfs/rootfs 192.168.1.0/24(rw,sync,no_root_squash,no_subtree_check,insecure)逐个解释为什么这么写rw根文件系统必须可写否则系统起来后一堆服务无法创建运行时文件。sync开发板经常直接断电sync能把数据丢失窗口缩到最小。no_root_squash板子里的 init 和各类服务基本都以 root 跑不关掉 root_squash 会遇到一堆权限错误。no_subtree_check根文件系统目录树庞大关掉子目录检查能减少性能损耗和 NFSv4 兼容问题。insecure有些开发板的内核网络栈或 U-Boot 阶段的 NFS 客户端源端口大于 1024默认secure会直接拒绝连接。配置完后应用并检查sudo exportfs -ra sudo systemctl enable --now nfs-server sudo showmount -e localhost如果你在 Ubuntu 上开启了防火墙还需要放行 NFS 相关端口至少包括 2049 和 rpcbindsudo ufw allow from 192.168.1.0/24 to any port 2049 proto tcp sudo ufw allow rpcbind4.2 U-Boot 的 bootargs 与几种常见报错RK3568 板子通常在 U-Boot 环境变量里配置启动参数核心是这一串setenv bootargs root/dev/nfs rw nfsroot192.168.1.100:/home/nfs/rootfs,v3,tcp ipdhcproot/dev/nfs告诉内核根设备走 NFSrw是内核挂载根文件系统时的挂载选项nfsroot后面用逗号分隔 NFS 版本和传输协议。这里我显式指定了v3,tcp因为嵌入式板卡早期启动阶段走 NFSv3 最成熟、最不容易出幺蛾子。如果你需要 NFSv4除了内核参数加v4还要把/etc/exports里的导出配合fsid0来规划两边的配合复杂度明显上升。启动过程中如果卡住了常见报错和对应思路如下VFS: Unable to mount root fs via NFS这是最笼统的提示后面通常跟具体错误码。先查三件事主机能不能 ping 通板子、导出的路径是否和nfsroot一致、宿主机 NFS 服务是否在运行。很多时候是板子通过ipdhcp拿到的 IP 不在192.168.1.0/24网段内和/etc/exports的允许网段不匹配。mount.nfs: Operation not permitted优先查root_squash和secure这两个默认值。把/etc/exports改成no_root_squash加insecure基本上能解决。mount.nfs: No such file or directory这个提示很误导人目录明明存在。最常见原因是 NFSv4 伪文件系统问题要么把nfsroot里的版本改回v3要么服务端导出时显式设置fsid0。4.3 挂载后的权限验证方法确认系统起来后直接在板子上执行一组测试验证参数是否真正生效touch /test_write # 验证可写 chown 0:0 /test_write # 验证 root 权限 ls -l /test_write # 看 owner 是否变成 nobody df -h / # 看挂载点和容量如果chown 0:0 /test_write之后ls -l显示 owner 是nobody或者数字 65534说明root_squash还在生效主机端/etc/exports里的no_root_squash没有正确应用。这时候回到主机执行exportfs -v大概率能看到配置被什么路径覆盖了。5. 用 exportfs 和 showmount 核对最终生效参数配置写对了还不够关键是确认内核里实际生效的参数和你以为的一样。NFS 的坑就在于文件里写得好看不代表内核里跑的就是这份。5.1exportfs -v是排查参数的最终依据在 Ubuntu 主机执行exportfs -v输出类似/home/nfs/rootfs 192.168.1.0/24(rw,sync,wdelay,root_squash,no_subtree_check,secsys,secure,no_all_squash)注意看即使你在/etc/exports里没写wdelay、没写root_squash输出里照样可能出现这些单词因为它们是被默认值补上的。只要看到不想生效的参数出现在这一行里说明配置里默认值正在起作用。例如我前面那个只写了/home/nfs/rootfs 192.168.1.0/24(rw,sync)却总是权限报错的现场exportfs -v 一输出root_squash和secure赫然在列问题一目了然。exportfs -v可以看到带选项的列表而showmount -e localhost只能看到导出了哪些目录和允许哪些主机看不到选项细节。所以别依赖 showmount 判断参数它是用来快速确认导出是否存在的。5.2 客户端侧的确认手段板子系统起来或手动挂载后在客户端执行挂载命令并查看实际协商参数mount -t nfs -o rw 192.168.1.100:/home/nfs/rootfs /mnt mount | grep /mntmount输出会列出客户端视角看到的一些挂载选项但注意root_squash、secure这类服务端选项不会完整反映在客户端 mount 输出里它们由服务端内核强制执行。所以最终的判断标准永远是服务端exportfs -v的输出加上客户端表现出的行为。5.3 实在查不出问题时抓包是最后的底牌如果读写法都正常、exportfs -v输出也说得通但板子就是挂不上根文件系统我建议直接在宿主机上抓包sudo tcpdump -i eth0 port 2049 -s 0 -w /tmp/nfs.pcap然后让板子重新启动挂载 NFS抓个几十秒后在 Wireshark 里打开重点看 NFSv3 MOUNT 和 NFS 协议头里的状态码。NFS3ERR_ACCES对应权限问题NFS3ERR_NOTSUPP往往是版本或选项不支持NULL大量重传则提示网络链路都不通。抓包能帮你把服务端配置问题和客户端网络问题快速分开不用靠猜。我自己调试 RK3568 时最典型的一次就是exportfs -v显示root_squash还在但/etc/exports明明已经改成了no_root_squash。后来发现是exportfs -ra没执行旧的内核导出表还挂着老的参数重启服务后一切恢复正常。所以一套流程走完最后一步永远是exportfs -ra exportfs -v把文件配置和内核实际状态对齐。这套默认值意识 优先级意识 核对命令的习惯养成了NFS 的绝大多数坑都能在你动手之前先被排掉。
返回列表