为什么文件重定向在 cp/mv 失败的地方成功?



我正在尝试在 EKS 上运行 Alpine Linux 的 Docker 容器中覆盖/etc/resolv.conf。复制和移动都失败:

~ # cp /tmp/resolv.conf /etc/resolv.conf 
cp: can't create '/etc/resolv.conf': File exists
~ # mv /tmp/resolv.conf /etc/resolv.conf 
mv: can't rename '/tmp/resolv.conf': Resource busy

但是 shell 重定向成功,并保留了 inode:

~ # ls -li /etc/resolv.conf 
1412461 -rw-r--r--    1 root     root           131 Feb 17 20:45 /etc/resolv.conf
~ # cat /tmp/resolv.conf.1386 > /etc/resolv.conf 
~ # ls -li /etc/resolv.conf 
1412461 -rw-r--r--    1 root     root            39 Feb 18 19:17 /etc/resolv.conf

为什么重定向在繁忙的文件上成功?

重定向不会更改文件所在的目录,而mv会更改。当 - 就像这里一样 - 当你的"目录条目"实际上是一个挂载点时,这一点至关重要,内核正在覆盖目录条目并且不允许公开通常的内容。

那是:


mv foo bar做什么

mv foo bar

。在操作系统级别,调用系统调用rename("foo", "bar"),将目录条目bar替换为目录条目当前指向foo的索引节点。它根本不打开或更改预先存在的bar文件;它唯一要做的就是更改以前包含该文件的目录,改为指向条目中具有相同名称的不同文件。


echo hello > bar做什么

相比之下,echo hello >bar根本不会更改目录条目;它只是更改目录条目指向的文件。具体来说,它运行open("bar", O_TRUNC),并write(<fdno>, "hellon")open()返回的文件描述符。


那么,cp在做什么呢?

。这是个好问题。通常cp就地修改现有文件。您可以按如下方式进行测试:

echo ORIGINAL > file1
ln file1 file2
echo REPLACED > file3
cp file3 file1
cat file2

如果cp用一个全新的文件替换file1,那么与file2关联的索引节点将保持不变(包含"ORIGINAL")。但是,事实并非如此 -file2仅直接标识file1为其输出文件的cp修改,将该文件更改为包含REPLACED

我需要查看strace cp /tmp/resolv.conf /etc/resolv.conf的输出来诊断所描述的行为。

最新更新