【Linux】ln - 硬链接与软链接

Posted by 西维蜀黍 on 2019-01-10, Last Modified on 2021-09-21

背景

在介绍链接的概念之前,我们需要先Unix/Linux文件系统和硬盘储存的基础概念。

基础知识

inode 是什么?

理解 inode,要从文件储存说起。

文件储存在硬盘上,硬盘的最小存储单位叫做**“扇区”(Sector)**。每个扇区储存512字节(相当于0.5KB)。

操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个**“块”(block)**。这种由多个扇区组成的"块",是文件存取的最小单位。“块"的大小,最常见的是4KB,即连续八个 sector 组成一个 block。

文件数据都储存在"块"中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为"索引节点”。

每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。

inode 的内容

inode 包含文件的元信息,具体来说有以下内容:

  • 文件的字节数
  • 文件拥有者的User ID
  • 文件的Group ID
  • 文件的读、写、执行权限
  • 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。
  • 链接数,即有多少文件名指向这个inode
  • 文件数据block的位置

可以用stat命令,查看某个文件的inode信息:

$ stat deploy.sh
16777220 8623911579 -rwxr-xr-x 1 weishi wheel 0 250 "Feb 10 16:36:51 2019" "Jan 31 12:23:49 2019" "Jan 31 12:23:49 2019" "Jan 31 11:50:03 2019" 4096 8 0 deploy.sh

总之,除了文件名以外的所有文件信息,都存在inode之中。至于为什么没有文件名,下文会有详细解释。

inode 的大小

inode 也会消耗硬盘空间,所以硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是 inode 区(inode table),存放 inode 所包含的信息。

每个 inode 节点的大小,一般是 128 字节或 256 字节。inode 节点的总数,在格式化时就给定,一般是每 1KB 或每 2KB 就设置一个 inode 。假定在一块 1GB 的硬盘中,每个 inode 节点的大小为 128 字节,每 1KB 就设置一个 inode,那么 inode table 的大小就会达到 128MB,占整块硬盘的12.8%。

查看每个硬盘分区的 inode 总数和已经使用的数量,可以使用 df 命令。

$ df -i
Filesystem    512-blocks      Used Available Capacity iused               ifree %iused  Mounted on
/dev/disk1s1   976490568 923300584  26609536    98% 3519925 9223372036851255882    0%   /
devfs                671       671         0   100%    1162                   0  100%   /dev
/dev/disk1s4   976490568  25168808  26609536    49%      12 9223372036854775795    0%   /private/var/vm
map -hosts             0         0         0   100%       0                   0  100%   /net
map auto_home          0         0         0   100%       0                   0  100%   /home
/dev/disk1s3   976490568   1009752  26609536     4%      24 9223372036854775783    0%   /Volumes/Recovery

inode 号码

每个 inode 都有一个号码,操作系统用 inode 号码来识别不同的文件。

这里值得重复一遍,Unix/Linux 系统内部不使用文件名,而使用 inode 号码来识别文件。对于系统来说,文件名只是 inode 号码便于识别的别称或者绰号。

表面上,用户通过文件名,打开文件。实际上,系统内部这个过程分成三步:首先,系统找到这个文件名对应的 inode 号码;其次,通过 inode 号码,获取 inode 信息;最后,根据 inode 信息,找到文件数据所在的 block,读出数据。

使用 ls -i 命令,可以看到文件名对应的 inode 号码:

$ ls -i db.json
8626038086 db.json

目录文件

Unix/Linux 系统中,目录(directory)也是一种文件。打开目录,实际上就是打开目录文件。

目录文件的结构非常简单,就是一系列目录项(dirent)的列表。每个目录项,由两部分组成:所包含文件的文件名,以及该文件名对应的 inode 号码

ls -i 命令列出整个目录文件,即文件名和inode号码:

$ ls -i Modem\ Scripts
8617985873 Bluetooth LAN Access.ccl            8617986179 MultiTech MTA128NT ML-PPP.ccl
8617985890 Farallon LAN:Modem PC Card.ccl      8617986196 MultiTech MTA128NT PPP.ccl
8617985907 Farallon Netopia 56K MLPPP.ccl      8617986213 MultiTech MTA128ST ML-PPP w:DBA.ccl
8617985924 Farallon Netopia 56K.ccl            8617986230 MultiTech MTA128ST ML-PPP.ccl

什么是链接?

链接简单说实际上是一种文件共享的方式,是 POSIX 中的概念,主流文件系统都支持链接文件。

一般情况下,文件名和 inode 号码是"一一对应"关系,即每个inode号码对应一个文件名。但是,Unix/Linux 系统允许,多个文件名指向同一个inode号码。

这意味着,可以用不同的文件名访问同样的内容;对文件内容进行修改,会影响到所有文件名;但是,删除一个文件名,不影响另一个文件名的访问。这种情况就被称为**“硬链接”(hard link)**。

硬连接的作用是允许一个文件拥有多个有效路径名(或者说多个文件名),这样用户就可以建立硬连接到重要文件,以防止“误删”的功能。

因为,该特定文件有一个以上的有效路径名(文件名)与同一 inode 号码的映射。只删除一个有效路径名(文件名)并不影响 inode 本身和有效路径名(文件名)之间的映射,只有当最后一个有效路径名(文件名)被删除后,inode 才会被释放。也就是说,文件真正删除的条件是与之相关的所有有效路径名(或者说硬连接文件)均被删除。

ln命令可以创建硬链接:

