一. 硬件理解
1. 磁盘、服务器、机柜、机房
1.1 磁盘
①核心功能:数据存储的基本物理单元。
② 简要叙述:磁盘是安装在服务器内部的硬件设备,相当于计算机的外设,用于永久存储所有数据(操作系统、应用程序、用户文件等)。主要分为传统的机械硬盘(HDD)和速度更快的固态硬盘(SSD)。可以将其理解为服务器内部的“数字仓库”或“记忆体”。我们如今使用的笔记本电脑的磁盘基本都是固态硬盘(SSD)。
③ 机械磁盘是计算机中唯⼀的⼀个机械设备,它的速度慢,但是容量大,价格便宜。把磁盘拆开,它的内部结构
如下图。

1.2 服务器
① 核心功能:提供计算和服务的核心计算机。
② 简要叙述:服务器是一种高性能计算机,内部包含中央处理器(CPU)、内存、磁盘、网卡等组件,专门用于处理数据、运行程序并为网络中的其他计算机(客户端)提供各种服务(如网站、应用程序、数据库、文件共享等)。磁盘是安装在服务器内部,为其提供存储空间的部件。
③ 下图是一台服务器的展示,它的内部能插入多块磁盘。

1.3 机柜
① 核心功能:集中安装和管理多台服务器的标准机架。
② 简要叙述:机柜是一个金属框架柜子,标准宽度(通常为19英寸),用于整齐、安全地垂直堆放多台服务器、网络交换机、存储设备等。它提供统一的电源分配、网络布线和管理通道,是机房内设备部署的基本单元。服务器就像一本本厚重的书,而机柜就是用来有序摆放这些书的书架。
③ 下图就是一个机柜的展示,它的内部能装很多台服务器。

1.4 机房
① 核心功能:容纳和保护所有IT设备及基础设施的专属物理空间。
② 简要叙述:机房是一个经过专门设计和建造的场所,用于集中存放大量的机柜以及支持其运行的全部基础设施。这包括精密空调(恒温恒湿)、不同断电源系统(UPS)、后备发电机、消防系统、物理安防(门禁、监控)和综合布线系统等。机房是为所有机柜、服务器及其内部的磁盘提供一个安全、稳定、可控的运行环境的总空间。一般机房的造价都非常高。
③ 下图就是机房的展示。

1.5 总结
磁盘装在服务器里,为服务器提供存储;服务器装在机柜里,进行集中管理和布线;机柜部署在机房里,由机房提供电力、冷却和安全的整体环境。
2. 磁盘的物理结构
我们可以把磁盘想象成一个有无数个小磁铁组成的,磁铁有南北极,我们可以把它想象成0和1,也可以把修改磁盘文件想象成改变小磁铁的南北极(0和1)。下图是磁盘的物理结构。

3. 磁盘的存储结构
3.1 磁盘存储结构示例




3.2 磁盘存储结构分析
① 扇区:是从磁盘读出和写入信息的最小单位,也是磁盘存储数据的基本单位,通常大小为 512 字节,即在进行磁盘IO时必须以512字节为单位。我们认为从盘片外圈到盘片内圈的各个磁道的各个扇区的存储容量都是512字节。
② 磁头(head):每个盘片⼀般有上下两面,每个面分别对应1个磁头,一个盘片共2个磁头。
③ 磁道(track):磁道是从盘片外圈往内圈进行编号,0磁道,1磁道…,靠近主轴的同心圆用于停靠磁头,不存储数据。
③ 柱面(cylinder):由多个半径相同的磁道构成,数量上等同于磁道的个数。
⑤ 扇区(sector):每个磁道都被切分成很多扇形区域,每个磁道的扇区数量都相同,
⑥ 圆盘(platter):即盘片的数量。
⑦ 磁盘总容量=磁头数 × 磁道(柱面)数 × 每个磁道的扇区数 × 每个扇区的字节数。
⑧ 结论:所有磁头共计退,所有盘片共旋转。
3.3 向扇区写入数据的方式
① 当我们要向扇区写入数据时,我们首先就得找到这个扇区。查找扇区的方法讲述有两种,我们这里先来谈讲法1。
② 讲法1:可以先定位磁头(header),即确定这个扇区在哪一个面上。 然后确定这个扇区在当前盘面的哪⼀个磁道(cylinder)上。最后确定这个扇区是该磁道的哪⼀个扇区(sector)。这就叫 CHS地址定位。
③ 文件 = 文件内容+文件属性,内容和属性都是数据,我们能向一个扇区中写,当然也能向多个扇区中写。⽆⾮就是占据那⼏个扇区的问题!因为只要我们能定位⼀个扇区,那么我们就能定位任意一个扇区,就能定位任意多个扇区。下面是查看系统的磁盘信息。

