摘要

  • RFC 1094 说明,无状态 NFS 服务器无法单独保留某些本地文件系统的行为:目录中的名字删掉后,已打开该文件的进程仍能继续使用它。
  • 文档给客户端留出一种做法:删除时先改名,等本地打开结束后再移除临时目录项。RFC 没有规定所有实现都必须这样做,也没有定义客户端故障后的清理策略。

“目录里没有”与“进程用完了”是两件事

文件的生命周期看起来像一个过程,实际至少有两个时钟。目录项可以先消失;一个已经打开文件的进程,可能仍握有可继续读写的本地引用。对本地操作系统来说,这种分离并不奇怪。搬到网络文件系统后,问题变成:远端服务器凭什么知道还有人没有用完?

1989 年 3 月发布的 RFC 1094 为 NFS 第二版描述了这个难题。它提到,一些操作系统允许删除仍被打开的文件,进程可以继续读写,直到自己关闭。RFC 的判断很直接:无状态服务器无法实现这类语义。它建议客户端在删除时重命名文件,到关闭时才删除新名字。

这不是说 NFS 完全不认识文件,也不是说客户端不能提供熟悉的接口。它揭示的是客户端界面与服务器协议之间的一条缝:本地进程知道“我仍在使用”,服务器却只接到一个目录操作。

网络请求里少了打开与关闭两个时点

NFSv2 的程序包含 LOOKUP、READ、WRITE、CREATE、REMOVE 和 RENAME 等操作。REMOVE 以目录和文件名为输入,删除的是目录中的名字。协议没有对应的 OPEN、CLOSE,让服务器记录某个客户端何时开始持有、何时释放最后一个打开引用。

客户端在文件操作中会携带服务器发出的 file handle。但这不是打开通知,也不是正在使用该文件的计数器。操作系统可以在本地处理应用程序的 open(),NFS 客户端再把后续请求转换成网络操作;服务器不一定会获得一条逐一对应的远端打开状态。

因此,服务器可以执行 REMOVE,却不知道另一个机器的进程还想继续访问对象。RFC 1094 并未要求把这种状态强塞进服务器;它接受服务器保持简单,再把适配工作交给握有本地打开信息的一侧。

改名把一次删除拆成了两步

客户端的做法是先改名,再等本地关闭后删除。对应用可见的原名已经消失;对服务器来说,临时名字仍然存在,文件因此可以继续被表示。实际删除的时点被推迟了。

这一做法改变了“删掉”的含义。名称空间的清理与存储回收不再同时发生。一个用户界面可能立刻显示文件不见了,而服务器仍保留对象。RFC 只提出这个技巧,并没有定义临时名字格式、冲突处理、其他客户端的观察方式,或客户端在关闭之前崩溃怎么办。

因此不能把建议升级成产品事实。RFC 1094 没有证明当时所有 NFS 客户端都实现了改名方案;要讲一个厂商版本如何做,仍需该版本的运行代码、手册或测试记录。

这种延后会带来可观察但不确定的后果:临时目录项可能存在得比预期久,或者清理操作可能没有完成。它们是根据机制推导出的运维问题,不是 RFC 报告的故障频率,也不是已证实的部署事故。

无状态约束了服务器要记住的对话

RFC 所说的无状态,指服务器不必为每个客户端保存协议会话状态。服务器或网络中断后,客户端能够重试,而不需要先恢复一整张远端打开表。文件内容、目录项、属性和磁盘占用当然仍有状态;被精简的是客户端与服务器之间的协议对话。

打开引用的寿命却来自本地操作系统,难以仅凭 NFSv2 的请求集合推断。RFC 1094 还把文件锁和记录锁列为独立服务,而不是这份 NFS 规范所描述的状态。它在文件数据操作上做了范围选择,并没有声称远端系统可以执行所有本地约定。

后来的协议选择了不同分工。NFSv3 的接口仍没有远端 OPEN、CLOSE;RFC 7530 描述的 NFSv4 则加入这两种操作与 stateid。这个变化不能证明所有环境都迁移,也不能证明一对任意版本的客户端和服务器实现了相同语义。它显示的是另一种架构选择:把打开状态纳入协议,再承担相应协调成本。

谁知道文件何时可以真正删除

远端服务器控制导出的目录,并执行 REMOVE。客户端操作系统知道哪些进程还保有打开引用。只要服务器没有接到打开状态,它就不能独自判定文件何时不再被使用。临时改名让客户端可以把“对用户隐藏”与“从服务器删除”安排在不同时间。

这使兼容问题成为责任分配问题。应用所有者要确认打开后删除是否是必需语义;客户端实现者要说明如何跟踪引用和清理;服务器运维要知道临时项是否可能留下。RFC 1094 给出了方向,却没有为这些参与方写出完整的故障恢复和保留政策。

最小服务器模型有它的价值:实现无需重建每个客户端的会话。但若应用依赖更细的本地行为,成本会转向客户端及其测试。挂载成功只能说明文件系统可达,不能替代对特定操作顺序的验证。

来源与证据边界

这些 RFC 证明了协议写了什么以及设计者讨论了什么;它们不证明改名技巧的普及率、某种客户端重启后的清理行为,或实际故障发生频率。