Linux 文件系统中链接的用户指南

了解如何使用链接,通过从 Linux 文件系统目录树中的多个位置访问文件,使任务更轻松。
744 位读者喜欢这个。
Links

Paul Lewin。Opensource.com 修改。CC BY-SA 2.0

在我为 Opensource.com 撰写的关于 Linux 文件系统各个方面的文章中,包括 Linux 的 EXT4 文件系统简介在 Linux 中管理设备Linux 文件系统简介Linux 用户逻辑卷管理指南,我简要提到了 Linux 文件系统的一个有趣特性,该特性通过从文件系统目录树中的多个位置访问文件,可以使某些任务更轻松。

Linux 文件系统链接有两种类型:硬链接和软链接。这两种类型链接之间的差异很大,但都用于解决类似的问题。它们都为单个文件提供多个目录条目(或引用),但它们以完全不同的方式执行此操作。链接功能强大,并为 Linux 文件系统增加了灵活性,因为 一切皆文件

例如,我发现有些程序需要特定版本的库。当库升级替换旧版本时,程序会崩溃,并显示一条错误消息,其中指定了旧的、现在缺少的库的名称。通常,库名称中唯一的更改是版本号。根据我的猜测,我只是添加了一个指向新库的链接,但链接的名称是旧库的名称。我再次尝试该程序,它运行得非常好。好吧,这个程序是一个游戏,而且每个人都知道游戏玩家为了保持游戏的运行会付出多大的努力。

事实上,几乎所有应用程序都使用通用名称链接到库,链接名称中只有主版本号,而链接指向实际的库文件,该文件也有一个次版本号。在其他情况下,所需的文件已从一个目录移动到另一个目录,以符合 Linux 文件规范,并且旧目录中存在链接,以便与尚未赶上新位置的程序向后兼容。如果您对 /lib64 目录进行长列表,您可以找到许多这两个例子。

lrwxrwxrwx.  1 root root       36 Dec  8  2016 cracklib_dict.hwm -> ../../usr/share/cracklib/pw_dict.hwm 
lrwxrwxrwx.  1 root root       36 Dec  8  2016 cracklib_dict.pwd -> ../../usr/share/cracklib/pw_dict.pwd 
lrwxrwxrwx.  1 root root       36 Dec  8  2016 cracklib_dict.pwi -> ../../usr/share/cracklib/pw_dict.pwi
lrwxrwxrwx.  1 root root       27 Jun  9  2016 libaccountsservice.so.0 -> libaccountsservice.so.0.0.0 
-rwxr-xr-x.  1 root root   288456 Jun  9  2016 libaccountsservice.so.0.0.0 
lrwxrwxrwx   1 root root       15 May 17 11:47 libacl.so.1 -> libacl.so.1.1.0 
-rwxr-xr-x   1 root root    36472 May 17 11:47 libacl.so.1.1.0 
lrwxrwxrwx.  1 root root       15 Feb  4  2016 libaio.so.1 -> libaio.so.1.0.1 
-rwxr-xr-x.  1 root root     6224 Feb  4  2016 libaio.so.1.0.0 
-rwxr-xr-x.  1 root root     6224 Feb  4  2016 libaio.so.1.0.1 
lrwxrwxrwx.  1 root root       30 Jan 16 16:39 libakonadi-calendar.so.4 -> libakonadi-calendar.so.4.14.26 
-rwxr-xr-x.  1 root root   816160 Jan 16 16:39 libakonadi-calendar.so.4.14.26 
lrwxrwxrwx.  1 root root       29 Jan 16 16:39 libakonadi-contact.so.4 -> libakonadi-contact.so.4.14.26 

/lib64 目录中的一些链接

上面的 /lib64 目录的长列表显示,文件模式中的第一个字符是字母“l”,这意味着每个都是软链接或符号链接。

硬链接

Linux 的 EXT4 文件系统简介 中,我讨论了这样一个事实,即每个文件都有一个 inode,其中包含有关该文件的信息,包括属于该文件的数据的位置。图 2 在该文章中显示了一个指向 inode 的单个目录条目。每个文件必须至少有一个指向描述该文件的 inode 的目录条目。目录条目是一个硬链接,因此每个文件至少有一个硬链接。

