20-硬链接与软链接

来源:互联网 发布:算法统宗一百馒头 编辑:程序博客网 时间:2024/06/09 19:00

首先,我们要理解的一个概念是:什么是链接?

链接是一个传送门。当你使用 vim 打开一个链接的时候,看到的内容是链接所指向的内容。当你使用 cat 后面加一个链接的时候,实际打印的也是链接所指向的内容。linux 为我们屏蔽了很多细节,让我们感觉,操作链接,好像是在操作原始文件一样。

既然如此,为什么还区分硬链接和软链接?它们的区别在哪里?


1 回忆 dir_entry

如果你不知道什么是 dir_entry,请复习 《文件系统初探》、《ext2 文件系统》、《ext2 文件系统实验》,并完成相关的实验。

一路读过来,相信你对文件系统底层已经比较了解,起码你得知道什么是 dir_entry(目录项),什么是 inode。本质上,他们都是结构体,只不过是有组织的保存在物理磁盘上。

本质上讲,目录也只不过是个文件而已,只是一个比较特殊的文件。之前在学 st_mode 的字段 的时候,我们就知道它的高4位就描述了文件的类型,其中就包括普通文件、目录文件、字符设备文件等等。

目录文件中存储的内容,就是一个个的 dir_entry 结构体。这很重要。

在 ext2 文件系统中,它被定义如下:

struct ext2_dir_entry_2 {    __u32   inode;          /* Inode number */    __u16   rec_len;        /* Directory entry length */    __u8    name_len;       /* Name length */    __u8    file_type;    char    name[255];    /* File name */};

时刻记住,目录只是个文件,和普通文件没什么两样。只不过,目录文件专门用来保存一个个的 dir_entry 结构体,或者说,保存的是 dir_entry 结构体数组。


2 回忆 inode 结构体

在 dir_entry 中有一个字段 inode,它保存的是 inode 结构体的索引号。根据这个索引号,就可以找到 inode 结构体的具体位置。这里我不打算把 inode 结构体完整的贴出来,它实在太大,我只引用其中现在我们要用的部分。想要看到完整的请参考《stat 函数 》。

struct ext2_inode {    // ...     __u16   i_links_count; // 有多少个 dir_entry 结构体引用了它    // ...    __u32   i_block[EXT2_N_BLOCKS]; // 数据块指针数组,可以根据这个成员来找到真正的的数据。    // ...}

inode 节点最重要的作用就是它的成员 i_block 保存了数据块的指针,根据 i_block 就可以找到真正的数据了。 《ext2 文件系统实验》 就已经完整的演示了如何找到文件的真正数据的。


3 dir_entry 与 inode 结构体分析实验

3.1 实验环境准备

不会使用 ln 命令不要紧,我们先把测试文件建立起来。当你读完后面的内容后,自然就明白了。

  • 搭建环境
$ mkdir link$ cd link$ echo hello > test.txt$ ln test.txt hellotest$ ln test.txt nihaotest$ ln -s test.txt /home/allen/learninglinux/filesystem/link/test.txt // 你的全路径可能和我有差异,但这不影响
  • 预览


这里写图片描述
图 1 实验需要的文件


3.2 实验分析

在图1 中使用了 ll -i 命令可以查看文件的详细内容(-i 选项可以把 inode 编号显示出来),其中第一列表示的是文件的 inode 编号,第 3 列是 inode 结构体中的成员 i_links_count 的值,也就是 inode 引用计数。

首要分析就是 link 目录,记住 link 也是文件。遗憾的是,我们没办法直接观察到 link 这个文件中存储的内容,不过前面的描述中我们已经知道 link 文件中保存的是一个个的 dir_entry 结构体。如此,我们可以自己根据图1分析出 link 文件中的每个 dir_entry 结构体是什么。它的文件内容就是一个大小为 6 的数组:

struct dir_entry entries[6]; // 想想为什么是 6?

这样一来,很容易知道 entries[0] 表示的是. 这个目录(当前目录),entries[1] 表示的是 .. 这个目录(上层目录)。

这里具体分析第 4 个 dir_entry.

entries[4].inode = 1050209; // 占用 4 字节entries[4].rec_len = 17; // 占用 2 字节; rec_len = 4 + 2 + 1 + 1 + 9 = 17entries[4].name_len = 9; // 占用 1 字节;entries[4].file_type = EXT2_FT_REG_FILE; // 占用 1 字节;entryies[4].name[9] = {'h','e','l', 'l', 'o', 't', 'e', 's', 't'}; // 占用 9 字节

其实到这里大家可以发现,dir_entry 的长度是不固定的,它的长度是由 dir_entry 的第二个成员 rec_len 来描述的,所以前面使用数组来定义可能不太准确,但是为了方便描述问题,大家就当成 dir_entry 的大小是固定的吧。


