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

Linux 复制和移动的命令对inode的影响,linux,operating-system,inode,Linux,Operating System,Inode,我将inode解释为指向文件实际存储位置的指针 但我在理解上有问题: 如果我在文件2已经存在的地方使用cp file1 file2,inode不会改变。如果最初有一个指向file2的硬链接,它们现在都指向刚刚复制到这里的新文件 我能想到的唯一原因是Linux将其解释为修改 文件,而不是删除和创建新文件。我不明白为什么它是这样设计的? 但是当我使用mv file1 file2时,inode变为file1的inode inode是类Unix/Unix文件系统中文件的元数据集合,即关于文件的信息。它包

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

但我在理解上有问题:

如果我在文件2已经存在的地方使用cp file1 file2,inode不会改变。如果最初有一个指向file2的硬链接,它们现在都指向刚刚复制到这里的新文件

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


inode是类Unix/Unix文件系统中文件的元数据集合,即关于文件的信息。它包括权限数据、上次访问/修改时间、文件大小等

值得注意的是,文件名/路径不是inode的一部分。文件名只是inode的可读标识符。一个文件可以有一个或多个名称,其数量在inode中由其链接数和硬链接数表示。与inode关联的编号,inode编号,我相信您将其解释为其在磁盘上的物理位置,它只是inode的唯一标识符。inode确实包含文件在磁盘上的位置,但这不是inode编号

知道了这一点,你会发现cp和mv的功能不同。当您复制一个文件时,您正在用一个新名称创建一个新的inode,并将旧文件的内容复制到磁盘上的一个新位置。当你移动一个文件时,你所做的就是改变它的一个名字。如果新名称已经是另一个文件的名称,则该名称将与旧文件解除关联,并且旧文件的链接计数将减少1,并与新文件关联


您可以阅读有关inode的更多信息。

inode是Unix/Unix类文件系统中文件的元数据集合,即关于文件的信息。它包括权限数据、上次访问/修改时间、文件大小等

值得注意的是,文件名/路径不是inode的一部分。文件名只是inode的可读标识符。一个文件可以有一个或多个名称,其数量在inode中由其链接数和硬链接数表示。与inode关联的编号,inode编号,我相信您将其解释为其在磁盘上的物理位置,它只是inode的唯一标识符。inode确实包含文件在磁盘上的位置,但这不是inode编号

知道了这一点,你会发现cp和mv的功能不同。当您复制一个文件时,您正在用一个新名称创建一个新的inode,并将旧文件的内容复制到磁盘上的一个新位置。当你移动一个文件时,你所做的就是改变它的一个名字。如果新名称已经是另一个文件的名称,则该名称将与旧文件解除关联,并且旧文件的链接计数将减少1,并与新文件关联


您可以阅读有关inode的更多信息。

您正确地指出,cp将修改文件,而不是删除和重新创建

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

如您所见,它检测到file2存在,stat返回0,但随后打开它以写入O|u WRONLY | O|u TRUNC,而不首先执行取消链接

例如,参见POSIX.1-2017,其中目标文件只能在无法打开进行写入且使用-f时解除链接:

dest_文件的文件描述符应通过执行 与系统中定义的打开功能等效的操作 使用dest_文件作为路径调用POSIX.1-2017的接口卷 参数,并且O_WRONLY和O_TRUNC的位包含或作为 这是一个有争议的论点

如果获取文件描述符的尝试失败并且-f选项为 实际上,cp应通过执行操作尝试删除文件 相当于系统接口中定义的取消链接功能 使用dest_文件作为路径参数调用POSIX.1-2017卷。如果 此尝试成功后,cp应继续执行步骤3b

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

GNUCP上的-remove destination选项将使它执行您认为应该是默认的操作

下面是strace cp-remove destination file1 file2输出的相关部分。请注意这次取消链接

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
当您使用mv并且源路径和目标路径位于同一个文件系统上时,它将进行重命名,这将产生 在目标路径上取消链接任何现有文件的ct。以下是strace mv file1 file2输出的相关部分

无论是通过从cp-remove destination调用的unlink,还是作为从mv调用的rename效果的一部分,在目标路径被取消链接的情况下,它所指向的inode的链接计数都将减少,但是,如果链接计数仍然大于0,或者任何进程上有打开的文件句柄,则它将保留在文件系统上。指向此inode的任何其他硬链接(即它的其他目录项)将保留

使用ls-i进行调查 ls-i将在与-l组合时显示inode编号作为第一列,这有助于演示正在发生的事情