4. 磁盘的逻辑结构
4.1 理解过程
① 磁带上面可以存储数据,我们可以把磁带“拉直”,形成线性结构,如下图。

② 虽然磁盘本质上是硬质的,但是逻辑上我们可以把磁盘想象成为卷在⼀起的磁带,那么磁盘的逻辑存储结构我们也可以类似于下图的样子,就是一个线性结构。

③ 所以磁盘在逻辑上就能理解为一个一维数组,数组的元素就是一个扇区。这样每⼀个扇区,就有了⼀个线性地址(其实就是数组下标),这种地址叫做 LBA地址。如下图。

4.2 真实过程
① 首先我们已经知道磁盘的所有磁头是共计退的,所有盘片是共旋转的。如下图。

② 我们所说的柱面是⼀个逻辑上的概念,其实就是在每一个盘面上,相同半径的磁道逻辑上构成了柱面。所以,磁盘物理上分了很多面,但是在我们看来,逻辑上,磁盘整体是由“柱面”卷起来的。
③ 在磁头摆动和盘片旋转过程中,磁头摆动的意义本质就是查找访问哪一个磁道(柱面)。而盘片旋转的本质则是定位到指定的扇区。我们选定一个磁铁就确定了要访问的扇区。
④ 所以磁盘的真实情况如下:
磁道展开:即一维数组。

柱面展开:柱面上的每个磁道,扇区个数是⼀样的,即二维数组。

整个磁盘展开:就是多张⼆维的扇区数组表(类似三维数组)。磁盘在逻辑上可以抽象成为一维数组,因为三维数组可以理解为一维数组。

⑤ 现在我们再来看定位扇区的方法讲法2。由于磁盘整体是由柱面构成,所以真实情况是当我们在访问瓷盘时,都是先查找这个扇区属于哪⼀个柱⾯(Cylinder) ,再确定扇区位于该柱面内哪⼀个磁道(其实就是磁头的位置,Head),最后确定是该磁道的哪一个扇区(Sector),所以就有了真正的 CHS地址定位 。
⑥ 所以,每⼀个扇区都有⼀个下标,我们叫做 LBA(Logical Block Address) 地址,其实就是线性地址。操作系统只使用LBA地址,而CHS地址和LBA地址的相互转换转换工作则由磁盘自己来做。

5. CHS地址 && LBA地址
5.1 CHS转成LBA:
① 磁头数 * 每个磁道的扇区数 = 单个柱面的扇区总数
② LBA = 柱面号C * 单个柱面的扇区总数 + 磁头号H * 每个磁道的扇区数 + 扇区号S - 1
③ 扇区号通常是从1开始的,而在LBA中,地址是从0开始的。
④ 柱面和磁道都是从0开始编号的。
⑤ 总柱面,磁道个数,扇区总数等信息,在磁盘内部会自动维护,上层开机的时候,会获取到这些参数。
5.2 LBA转成CHS:
① 柱面号C = LBA / (磁头数 * 每个磁道的扇区数)
② 磁头号H = (LBA % (磁头数*每个磁道的扇区数)) / 每个磁道的扇区数
③ 扇区号S = (LBA % 每磁道扇区数) + 1
5.3 结论
所以:在磁盘使用者看来,根本就不关心CHS地址,而是直接使用LBA地址,磁盘内部自己进行地址的转换。磁盘本质就是⼀个元素为扇区的⼀维数组,数组的下标就是每⼀个扇区的LBA地址。操作系统访问磁盘,就可以用⼀个数字访问磁盘扇区了。往后只要知道了磁盘总容量,这时 扇区总数 = 磁盘总容量 / 512byte。
二. 引入文件系统
1. 引入"块"概念
1.1 其实硬盘是典型的“块”设备,操作系统读取硬盘数据的时候,其实是不会⼀个个扇区地读取,因为磁盘这种外设速度很慢,一个一个地读取扇区效率太低,所以操作系统选择⼀次性连续读取多个扇区,即⼀次性读取⼀个”块”(block)。
1.2 硬盘的每个分区是被划分为⼀个个的”块”。⼀个”块”的大小是由格式化的时候确定的,并且不可以更改,最常见的块大小是4KB,即连续8个扇区组成⼀个 ”块”。”块”是⽂件存取的最小单位。
1.3 为什么操作系统不直接使用扇区来访问磁盘,而是用8个扇区组成的4KB的块来访问磁盘?
① 因为磁盘这种外设速度很慢,一个一个地读取扇区效率太低。即为了提高效率(主因)。
② 使软硬件解耦合(其他因素)。因为将来如果磁盘的基本存储单元扇区的大小变了,操作系统访问磁盘仍然是使用4KB的块大小为单位去访问,而不会影响操作系统本身。

