Linux文件系统---软硬连接
Linux文件系统—软硬连接
1. 软硬链接是什么?
在Linux中,链接(Link) 本质上是一个指向另一个文件的文件。你可以把它理解为Windows系统中的“快捷方式”,但这个比喻并不完全准确,尤其是对于硬链接。Linux链接主要分为两种:硬链接(Hard Link) 和符号链接/软链接(Symbolic Link / Soft Link)。
Linux中怎么创建软连接?
使用 ln -s
命令创建软链接。
命令格式:
ln -s [源文件/目录] [链接文件名]
示例:
# 创建一个指向 /var/www/html 的软链接,名为 my_web
ln -s /var/www/html my_web# 创建一个指向 file.txt 的软链接,名为 soft_link.txt
ln -s file.txt soft_link.txt
操作后,使用 ls -l
查看,结果类似于:
lrwxrwxrwx 1 user user 13 Oct 4 15:30 soft_link.txt -> file.txt
(注意:第一列的 l
表示这是一个软链接文件,并且它会清晰地显示指向关系 ->
)
Linux中怎么创建硬链接?
使用 ln
命令(不带 -s
参数)创建硬链接。
命令格式:
ln [源文件] [链接文件名]
示例:
# 为 file.txt 创建一个硬链接,名为 hard_link.txt
ln file.txt hard_link.txt
操作后,使用 ls -l
查看,你会发现两个文件的文件大小、修改时间等元数据完全一致,并且第二列的“链接数”会从1变为2。
-rw-r--r-- 2 user user 123 Oct 4 15:25 file.txt
-rw-r--r-- 2 user user 123 Oct 4 15:25 hard_link.txt
# 注意这里的数字 '2',表示有两个文件名指向同一个inode
2. 软硬链接的本质是什么?
要理解本质,必须先了解Linux文件系统的三个核心部分:(这3个内容我在前一节都已经讲清楚了:链接:Linux(操作系统)文件系统–对未打开文件的管理-CSDN博客)
- 文件名(Filename):人类可读的标识符。
- inode:一个数据结构,存储了文件的元数据(权限、大小、时间戳、数据块位置等),但不包含文件名。它是文件的“身份证”。
- 数据块(Data Blocks):实际存储文件内容的地方。
- 硬链接的本质:多个不同的文件名指向同一个inode。它只是在某个目录下新增了一条“文件名 -> inode”的记录。删除一个硬链接(文件名)只是删除了这条记录,只要还有至少一个文件名指向这个inode,该inode和数据块就不会被释放。
目录中的内容大概如下图所示:
inode号 | 文件名长度 | 文件名 | 文件类型 |
---|---|---|---|
1001 | 8 | myfile.c | 普通文件 |
1002 | 6 | myfile | 可执行文件 |
1003 | 8 | Makefile | 普通文件 |
当我们使用指令创建myfile.c
的硬链接y1.link
的时候,本质上就是目录中多一条映射信息。
本来只有一个文件myfile.c
能映射到1001
inode号,也就是说在当前目录中我们只能通过myfile.c
这个文件名找到inode
号为1001
的inode
,这个inode
是一个结构体,里面存储的其实就是myfile.c
的文件属性,并且其中还有myfile.c
文件内容实际存储的位置,存储在哪个数据块。
简单来说是这样的:文件系统中存储文件属性的结构体inode
不存储文件名称,只用一个唯一的inode
号对文件进行唯一标识,系统只认inode
号。但是我们用户依旧可以使用文件名称来读取和写入文件,这是由于目录中存储着文件名→inode号的映射。每创建出一个文件,目录中就会自动写入一条映射信息,一个文件默认只有一条映射信息。也就是说在这个目录中,你只能通过myfile.c
来映射找出1001
号的inode
,进而找到文件数据。
而今,我们使用指令创建了myfile.c
的硬链接y1.link
,这个时候其实就是目录中多一条映射信息。
inode号 | 文件名长度 | 文件名 | 文件类型 |
---|---|---|---|
1001 | 8 | myfile.c | 普通文件 |
1002 | 6 | myfile | 可执行文件 |
1003 | 8 | Makefile | 普通文件 |
1001 | 7 | y1.link | 普通文件 |
这个时候,你不仅可以通过使用myfile.c
这个文件名读取文件(包括文件内容和属性)(找到1001号inode
),还可以通过y1.link
读取文件。
所以这个时候你就算使用指令删除了myfile.c
,你以旧可以通过y1.link
读取文件和修改文件。你删除myfile.c
实际上只是删除了当前目录中的一条映射信息罢了。
inode号 | 文件名长度 | 文件名 | 文件类型 |
---|---|---|---|
1002 | 6 | myfile | 可执行文件 |
1003 | 8 | Makefile | 普通文件 |
1001 | 7 | y1.link | 普通文件 |
由于依旧有文件名映射到1001号inode
,所以文件本身并不会被删除。只有当没有文件名映射到1001号inode
的时候,文件才可以被删除。
所以切记:硬链接并不是创建出一个全新的独立的文件,它没有创建出额外的文件,创建硬链接本质其实就是在当前目录多创建一条文件名→inode号的映射。它在目录中显示名称只是方便我们用户理解罢了。实际上它并不是一个独立的文件。
- “多个别名”的比喻:
myfile.c
和y1.link
就像同一个人的大名和小名,地位完全平等,没有谁是“原始”谁是“链接”的区别。删除任何一个,另一个依然可以正常访问文件。 - 链接数(Link Count):图中inode号旁边的“链接数: 2”这个细节非常重要。每增加一个硬链接,该inode的链接数就+1;每删除一个硬链接(文件名),链接数就-1。使用
ls -l
命令时,第二列显示的数字就是链接数。
在Linux系统中我们可以使用这个命令来查看文件的inode
号:
# 查看单个文件
ls -i filename# 查看目录中的所有文件和子目录
ls -i# 结合 -l 参数,可以同时查看文件详细信息和 inode
ls -li# stat 命令会显示文件的详细元数据,包括 inode 号、大小、权限、时间戳等。
stat filename
能看到myfile.c
和y1.link
显示的信息都是一样的,本质就是他们两个文件名都映射同一个inode
结构体,inode
存储的就是文件属性,所以两者的信息自然是一样的。
- 软链接的本质:一个独立的全新文件。它有自己的inode和数据块,但它的数据块里存储的内容不是文件数据,而是另一个文件的路径字符串。当你访问软链接时,系统会读取这个路径,然后去找到目标文件。
大家可以把Linux系统中的软链接理解为Windows系统中的快捷方式:
我们可以使用下面这条指令来获取链接文件的目标路径地址:
readlink link_to_file
我们能看到它有自己的inode和数据块。创建软连接其实就是创建一个全新的独立的文件,只不过它里面的文件内容不是文件数据,而是目标文件的路径字符串。
在我们还没有创建myfile.c
的软链接的时候,目录中的内容大概如下图所示:
inode号 | 文件名长度 | 文件名 | 文件类型 |
---|---|---|---|
1001 | 8 | myfile.c | 普通文件 |
1002 | 6 | myfile | 可执行文件 |
1003 | 8 | Makefile | 普通文件 |
当我们使用指令创建myfile.c
的软链接m1.link
的时候,本质上就是创建了一个新文件,文件内容中存储的是目标文件的路径字符串。应该很好理解。
inode号 | 文件名长度 | 文件名 | 文件类型 |
---|---|---|---|
1001 | 8 | myfile.c | 普通文件 |
1002 | 6 | myfile | 可执行文件 |
1003 | 8 | Makefile | 普通文件 |
1004 | 7 | m1.link | 链接文件 |
如果你使用软链接来使用目标文件,所以软链接的工作流程大概是这样的:先通过链接文件的名称找到映射的inode
,假设这个inode
的inode
号是1004。找到inode
号之后,就可以找到链接文件存储的文件内容,也就是找到目标文件文件的路径字符串。然后再用这个路径去找目标文件。
3. 软硬链接的原理是什么?
下图清晰地展示了软硬链接在文件系统中的工作原理:
硬链接原理:如同上图的右半部分,文件名A
和 硬链接文件名B
是平等的,它们都直接指向同一个inode
(X)。文件系统通过inode
中的**“链接数”** 来记录有多少个文件名指向它。只有当一个inode
的链接数降为0,且没有进程占用它时,其对应的数据块才会被标记为可覆盖。
软链接原理:如同上图的左半部分,软链接文件名C
指向一个独立的inode
(Y),这个inode
关联的数据块里只存了一个字符串——目标文件的绝对或相对路径。当你尝试打开软链接C时,系统会读取这个路径字符串,然后去解析它,找到最终的目标文件。如果目标文件被移动或删除了,这个路径就失效了,软链接就成了“断链”(Dangling Link)。
4. 软硬链接两者的同和不同
特性 | 硬链接 (Hard Link) | 软链接 (Symbolic Link / Soft Link) |
---|---|---|
inode | 与源文件相同 | 是独立的新inode |
跨文件系统 | 不支持(因为inode号是文件系统内的) | 支持 |
链接目录 | 不允许(超级用户可能可以,但极易导致循环,禁止使用)(这个问题我等会会专门讲) | 允许 |
原始文件删除 | 不影响,数据依然可通过硬链接访问 | 有影响,软链接将失效(“断链”) |
文件类型 | 看起来和普通文件无区别 | 在 ls -l 中显示为 l ,并注明指向关系 |
文件大小 | 与源文件大小相同 | 等于它存储的“路径字符串”的字符数 |
关系 | 像是文件的“多个别名”,地位平等 | 像是文件的“快捷方式”,依赖原文件 |
5. 软硬链接的适用场景分别是什么?
硬链接的适用场景:
- 防止误删:为重要文件创建一个硬链接放在备份目录。这样即使原始文件被误删,数据依然通过硬链接保留着。
- 文件同步:多个程序或用户需要以不同的文件名访问同一份数据,且要求任何一个文件名下的修改都能即时同步(因为它们本质是同一份数据)。
mv
命令的本质:当你移动一个文件 within the same filesystem(同一文件系统内)时,mv
命令实际上只是创建了一个新的目录记录(硬链接),然后删除了旧的记录,效率极高,无需复制数据。
假设我现在使用mv
指令将d1目录中的myfile.c
文件移动到d2目录。
使用mv命令前,旧目录d1:
inode号 | 文件名长度 | 文件名 | 文件类型 |
---|---|---|---|
1001 | 8 | myfile.c | 普通文件 |
1002 | 6 | myfile | 可执行文件 |
1003 | 8 | Makefile | 普通文件 |
使用mv命令后,旧目录d1:
inode号 | 文件名长度 | 文件名 | 文件类型 |
---|---|---|---|
1002 | 6 | myfile | 可执行文件 |
1003 | 2 | Makefile | 普通文件 |
新目录d2:
inode号 | 文件名长度 | 文件名 | 文件类型 |
---|---|---|---|
1001 | 8 | myfile.c | 普通文件 |
软链接的适用场景:
- 快捷方式:为深层目录中的可执行程序、库文件或配置文件创建一个软链接在
~/bin
或/usr/bin
等PATH路径下,方便直接调用。 - 兼容性与版本管理:例如,系统中需要多个版本的软件库(如
libssl.so.1.1
和libssl.so.3
),可以创建一个libssl.so
的软链接指向当前使用的版本,程序只需链接libssl.so
即可。(库的内容下一节讲:链接:) - 动态切换:例如,
/etc/localtime
是一个指向/usr/share/zoneinfo/
下某个时区文件的软链接,要修改系统时区,只需改变这个软链接的指向即可。 - 跨文件系统链接:这是硬链接无法做到但软链接非常擅长的。
6. 软硬链接有什么需要注意的?
- 硬链接不能链接目录:官方语法上不允许。这是为了避免在文件树中引入复杂的循环,导致
find
、tar
等工具出现逻辑错误。 - 硬链接不能跨文件系统:因为inode号只在同一个文件系统内唯一。
- 软链接的路径:创建软链接时,建议使用绝对路径(如
/home/user/file
),这样即使移动软链接本身,它仍然能正常工作。如果使用相对路径(如../file
),它是相对于软链接文件所在的位置进行解析的,而不是相对于你当前的工作目录。 - 循环风险:虽然软链接可以链接目录,但要小心创建循环(例如,
a
链接到b
,b
又链接回a
),这会让一些工具陷入无限循环。 - 断链问题:软链接的目标不存在时,它就成了“僵尸”链接,访问它会报 “No such file or directory” 错误。
- 权限差异:软链接本身的权限通常是
rwxrwxrwx
,但这并不重要,最终访问权限由目标文件的权限决定。而硬链接的权限就是原文件的权限。
7. 硬链接不能链接目录(.
和..
的本质)
前面我们说过硬链接不能链接目录,这是为了避免在文件树中引入复杂的循环,导致 find
、tar
等工具出现逻辑错误。但是实际上是这样吗?
我们来看一个现象:
所以这里我再准确的说一遍:没错!我们用户,包括root用户,都是不能为目录创建硬链接的,但是操作系统可以为目录创建硬链接,而且是特殊的硬链接。
那么操作系统为目录创建了什么特殊硬链接呢?这个东西其实我们很常见。
每创建出一个目录我们都能看到两个玩意:.
,..
。我们之前都知道.
其实代表的就是当前目录嘛,..
代表的就是当前目录的上级目录嘛。
那现在我们就可以明白为什么.
可以代表当前目录,那是因为这个.
就是操作系统为当前目录创建出来的一个硬链接,所以我们每次创建出一个新目录的时候,都能发现这个目录的硬链接数是2,就是这个原因。
那么..
是什么呢?很简单,和.
一样,它也是一个被操作系统创建出来的硬链接,是当前目录的上级目录的硬链接。
所以有一个技巧,可以用来计算当前目录中有几个子目录。也就是用当前目录的硬链接数 - 2就得到了当前目录的子目录数。
当前目录的子目录数 = 当前目录的硬链接数 - 2
那么至于为什么操作系统能为目录创建硬链接,那是操作系统的事情了,操作系统会对这两个东西(.
和..
)做特殊处理的。
补充内容
-
如何查找所有硬链接?
因为硬链接共享同一个inode,所以你可以通过
ls -i
先查看文件的inode号,然后用find
命令查找所有拥有此inode的文件。ls -i file.txt # 假设输出 inode 号 12345 find /path/to/search -inum 12345
-
链接数(Link Count)的含义:
使用
ls -l
看到的第二列数字就是链接数。对于一个新建的普通文件,链接数为1。每创建一个硬链接,该数字+1。每删除一个硬链接(或原始文件名),该数字-1。目录的链接数:新建目录的链接数通常是2(因为它本身.
和父目录指向它的..
),其子目录每多一个,它的链接数就+1(因为每个子目录里都有..
指向它)。 -
cp -l
和cp -s
:cp
命令的-l
选项代表“创建硬链接而非复制”,-s
选项代表“创建软链接而非复制”。这提供了另一种创建链接的方式。