带有默认cp操作的示例

注:新inode 55的尺寸为3。未修改的inode 50仍然具有大小6

以mv为例


您说cp将修改文件而不是删除和重新创建文件是正确的

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

如您所见,它检测到file2存在,stat返回0,但随后打开它以写入O|u WRONLY | O|u TRUNC,而不首先执行取消链接

例如,参见POSIX.1-2017,其中目标文件只能在无法打开进行写入且使用-f时解除链接:

dest_文件的文件描述符应通过执行 与系统中定义的打开功能等效的操作 使用dest_文件作为路径调用POSIX.1-2017的接口卷 参数,并且O_WRONLY和O_TRUNC的位包含或作为 这是一个有争议的论点

如果获取文件描述符的尝试失败并且-f选项为 实际上,cp应通过执行操作尝试删除文件 相当于系统接口中定义的取消链接功能 使用dest_文件作为路径参数调用POSIX.1-2017卷。如果 此尝试成功后,cp应继续执行步骤3b

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

GNUCP上的-remove destination选项将使它执行您认为应该是默认的操作

下面是strace cp-remove destination file1 file2输出的相关部分。请注意这次取消链接

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
当您使用mv并且源路径和目标路径位于同一文件系统上时,它将进行重命名,这将产生在目标路径上取消任何现有文件链接的效果。以下是strace mv file1 file2输出的相关部分

无论是通过从cp-remove destination调用的unlink,还是作为从mv调用的rename效果的一部分,在目标路径被取消链接的情况下,它所指向的inode的链接计数都将减少,但是,如果链接计数仍然大于0,或者任何进程上有打开的文件句柄,则它将保留在文件系统上。指向此inode的任何其他硬链接(即它的其他目录项)将保留

使用ls-i进行调查 ls-i将在与-l组合时显示inode编号作为第一列,这有助于演示正在发生的事情

带有默认cp操作的示例

注:新inode 55的尺寸为3。未修改的inode 50仍然具有大小6

以mv为例


@阿兰尼维的回答涵盖了正在发生的事情,但这里也有一个隐含的原因

cp采用这种方式工作的原因是提供了一种用多个名称替换文件的方法,使所有这些名称都引用新文件。当cp的目标是一个已经存在的文件,可能通过硬链接或软链接具有多个名称时,cp将使所有这些名称都引用新文件。将不会有对遗留旧文件的“孤立”引用

有了这个命令,就可以很容易地获得“只需为一个名称更改文件”行为—首先取消文件链接。考虑到作为原语,很难实现“更改所有引用以指向新内容”的行为

当然,使用rm+cp有一些竞争条件问题,因为它是两个命令,这就是为什么在BSD unix中添加了install命令-它基本上只执行rm+cp,并进行一些检查,使其原子化在罕见的情况下,两个人试图同时安装到同一路径,还有一个更严重的问题,就是有人从你试图安装的文件中读取数据,导致普通cp出现问题。然后GNU版本添加了备份旧版本的选项和其他各种有用的簿记功能。

@alan iwi的回答涵盖了正在发生的事情,但这里也有一个隐含的原因

cp采用这种方式工作的原因是提供了一种用多个名称替换文件的方法,使所有这些名称都引用新文件。当cp的目标是一个已经存在的文件,可能通过硬链接或软链接具有多个名称时,cp将使所有这些名称都引用新文件。将不会有对遗留旧文件的“孤立”引用

有了这个命令,就可以很容易地获得“只需为一个名称更改文件”行为—首先取消文件链接。考虑到作为原语,很难实现“更改所有引用以指向新内容”的行为


当然,使用rm+cp有一些竞争条件问题,因为它是两个命令,这就是为什么在BSD unix中添加了install命令-它基本上只执行rm+cp,并进行一些检查,使其原子化在罕见的情况下,两个人试图同时安装到同一路径,还有一个更严重的问题,就是有人从您试图安装的文件中读取内容,导致普通cp出现问题。然后GNU版本添加了备份旧版本的选项和其他各种有用的簿记功能。

我可能应该在这些示例中包括ls选项-全职,以便更清楚地显示修改时间包括秒数。我可能应该在这些示例中包括ls选项-全职,以便更清楚地显示修改时间包括秒数。我没有真正考虑目标通过链接具有多个名称的情况。感谢您的解释。我还没有真正考虑过目的地通过链接有多个名称的情况。谢谢你的解释。
access("file2", W_OK)                   = 0
rename("file1", "file2")                = 0
$ 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
$ 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
$ 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