1.4磁盘就是⼀个三维数组,我们把它看待成为⼀个"⼀维数组",数组下标就是LBA,每个元素都是扇区, 每个扇区都有LBA地址。那么8个扇区组成⼀个块,每⼀个块的地址我们也能算出来。 即块号 = LBA/8;LAB=块号*8 + n。(n是块内第几个扇区)
2. 引入"分区"概念
2.1 由于磁盘的总容量一般都是几百GB,操作系统在进行磁盘管理时如果直接管理不是很好管理,就像我们国家为了更好地管理全国就把整个国家划分为各个省份来进行管理,所以操作系统在管理磁盘时就把磁盘划分成多个分区(partition)。以Windows操作系统的视角来看,就是把磁盘划分成C盘、D盘、E盘等,那么这个C盘、D盘、E盘就是分区。分区本质上上说就是对硬盘的⼀种格式化。但是Linux的设备都是以文件形式存在,那它又是怎么进行分区的呢?
2.2 柱面是分区的最小单位,我们可以利用参考柱面号的方式来进行分区,其本质就是设置每个区的起始柱面和结束柱面号。 此时我们可以将硬盘上的柱面(分区)进行平铺,将其想象成⼀个大的平面。如下图所示。

2.3 每个柱面的大小⼀致,即扇区的个数也⼀致,所以我们只要知道每个分区的起始和结束柱面号,知道每
⼀个柱面有多少个扇区,我们就能算出这个分区的大小了。
2.4 我们已经知道硬盘是典型的“块”设备,操作系统读取硬盘数据的时候,读取的基本单位是”块”。“块”又是硬盘的每个分区下的结构,那磁盘是如何对分区进行管理的呢?这就要引入文件系统的概念了。
三. ext2文件系统
1. ext2文件系统的宏观认识
1.1 当我们想要在硬盘上存储文件时,必须先把硬盘格式化为某种格式的文件系统,这样才能存储文件。文件系统的目的就是组织和管理硬盘中的文件。在Linux 系统中,最常见的文件系统是 ext2 系列的⽂件系统。其早期版本为 ext2,后来又发展出 ext3 和 ext4。ext3 和 ext4 虽然对 ext2 进行了增强,但是其核心设计并没有发生变化,所以我们今天要谈论的还是 ext2文件系统。
1.2 ext2文件系统为了将各个磁盘分区进行更精细化的管理,所以将整个分区划又分成若干个同样大小的块组 (Block Group),且每个Block Group都由相同的结构组成,正如我们国家又把各个省份划分为各个地级市来更精细化地管理。如下图所示,所以只要能把一个块组管理好,就能把所有块组管理好,就能把一个分区管理好,进而把所有分区管理好,最后就能把整个磁盘管理好。这也体现了一种 “分治” 的思想。

