在Unix系统中为什么删除文件后文件仍然可访问?

4
我考虑到一个并发问题(在Solaris中):如果在读取时有人试图删除同一个文件会发生什么。我对在Solaris/Linux中检查文件是否存在有疑问。假设我有一个名为test.txt的文件,我已经在vi编辑器中打开了它,然后我又打开了一个重复的会话并删除了该文件,但即使在删除该文件后,我仍然能够读取该文件。所以这是我的问题:
  • 在读取时,我需要考虑任何锁定机制,以便在读取时没有人能够删除同一个文件。

  • 为什么在Windows中显示不同的行为(例如,如果文件在某个编辑器中打开,则无法删除该文件)

  • 在删除该文件后,如果我没有从vi编辑器中关闭文件,我如何仍然能够读取该文件。

我询问的是一般的文件,但是特定于平台,即Unix。如果我正在使用Java程序(缓冲读取器)读取文件,并且在读取时删除了文件,缓冲读取器是否仍能够读取下一块文件?


5
因为Unix的问世早于Windows,所以我认为一个更好的问题应该是:“为什么Windows工程师没有成功实现这种行为?”因为Unix的行为要好得多... - unwind
当您打开文件进行读取(例如使用vi),其内容将存储在内存中。同时,它可能会被删除,因此每次退出vi时,它将从您的视线中消失。但是,如果您在vi内部保存它:w,那么它将被重新创建。 - fedorqui
你是在问特定的vi,还是一般的文件?至少在Linux上,vim在编辑文件时不会保持原始文件打开。 - nos
我是在一般情况下询问文件,但也包括特定平台。比如说,如果我正在使用一个Java程序(缓冲读取器)来读取文件,而在读取过程中文件被删除了,那么缓冲读取器是否仍然能够读取文件的下一部分呢? - Sanjay
2个回答

9
你的问题基本上有两个或三个不相关的部分。像文本编辑器这样的工具在编辑会话开始时喜欢将整个文件读入内存中。想象一下,你输入的每个字符都会立即保存到磁盘中,在它后面的所有字符都会被重写一遍以腾出空间。这将是非常糟糕的。更好的做法是,你实际上正在编辑的内容是文件的内存表示(可能带有一些元数据的行指针数组),只有在显式保存时才会转换回线性流。
任何相对较新的vim版本都会在原始位置删除正在编辑的文件时通知你,提示信息会如下所示:
E211: File "filename" no longer available

这个警告不仅适用于Unix。如果您删除正在编辑的文件,Windows上的gvim也会给出此警告。它提醒您在退出之前保存您正在使用的版本,如果您不想丢失文件。

(注意:警告不会立即出现 - 只有在从编辑器切换到其他应用程序后再次回到编辑器时,vim才会检查原始文件是否存在。)

所以这是第一个问题,文本编辑器的行为 - 它们没有理由在整个会话中保持文件处于打开状态,因为除了启动和保存操作外,它们实际上并未使用该文件。

第二个问题,为什么一些Windows编辑器保持文件处于打开和锁定状态 - 我不知道,可能是Windows人太疯狂了。

第三个问题实际上与Unix有关,为什么删除文件后仍然可以访问打开的文件 - 这是最有趣的问题。直接呈现答案会让您感到震惊:

没有任何命令、函数、系统调用或其他方法实际上请求删除文件。

rm和任何其他可能看起来像删除文件的命令的基础上,都有一个名为unlink的系统调用。它被称为unlink,而不是removedeletefile或类似的名称,因为它并没有删除文件。它删除了一个链接(也称为目录条目),该链接是文件和目录中名称之间的关联。(注意:ANSI C添加了remove作为更通用的函数,以取悦那些没有意图实现Unix文件系统语义的非Unix用户,但在Unix上,如果目标是目录,则remove只是rmdir,对于其他一切则是unlink。)

一个文件可以有多个链接(请参见如何使用ln命令创建它们),这意味着相同的文件由多个名称知道。如果您删除其中之一,其他链接将保留,文件将不会被删除。当您删除最后一个链接时会发生什么?好吧,现在你有一个没有名称的文件。但是名称只是对文件的一种引用。还有至少两种其他引用文件的方式:文件描述符和mmap区域。当最后一个对文件的引用消失时,文件就被删除了。

由于引用以多种形式出现,因此许多事件都可能导致文件被删除。以下是一些示例:

  • unlink (rm, etc.) - 删除文件名,而不是文件本身。只有在文件没有被使用时才会删除文件。
  • 关闭文件描述符
    • dup2 (在替换为不同的文件描述符的副本之前,可以隐式地关闭文件描述符)
    • exec (可以通过close-on-exec标志关闭文件描述符)
  • munmap (取消映射内存区域)
    • mmap (如果您在已经映射的地址处创建新的内存映射,则旧的映射将被取消映射)
  • 进程终止 (关闭进程的所有文件描述符并取消映射进程的所有内存映射)
    • 正常退出
    • 由内核生成的致命信号 (^C,段错误)
    • 从另一个进程发送的致命信号 (kill)

我不会称之为完整的列表。我也不鼓励任何人尝试构建完整的列表。只需知道rm是“删除名称”,而不是“删除文件”,文件在不再使用时就会消失。

如果您想立即销毁文件的内容,请截断它。所有已经使用它的进程将发现其大小突然变为0。(这在普通文件访问方法的情况下是破坏性的。为了更彻底地销毁它,以至于即使有人拥有原始磁盘访问权限也无法读取以前存在的内容,您需要覆盖它。有一个叫做shred的工具可以实现这个功能。)


1
我认为你的问题与Windows / Linux的区别无关。它是关于VI的工作原理的。 使用VI编辑文件时,VI会创建一个.swp文件。而.swp文件是您实际编辑的文件。同时,如果其他用户删除了原始文件,也不会影响您的编辑。 当您在VI中键入:w时,VI将使用.swp文件覆盖原始文件。

我不使用VI,所以我不会回答这个问题,但我认为答案可能更简单。只要文件有一个打开的句柄,你就可以从驱动器中删除它,但在所有句柄被销毁之前,它仍然存在。 - krowe
@krowe 虽然在Unix系统中通常是正确的,但是Vim在编辑文件时不会保持文件句柄处于打开状态。 - nos
.swp文件只有在使用vi编辑器时才会创建吗?如果在阅读时删除它,通常会发生什么? - Sanjay
1
@Sanjay: 在读取文件时?这取决于操作系统和/或文件系统。尽管可能会这样做,但Windows通常(*)不会干扰打开的文件。另一方面,Linux更好地分离了文件和文件名;大多数文件系统都有一个inode表(文件列表)和一个目录(文件名树),而文件名只是指向文件的硬链接。Linux将尝试(*)忘记文件名,但会推迟忘记文件,直到没有硬链接或打开的句柄为止。((*)文件系统、权限、打开模式标志等可能会影响并改变操作系统的行为。) - cHao
@krowe 还要注意,这种行为在某种程度上取决于文件系统 - 有些文件系统不支持“文件已删除但未被释放”的语义。尽管如nos所指出的那样,大多数Unix/Linux文件系统都可以处理这个问题... - twalberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接