  • 硬链接

如果分析完 link 文件里所有的 dir_entry,聪明的小伙伴会发现,entries[4](对应 hellotest )、entries[5](对应 nihaotest )、entries[6](对应 test.txt) 这三个 dir_entry 结构里的 inode 成员的值全是 1050209。这有很深刻的意义,在哪里?——这三个 dir_entry 结构用的都是同一个 inode 结构体。 单纯的从文件名上看,你根本不知道哪个文件是原始文件,哪个是你创建的链接,不是吗?你看,这三个文件前面的文件类型,没有任何迹象能观察出来,谁真谁假。

到这里,其实硬链接就算搞定了。硬链接的本质就在此,dir_entry 结构体中的 inode 值相同,那么就是硬链接。而 inode 结构体中的i_links_count,就是说有多少个 dir_entry 使用了这个 inode 结构体。从图1 上可以看到,hellotest、nihaotest 和 test.txt 他们的引用计数是 3. 即使不用看图1,你也能推算出来。


  • 软链接

图1中还有一个浅蓝色文字 hahatest 。它的权限位 lrwxrwxrwx 暴露了它是一个符号链接(软链接又称符号链接)。它对应的是 link 文件中的 entries[2]. 它的各个值如下。

entries[2].inode = 1050208; // 占用 4 字节entries[2].rec_len = 16; // 占用 2 字节; rec_len = 4 + 2 + 1 + 1 + 8 = 16entries[2].name_len = 8; // 占用 1 字节;entries[2].file_type = EXT2_FT_SYMLINK; // 占用 1 字节;entryies[2].name[8] = {'h','a','h', 'a', 't', 'e', 's', 't'}; // 占用 8 字节

这个 inode 值(1050208) 很明显与 direntries[4,5,6] 中的 inode 值(1050209) 不同了。我们很好奇,这个 hahatest 文件里存储的是什么?好在 linux 提供了命令 readlink 让我们可以看到它的内容。

$ readlink hahatest

执行完后显示:

/home/allen/learninglinux/filesystem/link/test.txt

这一串正好有 50 个字符,你不用数了。你在回到图1看看,hahatest 文件的大小,是不是 50.

所以,这就是所谓的软链接。它只是个普通文件,如果文件系统没有把 entries[2].file_type 的值改为 EXT2_FT_SYMLINK 的话,它真的就只是个普通文件。


最后,请允许我从网上盗一张图来描述硬链接,软链接的关系。


这里写图片描述
图2 硬链接与软链接

4 总结

  • 掌握一切皆文件的哲理,目录是,符号链接也是。
  • 掌握 dir_entry 结构体,它保存在哪里?
  • 熟悉 inode 结构体。dir_entry 如何找到它?
  • 掌握硬链接和软链接底层原理
  • 知道可以 readlink 命令读取软链接文件本身的内容
  • 掌握 ln 命令的使用方法

最后,留一个小小问题。可以创建一个目录的硬链接吗?为什么?

0 0
原创粉丝点击