1.3 上图中的启动块(Boot Sector)的大小是确定的,为1KB,用来存储磁盘分区信息和启动信息,任何文件系统都不能修改启动块。启动块之后才是ext2文件系统的开始。
1.4 所以在操作系统内部,既有磁盘的概念,又有分区的概念,还有分组的概念。所以操作系统作为软硬件资源的管理者,需要对这些文件进行管理,即先描述再组织。
1.5 在每个块组中,首先必须写入的是Super Block 、GDT 、Block Bitmap 、inode Bitmap 这四个文件管理信息,用来管理所有的文件。就像要管理一片无人区,首先要做的不是让百姓先去定居,而是先在当地成立一个政府。我们把写入管理信息的过程就叫做写入文件系统,所有的管理信息就叫做文件系统。
2. 块组(Block Group)内部构成
2.1 文件 = 文件内容 + 文件属性,在Linux系统中,文件内容和文件属性是分开存储的。文件内容和文件属性本质都是文件的数据,在一个块组(Block Group)中,必须包含文件的数据和管理这些数据的管理信息。正如各个省份的百姓和政府。
2.2 Data Blocks区
① Data Blocks区就是保存文件内容的区域,它是由一个一个的4KB大小的数据块所组成,每一个数据块都有自己的唯一编号。
② 对于普通⽂件,⽂件的数据存储在数据块中。
③ 对于目录,该目录下的所有文件名和目录名存储在所在目录的数据块中。除了文件名外,文件的其他属性信息
保存在该文件的inode中。
④ Block 编号按照分区划分,不可跨分区。
2.3 Inode Table(inode节点表)
2.3.1 inode的概念
① 我们之前查看文件的信息主要有以下两种方式:

② 我们知道文件的数据都储存在”块”中,文件的内容存储在Data Blocks区域的数据块中,因为文件的内容和属性是分开存储的,所以操作系统就把文件的属性存储到了一个叫做 inode 的结构体中。
③ 一个文件就有一个 inode,每个 inode 内部都有一个 inode_number,叫做 inode 编号。
④ 文件名属性并未纳入到 inode 结构体内部。
⑤ inode 结构体的大小就是固定的128字节。
⑥ 任何文件的内容大小可以不同,但是属性大小⼀定相同。
⑦ 有了上面的认知,那么文件= 文件内容 + 文件属性 就能转换为 文件 = inode + datablock了。
2.3.2 Inode Table
① Inode Table 是用来存放文件属性的,它的最小存储单元也是4KB, 所以一个存储单元就能存储32(4096/128)个文件的属性,将来操作系统 打开文件时会一次性加载4KB的块组到内存中,然后再根据 inode_number 查找对应的文件属性。
② Inode Table 中存储的是当前分组中所有文件 inode 属性的集合。
③ inode 编号以分区为单位,整体划分,不可跨分区。
④ 在每个文件的 inode 结构体中,都存在一个 i_block[] 的整型数组,这个数组中存储了该文件使用了 DataBlocks区中哪些4KB数据块来存储内容的数据块编号。所以在一个分组中,我们要查找一个文件内容,就可以根据文件的 inode_number 找到这个文件在 Inode Table的 inode 的位置,然后找到 inode 结构体中的 i_block[]数组,最后通过数组内容找到对应文件内容的 datablock 数据块。
2.4 块位图(Block Bitmap)
Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用。占用的数据块的比特位用1表示,未占用的数据块的比特位用0表示。
2.5 inode位图(Inode Bitmap)
inode位图中每个比特位表示⼀个 inode 是否是空闲可用的,类似 块位图。所以我们要删除一个文件只需要把对应文件的 inode 位图由1变0,对应的 块位图中使用的数据块的编号 由1变0即可。
2.6 GDT(Group Descriptor Table)
GDT是块组描述符表,用来描述块组属性信息,整个分区分成多少个块组就对应有多少个块组描述符。每个块组描述符存储⼀个块组的描述信息,比如在这个块组中从哪⾥开始是inode Table,从哪⾥开始是DataBlocks,空闲的inode和数据块还有多少个等等。块组描述符在每个块组的开头都有⼀份拷贝。
2.7 超级块(Super Block)
① 超级块(Super Block)用来存放文件系统本⾝的结构信息,描述整个分区的文件系统信息。记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,⼀个block和inode的大小,最近⼀次挂载的时间,最近⼀次写入数据的时间,最近⼀次检验磁盘的时间等其他文件系统的相关信息。如果Super Block的信息被破坏,那么就可以说整个⽂件系统结构就被破坏了。
② 超级块在每个块组的开头都有⼀份拷贝(第⼀个块组必须有,后⾯的块组可以没有)。 为了保证文件系统在磁盘部分扇区出现物理问题的情况下还能正常⼯作,就必须保证文件系统的super block信息在这种情况下也能正常访问。所以⼀个文件系统的super block会在多个block group中进行备份,这些super block区域的数据保持⼀致。
3. inode 和 datablock 的映射
3.1 我们已经知道在 inode结构体 的内部存在一个整型数组 i_block[],这个数组就是用来进行 inode 结构体和 block数据块 映射的。这样我们就能通过查看文件属性找到文件内容了。
3.2 问题:在我们已经知道 inode编号 的基础上,在指定的分区中,如何理解文件的创建、删除、修改和查看?(粗略理解)
3.2.1 文件创建
① 当我们创建文件时,一般都是通过进程调用系统调用来创建的。创建文件时,首先在内核中创建了 inode,然后把文件的属性写到 inode结构体中,然后把 inode Bitmap 以4KB为单位加载到内存中, 然后查找一个未被使用的比特位的位置,然后把这个位置由0置1,再根据这个位置编号在 inode Table 中找到未被使用的位置,然后把 inode结构体 中的文件属性拷贝到 inode Table中。这样一个文件就被创建出来了。
② 当我们想要向这个空文件里写数据时,一般都是通过进程的系统调用先写到文件内核缓冲区中,然后再刷新到磁盘上。在刷新时,操作系统会把 Block Bitmap 以4KB为单位加载到内存中,然后查找未被占用的数据块,然后把数据再写入到数据块中。最后在 inode Bitmap 中把对应位置的数据块比特位由0置为1。
3.2.2 文件删除
① 当我们要删除一个文件时,首先我们已经拿到了这个文件的 inode_number了。在删除时,我们先要把 inode Bitmap 以4KB为单位加载到内存中,然后根据 这个文件的 inode_number 查找对应的位置是否为1,若为1,则文件存在,存在即合法。
② 然后再根据 inode_number 在 inode Table 中找到该文件的 inode 属性,然后在 inode 中找到整型数组 i_block[] ,再根据这个数组找到文件占用的数据块编号。这时再把 Block Bitmap 以4KB为单位加载到内存中, 根据找到的数据块编号把 Block Bitmap 中的比特位由1置为0,最后再把 inode Bitmap 中的对应位置由1置为0。这样就完成了文件的删除。
3.2.3 文件修改
① 文件 = 文件内容 + 文件属性,所以我们进行文件修改无非就是修改文件内容或者文件属性。当我们要修改文件属性时,首先需要把 inode Table 以4KB为单位加载到内存中,然后再进行 inode 的修改,最后再把修改之后的 inode 刷新到磁盘上。
② 当我们需要修改文件内容时,也是要根据 inode_number 找到文件属性 inode 中的 i_block 数组,进而找到该文件所占用的数据块,然后把这些数据块加载到文件内核级缓冲区中,再调用系统调用进行修改,最后再把内容刷新到磁盘上。
3.2.4 文件查看
文件 = 文件内容 + 文件属性,查看文件无非就是查看文件内容和文件属性。因为我们已经知道了文件的 inode_number,所以我们先把 inode Bitmap 以4KB为单位加载到内存中,然后查找对应位置是否为1,若为1,说明文件存在。然后直接通过 inode_number 直接索引 inode Table 找到对应文件的 inode ,这样就能查看文件的属性了。若要查看文件内容则只要根据 inode 中的 i_block[] 就能找到文件的数据块 datablock 了,然后就能查看文件内容了。
4. 目录与文件名
4.1 问题引入
① 我们用户咋访问文件时,用的都是文件名,并没有用过inode编号啊?
② 目录是文件吗?
4.2 答案是: ① 目录也是文件,但是磁盘上没有目录的概念,只有文件属性+⽂件内容的概念。目录的属性和普通文件类似,目录的内容保存的是:文件名 和 文件Inode编号 的映射关系。

