复制和移动命令对inode的影响

Gab*_*iel 5 linux operating-system inode

我将 inode 解释为指向文件实际存储位置的指针。

但我的理解有问题:

  1. 如果我在已经存在的cp file1 file2地方使用,inode 不会改变。如果最初存在指向 file2 的硬链接,那么它们现在都指向刚刚复制到此处的新文件。file2

    • 我能想到的唯一原因是Linux将此解释为修改 文件而不是删除并创建新文件。我不明白为什么要这样设计?
  2. 但是当我使用时mv file1 file2,inode 变成了 的 inode file1

ala*_*ani 7

您的说法是正确的,这cp将修改文件而不是删除并重新创建。

下面是底层系统调用的视图strace(部分输出strace cp file1 file2):

open("file2", O_WRONLY|O_TRUNC)         = 4
stat("file2", {st_mode=S_IFREG|0664, st_size=6, ...}) = 0
stat("file1", {st_mode=S_IFREG|0664, st_size=3, ...}) = 0
stat("file2", {st_mode=S_IFREG|0664, st_size=6, ...}) = 0
open("file1", O_RDONLY)                 = 3
fstat(3, {st_mode=S_IFREG|0664, st_size=3, ...}) = 0
open("file2", O_WRONLY|O_TRUNC)         = 4
fstat(4, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
read(3, "hi\n", 65536)                  = 3
write(4, "hi\n", 3)                     = 3
read(3, "", 65536)                      = 0
close(4)                                = 0
close(3)                                = 0
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它检测到file2存在(stat返回 0),但随后打开它进行写入(O_WRONLY|O_TRUNC),而无需先执行unlink.

请参阅 POSIX.1-2017 示例,它指定目标文件只能unlink在无法打开写入的情况下进行 -ed 并-f使用:

dest_file 的文件描述符应通过执行与 POSIX.1-2017 系统接口卷中定义的 open() 函数等效的操作来获取,该函数使用 dest_file 作为路径参数,并使用 O_WRONLY 和 O_TRUNC 的按位或或作为oflag 论证。

如果尝试获取文件描述符失败并且 -f 选项有效,则 cp 应尝试通过执行与使用 dest_file 调用的 POSIX.1-2017 系统接口卷中定义的 unlink() 函数等效的操作来删除文件作为路径参数。如果此尝试成功,cp 将继续执行步骤 3b。

这意味着,如果目标文件存在,-f如果cp进程对其具有写权限(不一定以拥有该文件的用户身份运行),则复制将成功(不诉诸行为),即使它对目标文件没有写权限。包含目录。相比之下,取消链接和重新创建将需要目录的写入权限。我推测这就是标准之所以如此的原因。

--remove-destinationGNU 上的选项将cp使其执行您认为应该默认的操作。

这是输出的相关部分strace cp --remove-destination file1 file2。注意unlink这次。

stat("file2", {st_mode=S_IFREG|0664, st_size=6, ...}) = 0
stat("file1", {st_mode=S_IFREG|0664, st_size=3, ...}) = 0
lstat("file2", {st_mode=S_IFREG|0664, st_size=6, ...}) = 0
unlink("file2")                         = 0
open("file1", O_RDONLY)                 = 3
fstat(3, {st_mode=S_IFREG|0664, st_size=3, ...}) = 0
open("file2", O_WRONLY|O_CREAT|O_EXCL, 0664) = 4
fstat(4, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
read(3, "hi\n", 65536)                  = 3
write(4, "hi\n", 3)                     = 3
read(3, "", 65536)                      = 0
close(4)                                = 0
close(3)                                = 0
Run Code Online (Sandbox Code Playgroud)

当您使用mv并且源路径和目标路径位于同一文件系统上时,它将执行 操作rename,这将具有取消链接目标路径中任何现有文件的效果。这是输出的相关部分strace mv file1 file2

access("file2", W_OK)                   = 0
rename("file1", "file2")                = 0
Run Code Online (Sandbox Code Playgroud)

在任一情况下,目标路径未链接(无论是通过unlink()as 调用 from显式调用,还是作为as 调用 fromcp --remove-destination的效果的一部分),它所指向的 inode 的链接计数将减少,但仍将保留在文件系统,如果链接计数仍然 > 0 或者任何进程在其上打开了文件句柄。到此 inode 的任何其他(硬)链接(即它的其他目录条目)都将保留。rename()mv

调查使用ls -i

ls -i将显示索引节点号(与 组合时作为第一列-l),这有助于演示正在发生的情况。

cp默认操作的示例

$ rm file1 file2 file3 

$ echo hi > file1
$ echo world > file2
$ ln file2 file3

$ ls -li file*
49 -rw-rw-r-- 1 myuser mygroup    3 Jun 13 10:43 file1
50 -rw-rw-r-- 2 myuser mygroup    6 Jun 13 10:43 file2
50 -rw-rw-r-- 2 myuser mygroup    6 Jun 13 10:43 file3

$ cp file1 file2 
$ ls -li file*
49 -rw-rw-r-- 1 myuser mygroup    3 Jun 13 10:43 file1
50 -rw-rw-r-- 2 myuser mygroup    3 Jun 13 10:43 file2   <=== exsting inode
50 -rw-rw-r-- 2 myuser mygroup    3 Jun 13 10:43 file3   <=== exsting inode
Run Code Online (Sandbox Code Playgroud)

(请注意,现有 inode 50 现在的大小为 3)。

示例为--remove-destination

$ rm file1 file2 file3
$ echo hi > file1
$ echo world > file2
$ ln file2 file3

$ ls -li file*
49 -rw-rw-r-- 1 myuser mygroup    3 Jun 13 10:46 file1
50 -rw-rw-r-- 2 myuser mygroup    6 Jun 13 10:46 file2
50 -rw-rw-r-- 2 myuser mygroup    6 Jun 13 10:46 file3

$ cp --remove-destination file1 file2
$ ls -li file*
49 -rw-rw-r-- 1 myuser mygroup 3 Jun 13 10:46 file1
55 -rw-rw-r-- 1 myuser mygroup 3 Jun 13 10:47 file2   <=== new inode
50 -rw-rw-r-- 1 myuser mygroup 6 Jun 13 10:46 file3   <=== existing inode
Run Code Online (Sandbox Code Playgroud)

(注意新的 inode 55 的大小为 3。未修改的 inode 50 的大小仍然为 6。)

示例为mv

$ rm file1 file2 file3
$ echo hi > file1
$ echo world > file2
$ ln file2 file3

$ ls -li file*
49 -rw-rw-r-- 1 myuser mygroup 3 Jun 13 11:05 file1
50 -rw-rw-r-- 2 myuser mygroup 6 Jun 13 11:05 file2
50 -rw-rw-r-- 2 myuser mygroup 6 Jun 13 11:05 file3

$ mv file1 file2 
$ ls -li file*
49 -rw-rw-r-- 1 myuser mygroup 3 Jun 13 11:05 file2  <== existing inode
50 -rw-rw-r-- 1 myuser mygroup 6 Jun 13 11:05 file3  <== existing inode
Run Code Online (Sandbox Code Playgroud)