最初,mv(1)
这是一项重命名操作;它更新文件系统中的名称,但不复制文件。最近,添加了一项便利功能,即如果源和目标位于不同的文件系统上,它将复制并删除文件。又称为“设备间移动”。
现在我正试图整理我的备份。我想移动.../rest2/Public/Backups
到.../rest2/Backup/(Backups)
,所以:
root@ts412:/QNAP/mounts/rest2# mv Public/Backups Backup/
在哪里:
root@ts412:/QNAP/mounts/rest2# df -h /QNAP/mounts/rest2/Public/
Filesystem Size Used Avail Use% Mounted on
/dev/sdb10 831G 715G 75G 91% /QNAP/mounts/rest2
root@ts412:/QNAP/mounts/rest2# df -h /QNAP/mounts/rest2/Backup/
Filesystem Size Used Avail Use% Mounted on
/dev/sdb10 831G 715G 75G 91% /QNAP/mounts/rest2
因此相同的文件系统:
(仅供参考,rest2
是“剩余的空间disk2
”)
但是移动开始表现得像“设备间移动”(高 CPU、磁盘繁忙、有关非空目录的各种错误等),所以我终止了它。
以稍微不同的方式检查(注意.
):
root@ts412:/QNAP/mounts/rest2# df -h Backup/.
Filesystem Size Used Avail Use% Mounted on
/dev/sdb10 831G 715G 75G 91% /QNAP/mounts/rest2
root@ts412:/QNAP/mounts/rest2# df -h Public/Backups/.
Filesystem Size Used Avail Use% Mounted on
/dev/sdb10 831G 715G 75G 91% /QNAP/mounts/rest2/Public
然后我记得我还有一个绑定挂载(它使通过 NFS 共享的名称更加友好)。所以我卸载了额外的绑定挂载:
root@ts412:/QNAP/mounts/rest2# umount /QNAP/mounts/rest2/Public
root@ts412:/QNAP/mounts/rest2# df -h Public/Backups/.
Filesystem Size Used Avail Use% Mounted on
/dev/sdb10 831G 715G 75G 91% /QNAP/mounts/rest2
root@ts412:/QNAP/mounts/rest2# mv Public/Backups Backup/
mv(1)
正如我所料,它是立即发生的。
因此,尽管有额外的mount(8)
s, 但源和目标始终位于同一文件系统中,这mount -o bind /QNAP/mounts/rest2/Backups /Backups
不会影响这一点。所以我想知道,如果mv(1)
返回/QNAP/mounts/rest2
一个的挂载点和/QNAP/mounts/rest2/Public
另一个的挂载点,它会错误地确定这两个文件位于不同的文件系统上吗?
严格来说,并不是它
mv
决定了这两个文件位于不同的文件系统,而是内核。mv
应该盲目地尝试一个基本rename
操作;只有当操作失败EXDEV
(表明两个操作数位于不同的文件系统上)时,它才会继续手动复制和删除文件。然而在 Linux 上,
rename
它只关心挂载点,而不考虑底层文件系统:mv
可以使用 Linux 特定的功能来检测这一点,并且已经向 coreutils 开发人员提出了建议(带有概念验证),但当时被认为过于复杂。另请参阅在同一个文件系统中 Mount--bind 移动文件就像在同一个文件系统中一样。
我需要添加更多信息,因为我认为我的表述有些模棱两可,并且被一个相关(且非常有趣)的问题绊倒了。
真正的坐骑是:
我有2个重要的绑定挂载:
(仅供参考,我只能这样做:exportfs -o rw *:/Public,否则我不会使用/Public 等)
如果我做了:
然后我发现我会遇到评论中讨论的设备间移动问题。
但是,如果我将示例从相对路径名转换为完整路径名,则尝试执行的操作是:
它的行为就像是跨设备移动,直到我这样做:
(这是上面绑定挂载中使用的名称)
然后:
即时(无需跨设备移动)
现在我开始怀疑我的记忆了,所以我回去做了:
(即将其移回)
并且它再次表现得像一个设备间移动!
然而经过检查,我发现
root@ts412:/QNAP/mounts/rest2# mount | grep Public
/dev/sdb10 on /Public 类型 ext4 (rw,relatime)
/dev/sdb10 on /Public 类型 ext4 (rw,relatime)
/dev/sdb10 on /Public 类型 ext4 (rw,relatime)
/dev/sdb10 on /QNAP/mounts/rest2/Public 类型 ext4 (rw,relatime)
root@ts412:/QNAP/mounts/rest2# mount | grep Backup
/dev/sdb10 on /Backup 类型 ext4 (rw,relatime)
“/Backup” 的值是我期望/想要的,而 /Public 的多个条目完全不是我期望的。
/QNAP/mounts/rest2/Public 不应该在那里......而且由于它的路径与上面的 mv(1) 匹配,这就是为什么它似乎是跨设备的(以Stephen Kitt的解释为准)
...那么它是如何到达那里的呢?根源是一个脚本(片段):
在这个例子中,它可能被称为
这应该会导致:
/dev/disk/by-label/rest2 挂载到 /QNAP/mounts/rest2 然后: /share/Backup 到 (sym) 链接到 /QNAP/mounts/rest2/Backup /share/Public 到 (sym) 链接到 /QNAP/mounts/rest2/Public ... 还有 /Public /Backup 的 NFS 导出...
然而代码中原本缺少注释“奇怪的是...”下的那两行。
该函数最终由 udev 调用(例如,当插入磁盘时),因此可能会运行多次。
我开始对此失去兴趣,但我认为重复发出的绑定挂载导致了虚假的额外挂载存在[堆叠?],从而导致了最初的问题!......唷。
所以我最初感到惊讶的是,尽管我没有使用绑定挂载,但它的存在导致原本常规的 mv(1) “混乱”......但事实证明我确实使用了绑定挂载,但没有意识到这一点。