② 文件名 和 文件Inode编号 的映射关系本质也是数据,所以目录的内容也是保存在 DataBlocks 的数据块里。在磁盘和文件系统角度,存储目录和存储普通文件没有任何区别,这也是理解Linux下一切皆文件的另一个角度。
③ 当我们把目录的读写权限删除后,发现我们就不能查看目录下的文件和创建删除文件了。

首先我们不能创建文件是因为我们没有写权限,所以不能把 inode编号 和 文件名的 映射关系写到目录的 datablock的数据块中,即 inode 和 文件名的 映射关系建立不起来,所以文件创建失败;我们不能查看文件是因为我们没有读权限,所以不能查看目录的datablock数据块,所以不能获取 文件名和 inode编号 的映射关系,自然不知道文件的 inode,所以不能查看到文件。
4.3 证明
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage: %s <directory>\n", argv[0]);
exit(EXIT_FAILURE);
}
DIR *dir = opendir(argv[1]); //系统调用
if (!dir)
{
perror("opendir");
exit(EXIT_FAILURE);
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL)
{
// 系统调用
// Skip the "." and ".." directory entries
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..")== 0)
{
continue;
}
printf("Filename: %s, Inode: %lu\n", entry->d_name, (unsigned long)entry->d_ino);
}
closedir(dir);
return 0;
}

