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 的硬链接总数,并且对于每个硬链接都不能不同。它是一个文件,具有一组属性。唯一可以不同的属性是文件名,它不包含在 inode 中。位于同一目录中的单个 文件/inode 的硬链接必须具有不同的名称,因为单个目录中不能有重复的文件名。

文件的硬链接数与 ls -l 命令一起显示。如果您想显示实际的 inode 编号,则命令 ls -li 可以做到这一点。

符号(软)链接

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

这样做的缺点是:如果符号链接指向的硬链接被删除或重命名,则符号链接将断开。符号链接仍然存在,但它指向不再存在的硬链接。幸运的是,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 文件确实存在于主目录中,名称与 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 选项查看我们在主目录中创建的链接的 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 及其子目录中的文件。要查找所有链接,我们可以使用以下命令,该命令将您的主目录指定为搜索的起始位置。

[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 或分区上的文件系统到另一个文件系统上的文件系统)创建硬链接是不可能的。软链接是解决硬链接问题的手段。尽管它们可以达到相同的目的,但它们非常不同,了解这些差异很重要。

让我们首先在我们的 ~/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 的替代方案或补充,优点是您可以为任何类型的文件(尤其是库,根据我的经验,这似乎是最常见的需求)创建链接,而您可以为可执行文件指定路径。

硬链接有一个“陷阱”,IMHO 值得一提。

如果您使用的编辑器会自动备份 - 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.