在我为 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 中,这可能是用于在计算机用户之间共享文件的位置。
图 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.txt 或 main.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 课程编写了一个实验项目,才充分理解了链接的工作原理。 本文是对我在该课程中讲授的内容的简化,我希望它可以加快您的学习曲线。
4 条评论