$ ln file hard
$ ls -li
8626081538 -rw-r--r--   2 weishi  staff       12 22 Feb 11:01 file
8626081538 -rw-r--r--   2 weishi  staff       12 22 Feb 11:01 hard

运行上面这条命令以后,发现源文件与目标文件的inode号码相同(均为 8626081538),说明这两个有效路径名(文件名)都指向同一个 inode。inode 信息中有一项叫做"链接数",记录指向该 inode 的有效路径名(文件名)总数。

反过来,删除一个文件名,就会使得 inode 节点中的"链接数"减1。当这个值减到0,表明没有文件名指向这个 inode,系统就会回收这个 inode 号码,以及其所对应block区域。

这里顺便说一下目录文件的"链接数"。创建目录时,默认会生成两个目录项:".“和”.."。前者的inode号码就是当前目录的inode号码,等同于当前目录的"硬链接";后者的inode号码就是当前目录的父目录的inode号码,等同于父目录的"硬链接"。所以,任何一个目录的"硬链接"总数,总是等于2加上它的子目录总数(含隐藏目录)。

除了硬链接以外,还有一种特殊情况。

文件 A 和文件 B 的 inode 号码虽然不一样,但是文件 A 的内容是文件 B 的路径。读取文件 A 时,系统会自动将访问者导向文件 B。因此,无论打开哪一个文件,最终读取的都是文件 B。这时,文件 A 就称为文件 B 的**“软链接”(soft link)或者"符号链接(symbolic link)**。

这意味着,文件 A 依赖于文件 B 而存在,如果删除了文件 B,打开文件 A 就会报错:“No such file or directory”。这是软链接与硬链接最大的不同:文件 A 指向文件 B 的文件名,而不是文件 B 的inode号码,文件 B 的inode"链接数"不会因此发生变化。

使用方法

ln -s [源文件] [目标文件]

当我们需要在不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件。

我们只要在某个固定的目录,放上该文件,然后在其它的目录下用ln命令链接(link)它,而不重复占用磁盘空间。例如:ln -s /bin/less /usr/local/bin/less -s 是代号(symbolic)的意思。

当我们访问/usr/local/bin/less时,操作系统会自动将这个路径转换为ln -s /bin/less

ln命令会保持每一处链接文件的同步性,也就是说,不论你改动了哪一处,其它的文件都会发生相同的变化;

软链接和硬链接的区别

为了更清晰的看到他们的区别,我们可以做一个实验。

我们首先在自己的一个工作目录下创建一个文件,然后对这个文件进行链接的创建:

$ touch myfile && echo "This is a plain text file." > myfile
$ cat myfile
This is a plain text file.

现在我们创建了一个普通地不能再普通的文件了。然后我们对它创建一个硬链接,并查看一下当前目录:

$ ln myfile hard
$ ls -li
8622074481 -rw-r--r--   2 weishi  staff       27 10 Jan 11:15 hard
8622074481 -rw-r--r--   2 weishi  staff       27 10 Jan 11:15 myfile

ls 结果的最左边一列,是文件的 inode 值,你可以简单把它想成 C 语言中的指针。它指向了物理硬盘的一个区块,事实上文件系统会维护一个引用计数,只要有文件指向这个区块,它就不会从硬盘上消失。

你也看到了,这两个文件就如同一个文件一样,inode 值相同,都指向同一个区块。

我们修改一下刚才创建的 hard 链接文件:

$ echo "New line" >> hard
$ cat myfile
This is a plain text file.
New line

可以看到,这两个文件果真就是一个文件。 下面我们看看软链接(也就是符号链接)和它有什么区别。

$ ln -s myfile soft
$ ls -li
8623913939 lrwxr-xr-x   1 weishi  staff        6 31 Jan 11:57 soft -> myfile
8622074481 -rw-r--r--   2 weishi  staff       36 10 Jan 11:18 myfile

你会发现,这个软链接的 inode 竟然不一样啊,并且它的文件属性上也有一个 l的 flag,这就说明softmyfile文件分别指向了物理硬盘的不同区块。


下面我们试着删除 myfile 文件,然后分别输出软硬链接的文件内容:

$ rm myfile
$ cat hard
This is a plain text file.
New line
$ cat soft
cat: soft: No such file or directory

观察:

  • 之前的硬链接没有丝毫地影响,因为它 inode 所指向的区块由于有一个硬链接在指向它,所以这个区块仍然有效,并且可以访问到。

  • 然而软链接的 inode 所指向的内容实际上是保存了一个绝对路径,当用户访问这个文件时,系统会自动将其替换成其所指的文件路径,然而这个文件已经被删除了,所以自然就会显示无法找到该文件了。

为验证这一猜想,我们再向这个软链接写点东西:

$ echo "Something" >> soft
$ ls

hard   myfile soft

可以看到,刚才删除的 myfile 文件竟然又出现了!这就说明,当我们写入访问软链接时,系统自动将其路径替换为其所代表的绝对路径,并直接访问那个路径了。

踩坑

当对文件夹建立软链接,且使用相对路径时,在macOS下,发现会建立一个无效的软连接。

$ ls ./test1
800.jmx
$ ls test2
$ ln -s ./test1 ./test2
$ cd ./test2/test1
cd: too many levels of symbolic links: ./test2/test1

总结

到这里我们其实可以总结一下了:

  • 硬链接:与普通文件没什么不同,inode 都指向同一个文件在硬盘中的区块
  • 软链接:保存了其代表的文件的绝对路径,是另外一种文件,在硬盘上有独立的区块,访问时替换自身路径。

Reference