在下面的图 1 中,多个目录条目指向一个 inode。这些都是硬链接。我已经使用波浪号 (~) 约定缩写了三个目录条目的位置,其中 ~ 在本例中等效于 /home/user。请注意,第四个目录条目位于一个完全不同的目录 /home/shared 中,这可能是用于在计算机用户之间共享文件的位置。

fig1directory_entries.png图 1

硬链接仅限于包含在单个文件系统中的文件。“文件系统”在这里用作挂载在指定挂载点上的分区或逻辑卷 (LV) 的意义,在本例中为 /home。这是因为 inode 编号仅在每个文件系统内是唯一的,并且不同的文件系统(例如 /var/opt)将具有与我们的文件的 inode 相同的编号。

因为所有硬链接都指向包含有关文件的元数据的单个 inode,所以所有这些属性都是文件的一部分,例如所有权、权限以及指向 inode 的硬链接总数,并且对于每个硬链接不能不同。它是一个文件,具有一组属性。唯一可以不同的属性是文件名,该属性不包含在 inode 中。由于单个目录中不能有重复的文件名,因此位于同一目录中的单个 file/inode 的硬链接必须具有不同的名称。

使用 ls -l 命令显示文件的硬链接数。如果要显示实际的 inode 编号,则使用命令 ls -li

符号(软)链接

硬链接和软链接(也称为符号链接(或 symlink))之间的区别在于,硬链接直接指向属于该文件的 inode,而软链接指向目录条目,即其中一个硬链接。因为软链接指向文件的硬链接而不是 inode,所以它们不依赖于 inode 编号,并且可以在文件系统之间工作,跨越分区和 LV。

缺点是:如果 symlink 指向的硬链接被删除或重命名,则 symlink 将断开。symlink 仍然存在,但它指向不再存在的硬链接。幸运的是,ls 命令使用红色背景上的闪烁白色文本在长列表中突出显示断开的链接。

实验室项目:链接实验

我认为理解硬链接和软链接的使用和差异的最简单方法是做一个实验室项目。该项目应在空目录中以非 root 用户身份完成。我为这个项目创建了 ~/temp 目录,你也应该这样做。它创建了一个安全的地方来完成项目,并提供了一个新的空目录来工作,以便只有与该项目关联的文件位于那里。

初始设置

首先,创建将在其中执行此项目所需任务的临时目录。确保当前工作目录 (PWD) 是您的主目录,然后输入以下命令。

mkdir temp

更改为 ~/temp 以使用此命令使其成为 PWD。

cd temp

要开始,我们需要创建一个可以链接到的文件。以下命令执行此操作并提供一些内容。

du -h > main.file.txt

使用 ls -l 长列表来验证文件是否已正确创建。它应该与我的结果类似。请注意,文件大小仅为 7 个字节,但您的文件大小可能会相差一两个字节。

[dboth@david temp]$ ls -l 
total 4 
-rw-rw-r-- 1 dboth dboth 7 Jun 13 07:34 main.file.txt

请注意列表中文件模式后面的数字“1”。该数字表示该文件存在的硬链接数。目前,它应该是 1,因为我们尚未创建指向我们测试文件的任何其他链接。

硬链接实验

硬链接创建一个指向同一 inode 的新目录条目,因此当向文件添加硬链接时,您将看到链接数增加。确保 PWD 仍然是 ~/temp。创建一个指向文件 main.file.txt 的硬链接,然后对目录进行另一个长列表。

[dboth@david temp]$ ln main.file.txt link1.file.txt 
[dboth@david temp]$ ls -l 
total 8 
-rw-rw-r-- 2 dboth dboth 7 Jun 13 07:34 link1.file.txt 
-rw-rw-r-- 2 dboth dboth 7 Jun 13 07:34 main.file.txt

请注意,两个文件都有两个链接并且大小完全相同。日期戳也相同。这实际上是一个具有一个 inode 和两个链接(即指向它的目录条目)的文件。创建指向此文件的第二个硬链接并列出目录内容。您可以创建指向现有链接之一的链接:link1.file.txtmain.file.txt

