🤣 爆笑教程 👉 《看表情包学Linux》👈 猛戳订阅 🔥
💭 写在前面:上面我们学到的所有东西,全部都是在内存中的。是不是所有的文件都被打开了呢?不是所有的文件,都被打开的,我们之前研究的是进程打开的文件,是由操作系统去管理文件的动态特性,但是我们还有大量的文件,大多数文件都在磁盘上静静地 "躺着" 的。这批文件非常多、杂、乱,本章我们就要探讨在磁盘上 "躺着" 的文件。然后我们将学习 Inode,带着大家理解文件系统,为下面介绍软硬链接做铺垫。有了对文件系统的深刻了解后再去学习软硬链接,事情会变得简单许多!本章内容较多,让我们速速开始!
📜 本章目录:
Ⅰ. 磁盘基础知识(Disk)
0x00 引入:什么是文件系统?
0x01 磁盘的物理结构
0x02 磁盘的逻辑抽象结构
0x03 CHS 寻址
0x04 逻辑扇区 (LBA)
0x05 IO 基本单位
Ⅱ. 索引节点(Inode)
0x00 引入:理解文件系统
0x01 Inode 概念
0x02 伪删除(inode bitmap: 1 → 0)
0x03 创建新文件时OS会做什么?
Ⅲ. 软硬链接(Symbolic Links & Hard Links)
0x00 Linux 下的快捷方式:软链接
0x01 创建软链接
0x02 创建硬链接
0x03 软硬链接的删除
0x04 硬连接数
0x05 思考一些问题
Ⅰ. 磁盘基础知识(Disk)
0x00 引入:什么是文件系统?
这些在磁盘上静静 "躺着" 的文件,又杂又乱,我们为什么要让它们躺在那占用磁盘空间呢?
因为这些文件随时可能被打开,那些从来不被访问打开的文件就属于乐色文件,就被清掉了。
而 有可能被访问的文件就必须保留下来! 我们再举个恰当的例子:
《快递驿站》
这其实很好理解,比如你是一个快递驿站的老板,我们把驿站比作磁盘,而那些包裹比作文件。这些包裹总是要被客户取走的,但是你不知道你的客户什么时候会来取,如果你把包裹扔了,客户来取取不到怎么办,所以你需要把包裹存着。通过编号去记录包裹放在几行几列的柜子,老板存在的最大意义就是可以方便用户迅速的存取快递。
磁盘级别的文件管理,本质工作和快递驿站的老板做的工作是一样的!
对磁盘这个大空间的合理划分,让我们能快速定位查找到指定文件,乃至进行相关后续访问操作。
这就是所谓的 文件系统 (File-Sys) !在前言中我们也说了,这些文件是又多又杂又乱!
小至几 kb,大至几 GB 的都有,文件类型也是各种各样 .jpg, .sh 什么都有,文件所属组,
文件类型,只读或者只写,各种各样的 "可能被打开的" 文件都有!所以我们是需要管理它们的。
" 如果操作系统都不知道文件在哪,那还管个毛线 "
0x01 磁盘的物理结构
如果要理解文件系统,我们不妨把视角从内存中移开 (虽然和内存有关系) ,重点关注磁盘。
磁盘是我们电脑上的唯一的一个机械设备,目前我们的笔记本上可能已经不用磁盘了,而是固态银盘 (SSD) 。相对而言用起来更快,效率更高。固态硬盘是另一种存储的方案,和磁盘的存储差别很大,单价比磁盘大很多。一般 500G 的固态基本上比同等的磁盘要贵个两三百块。
磁盘上存储的基本单位是 扇区 (Sector),一般是 512 字节的。近三十年来,扇区的大小一直是 512 字节,但最近几年正迁移到更大、更高效的 4096 字节扇区,通常称为 4K 扇区。
读写磁盘的时侯,磁头找的是某一个面的某一个 磁道 (Traker),的某一个扇区 。
- 某一个面 → 哪一个磁头 (Head)
- 磁道指的是哪一个柱面 → 距离圆心的半径 → 哪一个磁头
- 扇区是磁道上的一段 → 盘面旋转决定的!
文件系统:什么文件,对应了几个磁盘块。
只要我们能找到磁盘上的盘面,柱面 (磁道) 和扇区,我们就能找到一个存储单元了。
用同样的方法,我们可以找到所有的基本单元。
所以,我们这种在物理上查找某一个扇区的寻址方式,叫做 地址。
机械式 + 外设 = 磁盘一定是很慢的 (CPU, 内存)
磁盘存储数据,磁性 N/S,改变 NS 极,就是改变了 0 1。
🔺 结论:你的文件数据就在这个盘面上。
0x02 磁盘的逻辑抽象结构
我们可以把对应的盘片,想象成为线性的结构。
扇区,我们把盘片想像成为线性的结构,我们当做数组呢?
定位一个 sector,只要找到下标就行了!对于磁盘的管理,转化成为了对数组空间的管理。类似于数组。磁盘上的每个扇区可以被看作是数组中的一个元素,而扇区的编号可以被看作是数组的索引。在这种情况下,可以使用索引来定位一个特定的扇区,就像在数组中使用索引来访问数组元素一样。磁盘管理的任务就变成了对这个线性结构(即盘片)的管理,包括读取和写入特定扇区的数据。例如,如果要读取或写入磁盘上的第5个扇区,就相当于访问数组中的第5个元素。通过索引,可以准确定位到特定的扇区,并进行相应的读取或写入操作。
这个抽象过程,仍然是 "先描述在组织"。
而这个下标,就是 LBA (logic block arrays), 这是操作系统认为磁盘基本单元的地址,它是一种逻辑块地址。所以,未来你想在磁盘中写入:只需要将 LBA 地址映射转化成 CHS 地址,然后将该内存中的数据配合 CHS 地址写入到磁盘里,至此就完成了写入:
💭 举个例子:假设 LBA 是 1234,1000 ~ 2000 属于第二面,2000~ 3000 属于第三面:
(在第一面)
(在第二面)
0x03 CHS 寻址
以前硬盘容量比较小,人们采用软盘的设计结构来设计生产硬盘,
硬盘盘片的每一条磁道都具备相同的扇区数量,由此就产生了 CSH 3D 参数 (Disk Geomentry)
即 磁头数 (Heads),柱面数 (Cylinders) 和 扇区数 (Sectors) ,以及对应的 CHS 寻址模式。
CHS 寻址模式是将硬盘划分为三个部分:磁头 (Heads)、柱面 (Cylinder)、扇区 (Sector) 。
- 磁头:每张磁盘的正面和反面都有一个磁头,一个磁头对应着一张磁盘的一个面,因此,用第几个磁头就能表示数据在哪个盘面。
- 柱面:由所有磁盘中半径相同的同心磁道构成,在这一系列的磁道水质叠放在一起,就形成了一个柱面的形状。所以 —— 柱面数 = 磁道数。
- 扇区:就是将磁盘划分为若干个小的区段,每个扇区虽然很小,但是看上去就像是一个 "扇子",所以称之为扇区,每个扇区的容量为 512 字节。
CHS 寻址的最大容量由 CHS 三个参数所决定:
- 磁头数最大为 255,用 8 个二进制位存储,从 0 开始编号
- 柱面数最大为 1023,用 10 个二进制位存储,从 0 开始编号
- 扇区数最大数为 63,用 6 个二进制位存储,从 1 开始编号
所以,CHS 寻址方式的最大寻址范围如下:
0x04 逻辑扇区 (LBA)
INT13:BIOS自带的第13号中断,mov ah,0/int13h复位磁盘;mov ah,2/int 13h读磁盘;mov ah,3/13h写磁盘。DOS 中的最有用,最危险的命令,可直接修改硬盘分区表。
由于 INT13 的限制,三维地址 CHS 的最大值只能为 ,
容量极限只能达到 。
其次,在系统管理文件时记录繁琐的 CHS 是件很费力的事情,效果较低。
然而使用 逻辑扇区 (LBA) 后,可在磁盘读写操作时可以摆脱柱面、磁头等硬件参数的限制。
逻辑扇区,是为了方便操作系统读取写入硬盘数据而设置的,其大小与具体地址,都可以通过一定的公式与物理地址对应。操作系统可以根据 LBA 来读取和写入数据,而无需关心物理地址的具体映射方式。
在 LBA 模式下,操作系统可以把所有的物理扇区都按照某种方式或规则看作是一个线性编号的扇区,从 0 到某个最大值方式排列,并连成一条线。把 LBA 作为一个整体来看待,而不是具体到实际的 CHS 值,这样就只需要用一个序数就能确定唯一的物理扇区,这就是线性地址的由来。显然,线性地址是物理扇区的逻辑地址。
IO 的基本单位是 4 kb,
0x05 IO 基本单位
首先,我们需要知道 的基本单位 。
磁盘的基本单位是 扇区 (常规为 512 字节),文件系统访问磁盘的基本单位是 :
① 提高 效率
② 不要让软件 (OS) 设计和硬件 (磁盘) 具有强相关性,即 解耦合。
整体 IO 效率提高,将磁盘的数据拷贝到内存花费的时间并不多,花费多的是在磁盘内部寻找位置的过程,该过程是 寻址过程。不管磁盘是多少转,你是永远无法比得上光电信号的。
什么是解耦合?
解耦合是指将两个或多个系统、组件或模块之间的紧密关联度降低,使它们能够独立演进,互不影响。在软件开发中,解耦合是一种设计原则,旨在减少系统各组件之间的依赖关系,从而提高系统的可维护性、灵活性和扩展性。
解耦合的目的是降低系统中各组件之间的相互依赖程度,使其能够独立进行修改、更新或替换,而不会对其他组件产生影响。当系统中的组件高度耦合时,对一个组件的修改可能会引发对其他组件的连锁反应,导致系统的不稳定和难以维护。而解耦合的设计可以使各组件更加独立,降低系统的复杂性,提高系统的灵活性和可扩展性。
对如何管理文件,变成了对一个小组数据的管理。那么如何对一个组做管理?
Ⅱ. 索引节点(Inode)
0x00 引入:理解文件系统
我们使用 ls -l
时,除了能看到文件名,还能看到文件的 元数据 (Metadata) :
每行包含七列,分别是: 模式、软硬连接数、文件所有者、组、大小、最后修改时间和文件名。
ls -l
做的就是读取存储在磁盘上的文件信息,然后把它们显示出来:
这个元数据除了通过这个方式来读取,还有可以通过 stat 命令看到更多的信息。
在讲解上面这些信息前,我们需要了解 inode 的概念。
0x01 Inode 概念
ext2 文件系统,下图为磁盘文件系统图(当然了,内核中内存映像肯定有所不同):
磁盘是典型的块设备,磁盘分区被划分为一个个小的 block。
一个 block 的大小是由格式化时决定的,并且不可修改。
例如 mke2fs 的 -b 选项可以设定 block 大小为 1024, 2048 或 4096 字节。
* 注:mke2fs 是一个语法,用于建立 ext2 文件系统。(make ext2 file system)
mke2fs [-cFMqrSvV][-b <区块大小>][-f <不连续区段大小>][-i <字节>][-N <inode数>][-l <文件>][-L <标签>][-m <百分比值>][-R=<区块数>][ 设备名称][区块数]
上图中,启动块 (Boot Block) 的大小是确定的。
① 块组 (Block Group) :ext2 文件系统会根据分区的大小划分 Block Group。
- 每个 Block Group 都有着相同的结构组成。
② 超级块 (Super Block):存放文件系统本身的结构信息。
- 记录的信息主要有:block 和 inode 的总量,未使用的 block 和 inode 的数量,一个 block 和 inode 的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。
- 如果 Super Block 的信息被破坏,可以说整个文件系统结构就被破坏了。
③ 块组描述符 (Group Descripter Table):描述块组属性信息。
④ 块位图 (Block Bitmap):Block Bitmap 中记录着 Data Block 中哪个数据块已经被占用,哪个数据块没有被占用。
⑤ inode 位图 (inode Bitmap) :每个 bit 标识一个 inode 是否空闲可用。
⑥ i 节点表:存放 "文件属性" 和 "文件大小","所有者"、"最近修改时间" 等。
⑦ 数据区:存放文件内容。
计算机在启动时,BIOS(基本输入/输出系统)会首先运行。
BIOS 是计算机主板上的固件程序,用于管理计算机的基本硬件和操作。BIOS包括一系列的自检程序(POST),用于检查计算机硬件是否正常工作。如果检查顺利通过,BIOS会将控制权交给操作系统,让操作系统接管计算机的控制权,进而启动操作系统并加载应用程序。如果检查失败,BIOS会发出声音或闪烁指示灯等提示,以表明出现问题。
Data Blocks:以块为单位,进行文件内容的保存。
Inode Table:以 128 字节为单位,进行 inode 属性的保存。
文件 = 内容 + 属性,所以都是数据,都要数据,Linux 采用的是将内容和属性分开存储的方案。
- 内容:block 中 4 KB
- 属性:inode 中就是磁盘上的另一份空间,128 字节。
struct inode {
int id;
mode_t mod;
user name;
data d;
...
}
struct inode i = {};
🔍 查看一个文件的 inode:
块位图 Block Bitmap 中 记录着 Data Block 中哪个数据块已经被占用,哪个数据块没有被占用。
- 0:表示未被占用
- 1:表示已经被占用
因此,我们可以利用比特位的内容来表示是否被占用:
inode 位图 (inode Bitmap) :表示 inode 块是否被占用,inode bitmap 表征 inode 的使用情况。
块组描述符 (Group Descripter Table):描述块组属性信息,有多少 inode,起始的 inode 编号,有多少 inode 被使用,有多少 block 被使用,还剩多少,你的总 group 大小是多少……
那么,一个 inode (文件, 属性) 如何和属于自己的内容关联起来呢?
在 inode table 内包括了文件的所有属性,其中有一个 blocks 数组,直接保存了该文件对应的 blocks 编号,我们 通过 blocks 编号就可以找到自己文件的内容。
* data block,4 kb,也可以保存其它块的编号!
struct inode 包括文件所有的属性:
struct inode {
// 文件的所有的属性
block[15];
};
block[15] 中, 中直接保存的就是该文件对应的 blocks 编号。
直接指向对应的 data blocks 中的某些块,这样就可以找到文件对应的某些内容了。 指向一个 datablock,但是这个 datablock 不保存有效数据,而保存该文件所使用的其他块的编号!
" 一个 inode 是可以和多个块建立关联的 "
📌 注意:文件名也算文件的属性,但是 inode 里面并不保存文件名!
就这一点,我们立马就能感受到了一个最终的结论:
Linux 下,底层实际都是通过 inode 编号标识文件的。
要找到文件一定要找到 inode 的编号,就知道找到分区内的哪一个小组,这个组的编号是一个到两万的,可以通过inode编号确定你在哪一个组。
知道 inode 了你知道文件的 inode 编号有没有找到,包括文件的大小文件的权限,都拿到了。
所以,要找到文件就必须找到 inode,找到 inode 就找到了所有。
那谁来帮我找 inode 编号呢?
inode 到现在我们对文件的访问全是文件名,我怎么知道 inode 编号是几呢?
下面我们就要重点谈一谈 Linux 下一切皆文件,目录是文件吗?当然也是文件!
如果目录是文件,那么根据 "文件 = 内容 + 属性" ,目录既然也是文件,那么它的属性也必须得有自己的 inode。一个目录也有自己的 inode 属性,我们目录里面内容对应的又是什么呢?
有文件也有对应的 blocks,目录包括文件类型,文件的所属组,文件的创建大小,
关键是一个目录如果有了inode 这个目录的数据块要放什么呢?
你想一想目录的数据块要放什么?我们在之前的章节中讲过一个关于 目录 权限的问题?但是我们说,进入一个目录需要 权限,执行。
但是我们今天不需要谈论 了,我们创建一个文件,如果要在目录下 创建文件,touch 文件名
创建一个文件需要的是 权限,查看文件名我们需要的权限是 权限。
重点理解 和 ,既然目录也是文件,那么目录文件也有自己的 inode 也有自己的数据块,它的数据块里放的是 文件名 和 inode 的映射关系!
对我们来说,目录也是文件,也有自己的属性,目录文件里保存的是目录文件和 inode 编号。
文件名:inode 编号的映射关系。文件名和 inode 编号是数据,最终保存在了目录内容中。
Linux 同一个目录下可以创建多个同名文件吗?不行!
所以,文件名本身就是一个具有 值的东西,是一对一的关系。
❓ 当我们创建一个文件,操作系统做了什么?
我们创建一个文件的时候,一定是在一个目录下的。文件名 inode 编号 -> 找到自己所处的目录 data block -> 将文件名和 inode 编号的映射关系写入到对应的目录的数据块中。
这也就解释了为什么 inode 里为什么不保存文件名了,因为文件名在目录中。
❓ 知道自己所处的目录名,是否就能知道该目录的 inode?
如果我们想要知道目录的 inode ,我们需要要到父目录去查找对应关系。因此,知道自己所处的目录下,是不能知道目录的 inode 的。
0x02 伪删除(inode bitmap: 1 → 0)
当我们删除一个文件,操作系统做了什么呢?只需将标记 inode bitmap 由 1 置为 0 即可:
所以,这实际上是一个 伪删除!
如果我们把文件删了,我们可以恢复这个文件,如果要 恢复文件只需要搞到曾经删除的 inode 值就行了。通过一些工具,将 bitmap 从 0 恢复成 1 就可以了。
当我们执行删除文件操作时,操作系统实际上会在文件系统的目录结构中删除该文件的目录项,并将该文件的 inode 节点中的链接数减 1。如果链接数变为 0,则该文件的数据块将被释放,并将 inode 节点标记为可用状态。
然而,删除文件并不意味着文件的数据就被立即清除,因为该文件可能被其他进程或操作系统本身仍然使用或打开。因此,只有当该文件的所有链接数都为 0 时,文件的数据才会被完全清除。
此外,在某些情况下,操作系统也可能使用一些特殊的工具来覆盖文件的数据,以确保文件内容不可恢复。这种覆盖方式被称为 安全删除 或 彻底删除。
恢复的最大难点:文件都删掉了,你怎么知道 inode 是多少呢?Linux 系统为了支持恢复,inode 编号会保存在系统的日志文件中的。恢复有点难度!
实际上 Windows 也是这样的,几乎所有的文件系统删文件都不会真的删文件。
0x03 创建新文件时OS会做什么?
创建一个新文件,操作系统主要会做如下四个操作:
① 存储属性:内核找到一个空闲的 结点 (这里是 263466),内核把文件信息记录到其中。
② 存储数据:该文件需要存储在三个磁盘块,内核找到了三个空闲块,300,500,800。将内核缓冲区的第一块数据复制到 300,下一块复制到 500,最后复制到 800……
③ 记录分配情况:文件内容按顺序 300,500,800 存放,内核在 inode 上的磁盘分布区记录了上述块列表。
④ 添加文件名到目录:新的文件名 abc。Linux 在当前目录中记录该文件,通过内核将入口 (263466, abc) 添加到目录文件,文件名和 inode 之间的对应关系将文件名和文件的内容及属性链接起来。
Ⅲ. 软硬链接(Symbolic Links & Hard Links)
0x00 Linux 下的快捷方式:软链接
我们介绍完 inode 了,我们再看我们刚才介绍的 元数据:
七列,分别是模式、软硬连接数、文件所有者、组、大小、最后修改时间和文件名。
上图中,红色圈出的就是 软硬连接数 了,我们刚才说了,可以使用 stat 文件名查看更多:
我们可以看到,我们的 mytest.c 文件的软硬连接数是 1。
🔍 软硬链接的区别:
- 软链接:是一个独立文件,有自己独立的 inode 和 inode 编号。
- 硬链接:不是一个独立的文件,它和目标文件使用的是同一个 inode。硬链接就是单纯的在 Linux 指定的目录下,给指定的文件新增 文件名 和 inode 编号的映射关系!
我们可以通过如下命令,创建一个文件的软硬链接:
$ ln -s 文件名 链接文件名 # 创建软连接
$ ln 文件名 链接文件名 # 创建硬链接
(下面我们先来讲软连接的创建,再讲硬链接的创建)
0x01 创建软链接
我们创建一个软连接,可以使用下面的指令:
$ ln -s 文件名 链接文件名 # 创建软连接
比如我们创建一个 my.txt 文件,我们像创建一个 my.txt 文件的软链接,我们可以:
这就是软连接,my.txt 和 my.txt.soft 的 inode 是不同的:
下面我们来举一个实际的例子来体验软链接有什么实际的用途:
💬 代码演示:vim mytest.c
#include <stdio.h>
int main(void) {
printf("hello, soft link...\n");
printf("hello, soft link...\n");
printf("hello, soft link...\n");
printf("hello, soft link...\n");
printf("hello, soft link...\n");
printf("hello, soft link...\n");
return 0;
}
🚩 运行结果如下:
程序正常运行,这里我们在 d1/d2/d3 下直接 ./mytest.exe 就可以运行。
但是,如果我们如果想在外面运行这个程序就会很累,因为它的路径有点深:
太麻烦了,所以这里我们就可以给它建立一个软连接,解脱双手:
$ ln -s ./d1/d2/d3/mytest.exe my.exe
这是不是有点像 Windows 下的 快捷方式?没错!
" 软链接就是 Linux 下的快捷方式 "
上面我们演示的是让软链接链接一个可执行程序,未来我们可以用它来链接头文件、库文件,动静态库,这样就可以不需要让我们冗余的在去某些地方找这些库了。
0x02 创建硬链接
对我们来说,硬链接是什么呢?硬链接其实非常简单!我们创建一个硬链接:
$ ln 文件名 链接文件名 # 创建硬链接
my.txt 和 my.txt.hard 映射的是同一个 inode:
硬链接就是单纯的在 Linux 指定的目录下,给指定的文件新增文件名和 inode 编号的映射关系!
0x03 软硬链接的删除
删除的话可以直接 rm,但是我们还是建议使用专门的 取消链接 的指令:unlink
$ unlink 链接文件名 # 取消链接
举个例子,我们把刚才创建的软链接和硬链接用 unlink 把它们扬了:
这个 unlink 就是用来取消链接的,但它也可以用来删文件。
0x04 硬连接数
我们先打道回府,重新创建一个硬链接,然后我们重点观察一下下面的 "数字":
我们可以再多建立几个硬链接,你可以看到这个数字的变化:
❓ 什么是硬链接数?
你看这个 inode 编号,是不是有点像指针的概念?
硬链接本质就是该文件 inode 属性中的一个计数器 count。用来标识就几个文件名和我的 inode 建立了映射关系。简而言之,就是有自己文件名指向我的 inode (文件本身) 。
" 软链接就是 Linux 下的快捷方式 "
既然是一个独立的文件,inode 是独立的,软连接的文件内容保存的是指向文件的所在路径。
0x05 思考一些问题
❓ 思考:为什么创建普通文件,硬链接数默认是 1 ?
因为 普通文件的文件名本身就和自己的 inode 具有映射关系,而且只有一个!所以默认的硬链接数为 1。
那为什么目录是 2 呢 ?
我们知道,任意一个目录一定存在一个点或两个点: . ..
那么 ./ 为什么表示的是当前路径呢?因为 . 表示的就是 mydir,当前所处的路径!
默认一个空目录创建一个 自己的名字 和 一个点,所以两个文件名指向它,所以是 2。
那么 .. 又是什么呢?.. 指向的是上级路径!这就是为什么我们 cd .. 可以回到上级目录的原因,因为它可以指向上级目录。
📜 尾记:其实,如果没有文件系统的铺垫,想直接理解软硬链接难免有些 "痴人说梦",是非常困难的。但我们讲完了文件系统再去理解软硬链接,你就会发现没有那么难,因为我们是从底层开始,向上去学习的!
📌 [ 笔者 ] 王亦优
📃 [ 更新 ] 2023.5.4
❌ [ 勘误 ] /* 暂无 */
📜 [ 声明 ] 由于作者水平有限,本文有错误和不准确之处在所难免,
本人也很想知道这些错误,恳望读者批评指正!
📜 参考资料 C++reference[EB/OL]. []. http://www.cplusplus.com/reference/. Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. . 百度百科[EB/OL]. []. https://baike.baidu.com/. 比特科技. Linux[EB/OL]. 2021[2021.8.31 |