4.4 inode编号和数据块号
① inode编号和数据块号不是组内有效,而是在整个分区内有效,即inode编号和数据块号在整个分区内是唯一的,且不能跨分区。
② 在一个分区内部,一个文件系统内部,inode的个数和数据块的个数都是提前设计好的,都是固定的。只要知道⽂件的inode编号,就能在指定分区中确定文件是在哪⼀个分组,进而在分组中确定是哪⼀个inode,最终我们就能拿到 inode文件属性和内容了。
5. 路径解析
5.1 前提
在访问⽂件时,必须打开当前文件所处的目录,根据文件名,获得对应的inode编号,然后进行文件的访问。所以,访问文件必须要知道文件单位当前工作目录,本质是必须能打开当前工作目录的⽂件,查看目录文件的内容。
5.2 引入路径解析
① 当我们要打开当前的工作目录文件来查看当前工作目录文件的内容时,当前工作目录不也是文件吗?我们访问当前⼯作⽬录不也是只知道当前工作目录的文件名吗?要访问它,不也得知道当前工作目录的 inode 编号吗?
② 因为目录存储的是文件名和文件inode编号的映射关系,目录也是文件, 所以我们也要打开当前工作目录的上级目录,上级目录不也是目录吗?所以这就类似"递归",需要把路径中所有的目录全部打开,递归出口是"/"根目录。
③ 实际上,任何文件,都有路径,访问目标文件,都要从根目录开始,依次打开每⼀个目录,根据目录名,找到对应目录的 inode编号,再依次访问每个目录下指定的目录,直到访问到目标文件。这个过程叫做Linux路径解析。
④ 所以我们访问文件必须要有路径。而根目录是直接的、固定的,能让我们直接访问到的,inode编号无需查找,系统开机后就知道。
6. 路径缓存
6.1 前言
如果我们每次访问文件时,都要做路径解析,也就是操作系统要多次访问磁盘,磁盘是外设,这样的话效率就太低了,所以操作系统会进行路径缓存,即操作系统会缓存历史路径。
6.2 引入路径缓存
① 当我们用户在访问操作系统时,可能会打开大量的路径。那操作系统作为软硬件资源的管理者,需要对这些历史路径节点进行管理,即 “先描述,再组织”。所以在Linux中,在内核中维护树状路径结构的内核结构体叫做: struct dentry。
② 每个文件其实都要有对应的dentry结构体,包括普通文件(叶子结点)。这样所有被打开的文件,就可以在内存中形成整个树形结构,这个树形结构是Linux目录结构的子集。
③ 整个树形结构的节点也同时会⾪属于struct list_head d_lru(least recently used,最近最少使用)这个结构体中,用来进行节点淘汰。整个树形节点也同时会隶属于struct hlist_node d_hash这个结构体,方便快速查找路径节点。

④ 这个树形结构,整体构成了Linux的路径缓存结构,打开访问任何文件,都要先在这棵树下根据路径进行查找,找到就返回对应文件的属性inode和内容,没找到就从磁盘加载新的路径,添加struct dentry结构,缓存新路径。所以这棵树形结构是会动态变化的。
⑤ 当我们第一次进行路径搜索时可能速度比较慢,第二次就快了。因为第一次搜索时操作系统要先进行路径缓存,第二次则是只需要缓存新的路径节点即可。
6.3 路径来源
① 我们访问任何文件,操作系统内核都会先做路径解析和路径缓存,可是路径是谁提供的?
② 答案是: 用户访问文件,都是通过指令或者工具来访问,本质是通过进程来访问,进程有cwd,cwd来源于父进程,父进程的cwd来源于系统和环境变量。所以进程能够提供路径。用户使用open打开文件,open接口中提供了路径。
③ 最开始的路径就是根目录,根目录下面用户能够新建目录、创建文件,这不就产生了天然的路径了吗。所以Linux的路径结构是系统和用户共同创建的。
7. 存储大文件问题
7.1 当我们想要存储一些大文件时,比如文件大小是几十个GB。我们已经知道在文件的 inode结构体中有一个整型数组 i_block[],这个数组的大小是15,这个数组存储数据块的块号,一个数据块的大小是4KB,那么难道文件系统中只能存储60KB大小的文件吗?
7.2 所以为了解决这个问题,操作系统对这个 i_block[]做了如下操作。
① 操作系统就把 i_block[] 这个数组的前12个元素存储真正的数据块号(这里把下表看成指针),第13个元素存储一个指针,这个指针指向一个数据块,这个数据块存储了1024(4KB/4)个存储文件内容的数据块的编号。
② 第14个元素存储了一个指针,这个指针指向一个数据块,这个数据块存储了1024个数据块的编号,这1024个数据块的每一个数据块都存储了1024个存储文件内容的数据块的编号。
③ 依此类推,第15个元素存储了一个指针,这个指针指向一个数据块,这个数据块存储了1024个数据块的编号,这1024个数据块的每一个数据块又都存储了1024个存储数据块编号的数据块的编号。如下图所示。
④ 由于数据块号是分区唯一,不是块组唯一。所以为了存储大文件,在一个块组内,不仅仅能够存储自己文件的数据,还能存储其他块组内文件的数据,即一个文件可以用不同块组的数据块来存储数据。