[dboth@david temp]$ ln link1.file.txt link2.file.txt ; ls -l
total 16 
-rw-rw-r-- 3 dboth dboth 7 Jun 13 07:34 link1.file.txt 
-rw-rw-r-- 3 dboth dboth 7 Jun 13 07:34 link2.file.txt 
-rw-rw-r-- 3 dboth dboth 7 Jun 13 07:34 main.file.txt

请注意,此目录中的每个新硬链接必须具有不同的名称,因为两个文件(实际上是目录条目)不能在同一目录中具有相同的名称。尝试使用与现有名称之一相同的目标名称创建另一个链接。

[dboth@david temp]$ ln main.file.txt link2.file.txt 
ln: failed to create hard link 'link2.file.txt': File exists

显然这不起作用,因为 link2.file.txt 已经存在。到目前为止,我们只在同一目录中创建了硬链接。因此,在您的主目录(我们到目前为止一直在使用的 temp 目录的父目录)中创建一个链接。

[dboth@david temp]$ ln main.file.txt ../main.file.txt ; ls -l ../main*
-rw-rw-r--    4 dboth dboth     7 Jun 13 07:34 main.file.txt

上面的列表中的 ls 命令显示 main.file.txt 文件确实存在于 home 目录中,并且与 temp 目录中的文件同名。当然,这些不是不同的文件;它们是同一个文件,具有指向同一 inode 的多个链接(目录条目)。为了更好地说明下一点,添加一个不是链接的文件。

[dboth@david temp]$ touch unlinked.file ; ls -l
total 12
-rw-rw-r-- 4 dboth dboth 7 Jun 13 07:34 link1.file.txt
-rw-rw-r-- 4 dboth dboth 7 Jun 13 07:34 link2.file.txt
-rw-rw-r-- 4 dboth dboth 7 Jun 13 07:34 main.file.txt
-rw-rw-r-- 1 dboth dboth 0 Jun 14 08:18 unlinked.file

使用 ls 命令的 -i 选项查看硬链接以及新文件的 inode 编号。

[dboth@david temp]$ ls -li
total 12
657024 -rw-rw-r-- 4 dboth dboth 7 Jun 13 07:34 link1.file.txt
657024 -rw-rw-r-- 4 dboth dboth 7 Jun 13 07:34 link2.file.txt
657024 -rw-rw-r-- 4 dboth dboth 7 Jun 13 07:34 main.file.txt
657863 -rw-rw-r-- 1 dboth dboth 0 Jun 14 08:18 unlinked.file

注意上面示例中文件模式左侧的数字 657024。那是 inode 编号,所有三个文件链接都指向同一个 inode。您也可以使用 -i 选项查看我们在 home 目录中创建的链接的 inode 编号,也会显示相同的值。只有一个链接的文件的 inode 编号与其他文件不同。请注意,您的系统上的 inode 编号会有所不同。

让我们更改其中一个硬链接文件的大小。

[dboth@david temp]$ df -h > link2.file.txt ; ls -li
total 12
657024 -rw-rw-r-- 4 dboth dboth 1157 Jun 14 14:14 link1.file.txt
657024 -rw-rw-r-- 4 dboth dboth 1157 Jun 14 14:14 link2.file.txt
657024 -rw-rw-r-- 4 dboth dboth 1157 Jun 14 14:14 main.file.txt
657863 -rw-rw-r-- 1 dboth dboth    0 Jun 14 08:18 unlinked.file

现在,所有硬链接的文件大小都比以前大了。 这是因为实际上只有一个文件,它通过多个目录条目链接到。

我知道下一个实验可以在我的计算机上进行,因为我的 /tmp 目录位于一个单独的 LV 上。如果您的文件系统位于单独的 LV 或不同的分区上(如果您不使用 LV),请确定您是否可以访问该 LV 或分区。如果不能,您可以尝试插入 USB 记忆棒并挂载它。如果其中一个选项对您有效,您可以进行此实验。

