分布式文件系统中的 rename 为什么会冲突:从文件到目录
在本地文件系统里,rename(old, new) 看起来只是“换个名字”;到了 GPFS(IBM Storage Scale)这类分布式文件系统中,它却是一笔需要跨节点协调的元数据事务。 核心原因是:rename 改变的不是文件内容,而是命名空间中的可达关系。 一次操作可能同时读写源目录、目标目录、被移动对象以及被覆盖对象。多个客户端若触及同一批元数据,就必须串行化,否则可能出现重名、对象丢失、目录环、错误覆盖,甚至节点间看到不同命名空间。 本文不讨论某个版本的内部锁名,而从通用实现原理出发,分别分析普通文件和目录的 rename 为什么冲突。 一、先把 rename 看成一笔事务 假设执行: rename("/A/x", "/B/y") 抽象到目录项,操作近似为: 1. 在目录 A 中查找 x,得到对象 inode X 2. 在目录 B 中查找 y 3. 若 y 已存在,校验并处理被覆盖对象 Y 4. 从 A 删除目录项 x -> X 5. 在 B 创建目录项 y -> X 6. 更新相关 inode 的时间、链接数或父目录信息 7. 提交日志,使整个变化原子可恢复 POSIX 语义要求其他进程不能看到“源名字已消失、目标名字尚未出现”的中间状态。系统崩溃后,也不能只完成其中一半。因此,分布式文件系统需要把这些修改组织成一个原子事务,并协调所有可能缓存相关元数据的节点。 可以把参与者概括为: 源父目录 Psrc ─┐ 源对象 X ├── rename 元数据事务 目标父目录 Pdst ┤ 目标对象 Y? ─┘ 冲突并不等于两个请求使用了相同路径字符串。只要它们需要修改同一个目录、同一个 inode,或需要做相互影响的命名空间判断,就可能冲突。 ...