如果文件被移动或删除,打开的文件句柄会发生什么?


概述

我们将了解当我们删除、移动或替换具有打开句柄的文件时,操作系统的行为方式。

我们首先简要介绍一下文件和inode。之后,我们将查看不同的场景,并了解每种情况下会发生哪种情况。

理解文件和inode

存储在 Linux 文件系统上的文件使用 inode 编号来跟踪其内容。

我们通常通过列出其文件名(链接)然后列出其对应的 inode 编号(硬链接)来列出目录的内容。

我们可以对文件使用 stat 并查看它引用哪个 inode -

$ touch inode_example
$ stat inode_example
   File: inode_example
   Size: 0              Blocks: 0          IO Block: 4096           regular empty file
Device: fd03h/64771d    Inode: 20448632    Links: 1

此文件链接到 inode 204488632,并且 stat 还显示有一个链接。让我们添加一个新的硬链接文件并再次在其上运行 stat。

$ ln inode_example hardlink_inode_example
$ stat inode_example
   File: inode_example
   Size: 0                 Blocks: 0             IO Block: 4096     regular empty file
Device: fd03h/64771d       Inode: 20448632       Links: 2

我们可以看到每个链接在其 URL 中都添加了一个增量值。我们现在也可以对 hardlink_inode_example 使用 stat -

$ stat hardlink_inode_example
   File: hardlink_inode_example
   Size: 0               Blocks: 0              IO Block: 4096       regular empty file
Device: fd03h/64771d     Inode: 20448632        Links: 2

与共享 inode 编号的硬链接不同,符号链接的 inode 编号与父目录分开。

Explore our latest online courses and learn new skills at your own pace. Enroll and become a certified expert to boost your career.

删除文件

每次我们创建新连接时,都会生成一个新进程。每个进程都有一个关联的内存空间,数据存储在其中。为了访问数据,操作系统必须将地址转换为内存空间内的物理位置。

让我们创建一个名为 remove_opened_file 的 shell 文件来测试我们的想法。

#!/bin/bash

FILE="/tmp/remove_example"
( 
   sleep 1
   echo "Before rm." >&4
   rm "$FILE"
   if [ ! -e "$FILE" ]; then
      echo "The file $FILE was removed."
   fi
   echo "After rm." >&4
) 4>"$FILE" &
( 
   sleep 2
   echo "This is the content in file handle 4:"
   cat <&4
) 4<"$FILE"

我们首先启动两个子 shell,然后我们使用重定向在每个子 shell 中创建和访问 /tmp/remove_examples 作为文件句柄 4。当子 shell 终止时,系统会关闭文件句柄。

让我们运行它并检查它是否有效。

$ ./remove_opened_file.sh
The file /tmp/remove_example was removed.
This is the content in file handle 4:
Before rm.
After rm.

当我们删除目录时,我们实际上并没有删除其中的文件;相反,我们只是删除了文件名和 inode 之间的链接。因此,尽管文件本身消失了,但它们仍然可以通过其 inode 访问。

当系统删除 inode 时,它会删除其内容。

  • 没有更多指向它的硬链接

  • 没有打开的文件句柄

删除文件并不一定意味着可用磁盘存储空间减少

移动文件

系统有两种方法可以将文件从一个位置传输到另一个位置 -

  • 重命名文件

  • 将其内容复制到目标,然后删除源

根据具体情况,程序会使用一种或另一种方法。例如,如果我们正在将文件从一个位置复制到另一个位置,那么程序可能会使用 Copy 命令。

文件被重命名

如果更改了原始文件名,则不会影响打开的文件。我们仍然可以写入和读取它们。由于 inode 相同,因此我们在移动后写入的内容将写入新位置。

让我们编写 move_open_file.sh 来测试移动打开的文件。

#!/bin/bash

FILE=/tmp/move_example
FILE_NEW=/tmp/move_example.new

( 
   echo "Before mv." >&4
   mv "$FILE" "$FILE_NEW";
   if [ ! -e "$FILE" -a -e "$FILE_NEW" ]; then
      echo "The file $FILE was moved to $FILE_NEW."
   fi
   echo "After mv." >&4
   echo "This is the content in $FILE_NEW:"
   cat "$FILE_NEW"
) 4>"$FILE"

在这里,我们使用子 shell 和重定向,就像我们在上一个示例中所做的那样。我们首先创建一个打开的文件句柄以写入输出文件。然后,我们运行 mV 命令。

我们可以看出文件是从相同位置复制的,因为它们具有相同的名称。现在让我们看看是否可以更改其名称。

文件 /tmp/move_example 已移动到 /tmp/move_example.new。

文件被复制

当系统将源文档复制到目标位置时,不会保留源文档的原始内容。相反,副本仅包含源文档中包含的信息。源文档关闭后,对源文档所做的任何更改都会丢失。

现在我们需要修改我们的 move_opened_files.sh 脚本,以便目标位于单独的文件系统中。通常 /tmp 和 /home 位于不同的文件系统中。结果如下 -

文件 /tmp/move_example 已移动到 /home/baeldung/move_by_copy_example.new。

这是 /home/baeldung/move_by_copy_example.new 中的内容 -

Before mv.

替换文件

当程序的新版本替换旧版本时,系统会删除程序的旧版本。这意味着我们可以继续使用旧版本的软件,直到我们关闭应用程序的窗口。

我们可以编写一个名为 replace_opened_file 的 shell 脚本,该脚本测试是否已将打开的文件替换为另一个文件。

#!/bin/bash

FILE=/tmp/replace_example
FILE_OLD=/tmp/replace_example.old

echo "This is the old file." > $FILE_OLD

( 
   sleep 1
   echo "Before mv." >&4
   mv "$FILE_OLD" "$FILE"
   echo "After mv." >&4
) 4>"$FILE" &

( 
   sleep 2
   echo "This is the content in file handle 4:"
   cat <&4
   echo "This is the content in $FILE:"
   cat $FILE
) 4<"$FILE"

在此脚本中,让我们首先创建同一文档的旧版本。然后我们打开一个新的 shell 并将同一文档的新版本写入其中。最后,我们打印新写入的文档的内容。

让我们运行 replace_opened_file。

$ ./replace_opened_file.sh

这是文件句柄 4 中的内容 -

Before mv.
After mv.

结论

我们研究了当我们删除文件、复制文件或更改其位置时会发生什么。

我们看到我们可以删除或替换单个文档,并且我们仍然可以使用其打开的文档句柄访问它,即使我们已删除或替换它。

一方面,我们看到如果我们将文件移动到同一目录中,则打开的句柄将指向新文件。但是,如果我们将其移动到另一个目录,则打开的文件将指向旧文件。

更新于: 2022-12-26

265 次浏览

开启你的 职业生涯

通过完成课程获得认证

开始
广告