尝试在 /tmp (或您的不同文件系统目录所在的任何位置)中创建一个指向 ~/temp 目录中的文件的链接。

[dboth@david temp]$ ln link2.file.txt /tmp/link3.file.txt
ln: failed to create hard link '/tmp/link3.file.txt' => 'link2.file.txt': 
Invalid cross-device link

为什么会出现此错误?原因是每个单独的可挂载文件系统都有自己的一组 inode 编号。简单地通过 inode 编号跨整个 Linux 目录结构引用文件可能会导致混淆,因为每个已挂载的文件系统中都可能存在相同的 inode 编号。

有时您可能需要找到属于单个 inode 的所有硬链接。 您可以使用 ls -li 命令找到 inode 编号。 然后,您可以使用 find 命令查找具有该 inode 编号的所有链接。

[dboth@david temp]$ find . -inum 657024 
./main.file.txt
./link1.file.txt
./link2.file.txt

请注意, find 命令没有找到指向此 inode 的所有四个硬链接,因为我们从当前目录 ~/temp 开始。 find 命令仅查找 PWD 及其子目录中的文件。 要查找所有链接,我们可以使用以下命令,该命令将您的 home 目录指定为搜索的起点。

[dboth@david temp]$ find ~ -samefile main.file.txt 
/home/dboth/temp/main.file.txt
/home/dboth/temp/link1.file.txt
/home/dboth/temp/link2.file.txt
/home/dboth/main.file.txt

如果您没有以非 root 用户身份获得权限,您可能会看到错误消息。 此命令还使用 -samefile 选项而不是指定 inode 编号。 这与使用 inode 编号的效果相同,如果您知道其中一个硬链接的名称,则可能更容易。

软链接实验

正如您刚才所看到的,跨文件系统边界(即,从一个 LV 或分区上的文件系统到另一个 LV 或分区上的文件系统)创建硬链接是不可能的。软链接是一种解决硬链接问题的手段。 尽管它们可以达到相同的目的,但是它们非常不同,并且了解这些差异非常重要。

让我们首先在我们的 ~/temp 目录中创建一个符号链接来开始我们的探索。

[dboth@david temp]$ ln -s link2.file.txt link3.file.txt ; ls -li
total 12
657024 -rw-rw-r-- 4 dboth dboth 1157 Jun 14 14:14 link1.file.txt
657024 -rw-rw-r-- 4 dboth dboth 1157 Jun 14 14:14 link2.file.txt
658270 lrwxrwxrwx 1 dboth dboth   14 Jun 14 15:21 link3.file.txt -> 
link2.file.txt
657024 -rw-rw-r-- 4 dboth dboth 1157 Jun 14 14:14 main.file.txt
657863 -rw-rw-r-- 1 dboth dboth    0 Jun 14 08:18 unlinked.file

硬链接(具有 inode 编号 657024 的链接)未更改,并且为每个链接显示的硬链接数也未更改。 新创建的符号链接具有不同的 inode,编号为 658270 。 名为 link3.file.txt 的软链接指向 link2.file.txt 。 使用 cat 命令显示 link3.file.txt 的内容。 符号链接的文件模式信息以字母 "l" 开头,表明此文件实际上是一个符号链接。

在上面的示例中,符号链接 link3.file.txt 的大小仅为 14 字节。 那是文本 link3.file.txt -> link2.file.txt 的大小,它是目录条目的实际内容。 目录条目 link3.file.txt 不指向 inode;它指向另一个目录条目,这使得它可用于创建跨越文件系统边界的链接。 因此,让我们创建之前从 /tmp 目录尝试创建的链接。

[dboth@david temp]$ ln -s /home/dboth/temp/link2.file.txt 
/tmp/link3.file.txt ; ls -l /tmp/link*
lrwxrwxrwx 1 dboth dboth 31 Jun 14 21:53 /tmp/link3.file.txt -> 
/home/dboth/temp/link2.file.txt

删除链接

当您需要删除链接或它们指向的文件时,您应该考虑其他一些事项。

首先,让我们删除链接 main.file.txt 。 请记住,指向 inode 的每个目录条目都只是一个硬链接。

