文章目录
- 💐专栏导读
- 💐文章导读
- 🐧认识磁盘
- 🐧逻辑抽象
- 🐧文件系统
- 🐦Block
- 🐦Block Group
- 🐔Block Group 的组成部分
- 🐦Superblock(超级区块)
- 🐦Group Description(组描述)
- 🐦Block bitmap(区块对照表)
- 🐦Inode bitmap(inode 对照表)
- 🐦Inode table
- 🐦Data block
- 🐧Inode VS 文件名
- 🐦重新认识目录
- 🐦直接索引与二级索引
- 🐧软硬链接
- 🐦软硬链接对比
- 🐦硬链接
- 🐦硬链接的作用
- 🐦软链接
- 🐦软链接的作用
💐专栏导读
🌸作者简介:花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。
🌸专栏简介:本文收录于 Linux从入门到精通,本专栏主要内容为本专栏主要内容为Linux的系统性学习,专为小白打造的文章专栏。
🌸相关专栏推荐:C语言初阶系列、C语言进阶系列 、C++系列、数据结构与算法。
💐文章导读
本章我们将深入学习Linux文件系统,了解物理磁盘,建立从磁盘到逻辑线性地址的抽象过程。并以Linux EXT2 为例详细理解文件系统,以及软硬链接的基本概念及原理。
🐧认识磁盘
磁盘是一种用于存储和检索数据的数据存储设备
,通常是计算机系统中的重要组成部分。它是一种非易失性存储设备,意味着它可以在断电后保持数据的存储状态。磁盘以其数据存储介质为基础,根据其工作原理和使用场景的不同,可以分为多种类型,包括硬盘驱动器(Hard Disk Drive,HDD)和固态硬盘(Solid State Drive,SSD)等。
下图为一个老式磁盘。
以下是一个磁盘各部分结构。
其中一个磁盘有很多盘片,一个盘片又有两个盘面,盘面上可以进行读写数据。
下图为一个盘面的示意图。
首先我们来认识几个名词:
- 磁道(Cylinder):磁盘表面被分为很多同心圆,每个同心圆被称为一个磁道;
- 磁头(Head):一个磁头负责一个面的读取;
- 扇区(Sector):每个磁道被划分为若干段,每一段为一个扇区,每一个扇区的存储容量为512字节(或4KB)。
那么如何在磁盘中定位一块扇区呢?
- 先定位在磁盘的哪一面——通过磁头的编号来确定,一个磁头对应一个盘面;
- 再确定该扇区所在的磁道——每个磁道也有自己的编号;
- 最后确定在哪一块扇区——每个扇区也有自己的编号;
这就是所谓的 CHS 定位法
。当我们存储一个文件时,一个文件占用若干个扇区。
🐧逻辑抽象
现在我们知道一个扇区是可以被定位的。那么 OS 是不是直接通过一个 CHS 地址来访问磁盘的呢?答案是否定的,原因如下:
- 倘若 OS 直接使用 CHS 地址,那么磁盘物理结构发生变化,OS 也要跟着改变,这是万万不能的。所以我们要实现 OS 与硬件之间的解耦;
- 扇区的单位是 512 字节,而 OS 进行 I/O 时基本单位为 4KB 。所以 OS 应当与磁盘地址建立映射关系;
那么我们就必须将CHS地址转化为我们熟悉的线性地址(LBA),方便与OS建立联系。
现在我们定位一块扇区只需要一个数组下标即可。OS 是以4KB为单位进行 I/O 的,相当于8块扇区的大小。
当我们把磁盘看作一个大数组后,OS对于磁盘的管理变成了对数组的管理。
🐧文件系统
我们以Linux EXT2
文件系统来做说明。现在以一个 500GB 的磁盘为例,OS在管理该磁盘时采用的是分治
的方法,先分区(例如C盘和D盘),再分组
。
🐦Block
对于 ext2 文件系统来说,硬盘分区首先被分割为一个一个的逻辑块(Block),每个 Block 就是实际用来存储数据的单元,大小相同,Block 按照0,1,2,3 的顺序进行编号,第一个 Block 的编号为 0。
ext2 文件系统支持的 Block 的大小有 1024 字节、2048 字节和 4096 字节,Block 的大小在创建文件系统的时候可以通过参数指定,如果不指定,则会从 /etc/mke2fs.conf
文件中读取对应的值。原则上,Block 的大小与数量在格式化后就不能够发生改变了,每个 Block 内最多只会存放一个文件的数据(即不会出现两个文件的数据被放入同一个 Block 的情况),如果文件大小超过了一个 Block 的 size,则会占用多个 Block 来存放文件,如果文件小于一个 Block 的 size,则这个 Block 剩余的空间就浪费掉了。
可以使用 dumpe2fs 命令查看 Block 的大小:
$ sudo dumpe2fs /dev/sda1 | grep "Block size:"
注意,Ext2 文件系统的 block 主要有下面一些特点:
block 的大小与数量在格式化完就不能够再改变了(除非重新格式化)
- 每个 block 内最多只能够放置一个文件的数据;
- 如果文件大于 block 的大小,则一个文件会占用多个 block 数量;
- 若文件小于 block,则该 block 的剩余容量就不能够再被使用了;
🐦Block Group
Block 在逻辑上被划分为多个 Block Group
,每个 Block Group 包含的 Block 数量相同,具体是在 SuperBlock 中通过 s_block_per_group 属性定义的(最后一个 Block Group 除外,最后剩下的 Block 数量可能小于 s_block_per_group,这些 Block 会被划分到最后一个 Block Group 中)。dumpe2fs
命令会列出所有的 Block Group 信息,但是在统计信息中却没有说明当前的文件系统中包含有多少个 Block Group。
🐔Block Group 的组成部分
如上图所示,每个 Block Group 都由下面几个组成部分:
- Superblock(超级块);
- Group Description(组描述);
- Block bitmap(块位图);
- Inode bitmap(inode 位图);
- Inode table(inode 表);
- Data Blocks(数据块);
🐦Superblock(超级区块)
Superblock
是记录整个文件系统相关信息的地方,其实上除了第一个 block group 内会含有 superblock 之外,后续的 block group 不一定都包含 superblock,如果包含,也是做为第一个 block group 内 superblock 的备份。superblock 记录的主要信息有:
- block 与 inode 的总量;
- 未使用与已使用的 inode/block 数量;
- block 与 inode 的大小(block 为 1,2,4K,inode 为 128 Bytes 或 256 Bytes);
- filesystem 的挂载时间、最近一次写入数据的时间、最近一次检验磁盘(fsck)的时间等文件系统的相关信息;
- 一个 valid bit 数值,若此文件系统已被挂载,则 valid bit 为 0,若未被挂载,则 valid bit 为 1;
Superblock 的大小为 1024 Bytes,它非常重要,因为分区上重要的信息都在上面。如果 Superblock 挂掉了,分区上的数据就很难恢复了
。
🐦Group Description(组描述)
Group Description 用来描述每个 group 的开始与结束位置的 block 号码
,以及说明每个块(superblock、bitmap、inodemap、datablock) 分别介于哪一个 block 号码之间。
🐦Block bitmap(区块对照表)
在创建文件时需要为文件分配 block,届时就会选择分配空闲的 block 给文件使用。如何查看 block 是否已经被使用了呢?此时就需要借助于 block bitmap
了。通过 block bitmap 可以知道哪些 block 是空的,因此系统就能够很快地找到空闲空间来分配给文件。同样的,在删除某些文件时,文件原本占用的 block 号码就要释放出来,此时在 block bitmap 当中相对应到该 block 号码的标志就需要修改成"空闲"。这就是 block bitmap 的作用。
🐦Inode bitmap(inode 对照表)
inode bitmap 与 block bitmap 的功能类似,只是 block bitmap 记录的是使用与未使用的 block 号,而 inode bitmap 则记录的是使用与未使用的 inode 号。
🐦Inode table
Inode table 中存放着一个个 inode,inode 的内容记录文件的属性以及该文件实际数据是放置在哪些 block 内,inode 记录的主要的文件属性如下:
- 该文件的读写权限(rwx)
- 该文件的拥有者和所属组(owner/group)
- 该文件的容量
- 该文件的 ctime(创建时间)
- 该文件的 atime(最近一次的读取时间)
- 该文件的 mtime(最近修改的时间)
- 该文件的特殊标识,比如 SetUID 等
- 该文件真正内容的指向(pointer)
inode 的数量与大小也是在格式化时就已经固定了的,另外 inode 还有如下特点:
- 每个 inode 大小均固定为 128 Bytes(新的 ext4 为 256 Bytes)
每个文件都仅会占用一个 inode
文件系统能够创建的文件数量与 inode 的数量相关
系统读取文件时需要先找到 inode,并分析 inode 所记录的权限与使用者是否符合,若符合才能够开始读取 block 的内容
🐦Data block
Data block 是用来存放文件内容的地方,Ext2 文件系统 1K、2K 和 4K 大小的 block。在格式化文件系统时 block 的大小就确定了,并且每个 block 都有编号。需要注意的是,由于 block 大小的差异,会导致文件系统能够支持的最大磁盘容量和最大单个文件的大小并不相同。
🐧Inode VS 文件名
上面我们提到 inode 具有以下特点:
- 每个 inode 大小均固定为 128 Bytes(新的 ext4 为 256 Bytes)
- 每个文件都仅会占用一个 inode
- 文件系统能够创建的文件数量与 inode 的数量相关
- 系统读取文件时需要先找到 inode,并分析 inode 所记录的权限与使用者是否符合,若符合才能够开始读取 block 的内容
总的来说,在Linux下查找一个文件,需要根据文件的 inode 来查找,包括读取文件内容等操作,一个文件对应一个 inode,且该文件的 inode 属性和该文件对应的数据块是有映射关系的。
在Linux系统中,操作系统只认识 inode,文件的 inode 属性中并不存在文件名,文件名仅仅是给用户使用的。
🐦重新认识目录
首先目录是文件,当然也有自己的 inode。那么目录是否有自己的数据块呢?答案是有的!目录的数据块里面保存的是该目录下的所有文件的文件名和 inode 对应的映射关系。
当我们访问一个文件时,我们总是在特定的目录下访问的。以 cat log.txt 为例:
- 首先我们需要在 log.txt 所在的目录下找到 log.txt 所对应的 inode 编号。
- 一个目录也是一个文件,一定隶属于一个分区,结合 inode,在该分区中找到分组,在该分组的inode table 中找到文件的 inode。
- 通过inode 和对应的datablock的映射关系,找到该文件的数据块,并加载到 OS,并在显示器显示。
🐦直接索引与二级索引
一个 inode 中存储着与 datablock 的映射关系,我们粗略的表示一个 inode 如下:
struct inode
{
int inode_number; // inode 编号
int ref_count; // 链接数
mode_t mode; // 文件权限
int uid;
int gid;
int size;
//...
int datablocks[NUM]; // 所指向的datablock
}
一个磁盘中的特定分区、特定分组下存在着若干个 datablock(假设大小都为4KB),每个 datablock都有着自己的编号。
上面的 datablocks 数组中记录着存储该文件内容的若干个 datablock。假设 datablocks 仅仅只存储着存储文件内容用到的 datablock,那么即使 NUM 非常大,为 100 0000 ,那这个文件的存储上限也就是 4GB 左右,但是在我们的印象中,一个文件大到上 100GB 的都有,如果真如同刚才所说的,那肯定是行不通的。
其实在 datablocks 数组中,前面一部分存储的是直接索引,也就是用到哪些 datablock 就存储哪些 datablock 的索引。再往后一部分是二级索引,二级索引指向的 datablock 中存储的并不是直接的文件数据,而是其他 datablock 的索引。倘若需求再大一些,甚至还会用到三级索引。
🐧软硬链接
学习了文件系统,我们再来看看有趣的软硬链接。
🐦软硬链接对比
首先我们通过软硬链接的对比来知道二者的差别。
首先来创建一个文件 myfile.txt;
$ touch myfile.txt
为该文件设置软链接
;
$ ln -s myfile.txt my_soft
为该文件设置硬链接
;
$ ln myfile.txt my_hard
查看三个文件,为ls
添加-i
参数,可查看文件的 inode 编号;
$ ls -il
total 0
541408 -rw-rw-r-- 2 hxy hxy 0 Feb 28 13:04 myfile.txt
541408 -rw-rw-r-- 2 hxy hxy 0 Feb 28 13:04 my_hard
541409 lrwxrwxrwx 1 hxy hxy 10 Feb 28 13:07 my_soft -> myfile.txt
首先我们可以直观地看到的是:myfile.txt
与 my_hard
的 inode 编号相同;myfile.txt
与 my_soft
的inode 编号不同。
我们刚才知道,一个文件对应一个inode,那么现在这两个文件的 inode 编号相同,这是怎么回事呢?
🐦硬链接
我们不妨大胆的猜测一下,假设之前的理论没错,那么myfile.txt
与my_hard
虽然名字不一样,但两个文件其实是一个文件,指向的是同一个 datablocks。我们现在加以验证:
首先在myfile.txt
中写入内容:
$ echo "hello world" >> myfile.txt
$ echo "hello world" >> myfile.txt
$ echo "hello world" >> myfile.txt
$ echo "hello world" >> myfile.txt
$ echo "hello world" >> myfile.txt
$ cat myfile.txt
hello world
hello world
hello world
hello world
hello world
现在myfile.txt
中已经被写入了一些内容。接下来我们查看my_hard
中的内容。
$ cat my_hard
hello world
hello world
hello world
hello world
hello world
结果显而易见。我们的猜想是对的。
其实在文件系统中,一个文件可以有多个文件名指向同一份数据内容,这些文件名被称为硬链接。在硬链接中,所有硬链接文件都指向相同的物理存储块,它们实际上共享相同的 inode。
每个文件在文件系统中都有一个唯一的 inode,该 inode 包含有关文件的元数据和指向文件数据的指针。硬链接通过在文件系统中创建一个新的目录项,并将该目录项与相同的 inode 关联来实现。由于所有硬链接文件共享相同的 inode,它们实际上是对同一份数据内容的不同引用。
硬链接通常只能在同一文件系统中创建,并且不能指向目录。删除一个硬链接文件并不会删除实际的数据,直到所有硬链接都被删除,即引用计数降为零时,文件系统才会释放相关的inode和存储块。
通过 ls -l
我们还可以查看文件的硬链接数:
🐦硬链接的作用
硬链接的特性我们了解了,但是它有什么用呢?其实,我们平时没少用到它。还记得初学Linux时,我们遇到的两个隐藏文件.
(当前目录) 和. .
(上级目录)吗?
$ mkdir dir
$ ll -a dir
total 8
drwxrwxr-x 2 hxy hxy 4096 Feb 28 13:39 .
drwxrwxr-x 3 hxy hxy 4096 Feb 28 13:39 ..
我们注意到在dir目录下.
的硬链接数是2
,. .
的硬链接数是3
。其实 . 代表当前目录,它是在上级目录下dir
的硬链接。而 . .
代表上级目录,它是上级目录的硬链接。那为什么 . .
的硬链接数是 3
呢?
因为. . 指向上级目录并且在上级目录下还存在一个.
也指向上级目录。
当然硬链接的作用不仅仅在于当前目录与上级目录,我们只是以此为例。
🐦软链接
$ ls -il
total 0
541408 -rw-rw-r-- 2 hxy hxy 0 Feb 28 13:04 myfile.txt
541408 -rw-rw-r-- 2 hxy hxy 0 Feb 28 13:04 my_hard
541409 lrwxrwxrwx 1 hxy hxy 10 Feb 28 13:07 my_soft -> myfile.txt
my_soft
的硬链接数是1
,且 inode 也与myfile.txt
不相同,显然它们是独立的两个文件。但是当我们查看my_soft
中的内容时:
$ cat my_soft
hello world
hello world
hello world
hello world
hello world
我们发现,my_soft
与myfile.txt
中的内容竟然也相同。
其实软链接是一种特殊的文件,它包含一个指向另一个文件或目录的路径的引用。与硬链接不同,软链接不共享相同的 inode 或存储块,而是简单地包含指向目标文件的路径。当你访问软链接时,系统会自动解析路径并定位到目标文件。
软链接有如下特点:
-
独立的inode: 软链接和目标文件有不同的inode。软链接的inode包含有关软链接本身的元数据和指向目标文件的路径。
-
路径引用: 软链接存储的是目标文件的路径,而不是实际的数据块。当访问软链接时,文件系统会解析路径并跳转到目标文件。
-
跨文件系统: 软链接可以跨越不同的文件系统,因为它们只包含目标的路径,而不是具体的inode或存储块。
-
删除链接不影响目标文件: 删除软链接并不会影响目标文件,只有软链接本身被删除。如果删除目标文件,软链接就会成为“死链接”(dangling link),无法访问。
-
可以链接目录: 软链接可以指向目录,而硬链接通常不能。
使用软链接的一些优势包括更灵活的文件链接,可以跨越文件系统边界,以及对目录的链接支持。然而,由于软链接依赖于路径而不是直接指向数据块,当目标文件被移动或删除时,软链接可能会成为“死链接”
,因此需要谨慎使用。
🐦软链接的作用
当我们有一个文件的路径被藏得很深的时候,每次访问该文件时,需要带很长的路径并不是很方便,此时我们就可以为该文件建立软链接,这样在自己的工作目录下就可以很便捷地使用了。
示例
$ tree
.
├── dir
│ └── dir1
│ └── dir2
│ └── dir3
│ └── dir4
│ └── dir5
│ └── test.c
├── myfile.txt
├── my_hard
└── my_soft -> myfile.txt
此时我们想要访问test.c
,需要这样:
$ cat ./dir/dir1/dir2/dir3/dir4/dir5/test.c
为了方便我们为该文件设置软链接:
$ ln -s ./dir/dir1/dir2/dir3/dir4/dir5/test.c test_soft
$ ll
total 12
drwxrwxr-x 3 hxy hxy 4096 Feb 28 14:14 dir
-rw-rw-r-- 2 hxy hxy 60 Feb 28 13:19 myfile.txt
-rw-rw-r-- 2 hxy hxy 60 Feb 28 13:19 my_hard
lrwxrwxrwx 1 hxy hxy 10 Feb 28 13:07 my_soft -> myfile.txt
lrwxrwxrwx 1 hxy hxy 37 Feb 28 14:18 test_soft -> ./dir/dir1/dir2/dir3/dir4/dir5/test.c
现在每次我们想访问test.c
文件时,只需访问test_soft
即可。
$ cat test_soft
这里有点类似于widows
下的快捷方式
。
本章的内容到这里就结束了!如果觉得对你有所帮助的话,欢迎三连~