8. 挂载分区
8.1 问题引入
我们现在已经能够根据文件的 inode编号在指定的分区中查找文件了,也已经能根据目录文件内容,查找指定的文件inode了。但是:文件 inode 是不能跨分区的,Linux可以有多个分区,那我怎么知道我处在哪⼀个分区中呢?
8.2 实验证明





8.3 结论
① 磁盘进行分区后,需要格式化,即写入管理信息,即写入文件系统,但是分区写入文件系统之后,这个分区还是无法直接使用。我们需要把这个分区挂载到指定的目录下后才能使用。
② 所以在挂载之后,访问文件需要路径,所以可以根据访问目标文件的"路径前缀"来判断当前我所处的分区。
9. 文件系统总结
下面几幅图展现了文件系统的整体框架和结构。



四. 软硬链接
1. 硬链接
1.1 示例:

1.2 通过以上实验,我们能得到以下的结论。
① 我们知道在同一个目录下文件名具有唯一性,一个文件对应一个 inode编号。因为硬链接和目标文件对应的 inode编号相同,所以硬链接和目标文件的本质是同一个文件。
② 所以给目标文件建立硬链接的本质就是给目标文件取一个别名,即在当前目录下建立一个新文件名和目标文件的映射关系。
③ 而上述实验中的数字则是硬链接数,硬链接数表明有多少个文件名指向同一个 inode编号。当我们在删除文件时,都是先删除文件名和 inode编号的映射关系,然后把硬链接数 -1,当硬链接数减为0时,inode编号 对应的文件才会真正地被删除释放。如下图。

1.3 硬链接的用途(意义)
① 对文件进行备份。我们能够给目标文件建立多个硬链接来进行文件备份,它们都指向同一个 inode编号,当我们不小心把一个文件删除后,只会让硬链接数 -1,而不会把这个文件真正删除掉。
② 构建Linux系统的目录结构。如下图。“.” 和 “…” 的本质是硬链接。

1.4 目录的硬链接
① 当我们创建普通文件时,因为只有一个文件名和 inode编号建立映射关系,所以它的硬链接数默认是1,。而目录由于在目录内部还要一个 “.” 表示当前目录,所以它的硬链接数默认是2。所以我们通过目录的硬链接数再减去2就是该目录中的目录数。如下图,该目录中有三个目录。

② 如下图,我们发现不能给目录建立硬链接,因为这会导致出现环形路径,可能导致用户在查找路径时出现死循环。

③ 那如何看待 “.” 和 “…” 这两个目录呢?它们不会导致环形路径吗?
答案是: “.” 和 "… " 是Linux系统中两个特殊的文件名,用来构建相对路径。Linux会对这两个文件进行特殊处理,在用户进行查找时Linux系统会忽略这两个路径。
2. 软链接
2.1 软链接是一个新的独立的文件,inode编号和目标文件不一样,有自己的文件内容和文件属性,它的文件内容保存的是目标文件的路径,路径也是数据。软连接类似windows下的快捷方式。

2.2 引用场景——安装软件
软连接有时能代替拷贝指定软件到指定目录下,而是给目标软件建立一个软连接。

2016

被折叠的 条评论
为什么被折叠?