[dboth@david temp]$ rm main.file.txt ; ls -li
total 8
657024 -rw-rw-r-- 3 dboth dboth 1157 Jun 14 14:14 link1.file.txt
657024 -rw-rw-r-- 3 dboth dboth 1157 Jun 14 14:14 link2.file.txt
658270 lrwxrwxrwx 1 dboth dboth   14 Jun 14 15:21 link3.file.txt -> 
link2.file.txt
657863 -rw-rw-r-- 1 dboth dboth    0 Jun 14 08:18 unlinked.file

链接 main.file.txt 是创建文件时创建的第一个链接。 现在删除它仍然会将原始文件及其数据保留在硬盘上以及所有剩余的硬链接。 要删除文件及其数据,您必须删除所有剩余的硬链接。

现在删除 link2.file.txt 硬链接。

[dboth@david temp]$ rm link2.file.txt ; ls -li 
total 8 
657024 -rw-rw-r-- 3 dboth dboth 1157 Jun 14 14:14 link1.file.txt 
658270 lrwxrwxrwx 1 dboth dboth   14 Jun 14 15:21 link3.file.txt -> 
link2.file.txt 
657024 -rw-rw-r-- 3 dboth dboth 1157 Jun 14 14:14 main.file.txt 
657863 -rw-rw-r-- 1 dboth dboth    0 Jun 14 08:18 unlinked.file

请注意软链接会发生什么。 删除软链接指向的硬链接会留下一个断开的链接。 在我的系统上,断开的链接以颜色突出显示,目标硬链接正在闪烁。 如果需要修复断开的链接,您可以在同一目录中创建另一个与旧链接同名的硬链接,只要并非所有硬链接都已被删除。 您也可以重新创建链接本身,使该链接保持相同的名称,但指向其余的硬链接之一。 当然,如果不再需要该软链接,可以使用 rm 命令将其删除。

unlink 命令也可以用于删除文件和链接。 它非常简单,没有像 rm 命令那样的选项。 但是,它更准确地反映了删除的底层过程,因为它删除了指向要删除的文件的链接(目录条目)。

最后的想法

在使用两种类型的链接很长时间之后,我才开始了解它们的功能和特性。 我花时间为一个 Linux 课程编写了一个实验项目,才充分理解了链接的工作原理。 本文是对我在该课程中讲授的内容的简化,我希望它可以加快您的学习曲线。

标签
David Both
David Both 是一位开源软件和 GNU/Linux 倡导者、培训师、作家和演讲者。 自 1996 年以来,他一直在使用 Linux 和开源软件,自 1969 年以来,他一直在使用计算机。他是“系统管理员的 Linux 哲学”的坚定支持者和传播者。

4 条评论

在某些用途中,链接是使用 $PATH 的替代方案或补充,其优点是您可以为任何类型的文件(尤其是库,根据我的经验,这似乎是最常见的需求)创建链接,而您可以为可执行文件指定路径。

硬链接有一个“陷阱”,恕我直言,值得一提。

如果您使用的编辑器会自动备份 - emacs 肯定就是其中之一 - 那么您最终可能会得到已编辑文件的新版本,而备份是链接的副本,因为编辑器只是将文件重命名为备份名称(使用 emacs,test.c 将被重命名为 test.c~),而保存为旧名称的新版本不再链接。

符号链接避免了这个问题,所以我倾向于在需要的地方将它们用于源代码。

尊敬的管理员
我有一个关于 /dev 目录的问题。
为什么总是有大量的空设备文件?
是否可以在启动时查询硬件,然后只生成与实际硬件对应的文件?
感谢任何答案

简短的答案是肯定的,Linux 一直在按照您建议的方式进行操作,已经很长时间了。 我确信似乎设备文件的数量比实际设备多得多,但实际上大多数都以某种方式使用。

我的文章“在 Linux 中管理设备”,网址为 https://open-source.net.cn/article/16/11/managing-devices-linux 正好讨论了这一点。

感谢您的问题。

回复 作者:David Bowskill (未验证)

Creative Commons License本作品已根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.