1.引入“块”的概念

        其实硬盘是典型的“块”设备,操作系统读取硬盘数据的时候,其实是不会⼀个个扇区地读取,这样效率太低,而是⼀次性连续读取多个扇区,即⼀次性读取⼀个”块”(block)。

        硬盘的每个分区是被划分为⼀个个的”块”。⼀个”块”的大小是由格式化的时候确定的,并且不可以更改,最常见的是4KB,即连续八个扇区组成⼀个 ”块”。”块”是文件存取的最小单位。

注意:
        磁盘就是⼀个三维数组,我们把它看待成为⼀个"⼀维数组",数组下标就是LBA,每个元素都是扇区。
        每个扇区都有LBA,那么8个扇区⼀个块,每⼀个块的地址我们也能算出来。
        知道LBA:块号 = LBA/8
        知道块号:LAB=块号*8 + n. (n是块内第几个扇区)

2. 引入分区的概念

        磁盘的存储量的很大的,不好管理,于是我们引入分区的概念,即将文件分成多个区域,以window是视角来看,就是CDE盘,CDE就是三个分区,分区从实质上来说就是硬盘的一种格式化。Linux下是怎么分区的呢?

        柱面是分区的最小单位,我们可以利用参考柱面号码的方式来进行分区,其本质就是设置每个区的起始柱面和结束柱面号码。

3.引入inode概念

        之前我们说过文件=数据+属性 ,我们使用 ls -l 的时候看到的除了看到文件名,还能看到文件元数据(属性)。

-rwxr-xr-x. 1 root root 7438 "9⽉ 13 14:56" a.out

stat a.out #可以查看更多信息

依次是:模式、硬链接数、文件所有者、组、大小、最后修改时间、文件名

        文件数据都储存在”块”中,那么很显然,我们还必须找到⼀个地方储存文件的元信息(属性信息),比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为”索引节点”。ls -il可以看对于的inode信息。

4.文件系统

        我们想要在硬盘上储文件,必须先把硬盘格式化为某种格式的文件系统,才能存储文件。文件系统的目的就是组织和管理硬盘中的文件。在Linux 系统中,最常见的是 ext2 系列的文件系统,后来又发展出 ext3 和 ext4。ext3 和 ext4 虽然对 ext2 进行了增强,但是其核心设计并没有发生变化,我们仍是以 ext2 作为演示对象。

        ext2文件系统将整个分区划分成若干个同样大小的块组 (Block Group),如下图所示。只要能管理一个分区就能管理所有分区,也就能管理所有磁盘文件(分治思想)。

        上图中启动块(Boot Block/Sector)的大小是确定的,为1KB,由PC标准规定,用来存储磁盘分区信息和启动信息,任何文件系统都不能修改启动块。启动块之后才是ext2文件系统的开始。

4.1 Block Group:

        ext2文件系统会根据分区的大小划分为数个Block Group,且块组的大小固定,且每个块组内的inode和block的节点数是一致的。而每个Block Group都有着相同的结构组成。

        块组内部组成:

4.1.1 超级块(Super Block)

        存放文件系统本身的结构信息,描述整个分区的文件系统信息。记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,⼀个block和inode的大小,最近⼀次挂载的时间,最近⼀次写入数据的时间,最近⼀次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了。

如此重要的模块,它是怎么分布的呢?

        在第一个块组的开头必定会有一个超级块(Block1),第0号块(Block 0)是引导块,从Block 1开始,才是ext2文件系统的头部,超级块就在这块的开头。为了应对第1号块的超级块损坏,ext2只会在特定编号(3,5,7的幂次方)的块组中保存备份版本,否则太浪费空间了。

4.1.2 GDT(Group Description Table)

        块组描述符表,描述块组属性信息,整个分区分成多个块组就对应有多少个块组描述符。每个块组描述符存储⼀个块组的描述信息,如在这个块组中从哪里开始是inode Table,从哪里开始是DataBlocks,空闲的inode和数据块还有多少个等等。块组描述符在每个块组的开头都有⼀份拷贝。

当我么知道一个文件的inode号,通过简单的/%运算就可以知道在那个分区了

4.1.3 块位图(Block Bitmap)

        Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用,用bit位来表示,1表示这块内容占用,0表示没有占用

4.1.4 inode位图(Inode Bitmap)

        每个bit表示⼀个inode是否空闲可用,1表示占有,0表示没有被占有。这也就是为什么删除数据/文件非常快的原因,我们只需找到文件对应的inode号和blcok号,将它们置0即可。

4.1.5 i节点表(Inode Table)

        Inode Table本质是一个结构体数组,每个元素都是一个结构体(ext2是128字节大小,ext3/4是256字节),用于存放文件属性如文件大小,所有者,最近修改时间等(注意:文件名并没有保存在该结构体内部),在该结构体内部还有数据块指针数组,指向文件内容存放的数据块,是当前分组所有Inode属性的集合。

inode和datablock的映射:

        我们刚刚提到,inode结构体内部还有指向数据块的指针数组,该数组的前12个元素是直接指向block块的,13/14/15则分别对应着一/二/三级间接块索引表指针,存储的数据量逐级扩大,一般只有大文件才需要。逻辑结构如下:


        inode编号以分区为单位,整体划分,不可跨分区

4.1.6 Data Block

        数据区:存放文件内容,也就是⼀个⼀个的Block。根据不同的文件类型有以下几种情况:
        对于普通文件,文件的数据存储在数据块中。
        对于目录,该目录下的所有文件名和目录名存储在所在目录的数据块中。
        Block 号按照分区划分,不可跨分区

对文件格式化的理解:

        在已经划分好的分区里,写入文件系统的管理结构(超级块、块组、inode表、位图),并把所有资源标记为“空闲”,让操作系统能够按规则存储和读取文件。

4.1.7 目录与文件名

        我们已经知道文件名并没有和它的属性保存在一起,那到底应该保存在哪呢?在目录对应的内容里!因为目录也是文件,文件=属性+内容,目录的内容部分就是用来存储文件名的!而目录名自然也存在上一级目录的内容里。站在磁盘角度,就存储方式而言,普通文件和目录是一样的。

        所以,访问文件,必须打开当前目录,根据文件名,获得对应的inode号,然后进行文件访问

4.2 路径解析

        上面我们已经知道,访问文件,必须先打开当前目录,但目录不也是文件吗?那也还要再打开当前目录的工作目录,类似“递归”,需要把路径当中的所以目录全部解析,出口是“/”根目录。这也就是为什么访问文件必须带路径的原因。从根目录开始,根据路径依次打开每一个目录查找下一级指定目录,直到找到访问文件,该过程就是路径解析。而根目录是固定文件名,inode号无需查找,系统开机后就会知道。

        路径怎么来?当访问文件时,都是通过指令/工具,本质就是一个进程访问,而进程内有CWD,提供当前工作路径。

4.3 路径缓存(dcache)

        问题:访问任何文件,都是从根目录开始进行解析的吗?

        答:原则上是,但是这样很慢,效率不高,所以了Linux会缓存历史路径结构

Linux中,在内核维护树状路径结构的内核结构叫做struct dentry:它的本质是内核内存中的一个对象,用于在内存中建立文件名和 inode 之间的快速映射关系。

注意:

        每个文件其实都要有对应的dentry结构,包括普通文件。这样所有被打开的⽂件,就可以在内存中形成整个树形结构。

        整个树形节点也同时会隶属于Hash,方便快速查找。

        更重要的是,这个树形结构,整体构成了Linux的路径缓存结构,打开访问任何文件,都在先在这棵树下根据路径进行查找,找到就返回属性inode和内容,没找到就从磁盘加载路径,添加dentry结构,缓存新路径。

4.4 挂载分区

挂载(mount) 是指:

        将某个存储设备(或分区)上的文件系统,关联到当前操作系统目录树中的一个空目录上,使得通过该目录可以访问该设备上的所有文件和子目录。这个被关联的目录叫做挂载点(Mount Point)

        也就是说分区写入文件系统无法被直接使用,还需要和指定的目录关联进行挂载才能使用。

那Linux系统怎么知道我要查找的文件在那个分区呢?

        简单来说,Linux 不是通过文件名查分区,而是通过路径逐级解析,在每一级目录上检查“是否挂着另一个分区”

一块磁盘,要被用户使用需要经过的步骤:

        分区->格式化->写入文件系统->写入管理信息->进行挂载

4.5 文件系统总结

5. 软硬件链接

5.1 硬链接

ln [文件名] link-hard

        真正在磁盘上找到文件的并不是文件名而是inode,而在Linux中,我们可以通过硬链接的方式,让多个文件名对应同一个inode。

        硬链接不是一个独立的文件,而是一组新的文件名和目标文件inode的映射关系。

        ls -l 可以查看硬链接数,当我们删除文件时干了两件事:1.在目录中将对应的记录删除 2.引用计数(inode内部维护)的硬链接数-1,如果为0,则将对应的磁盘释放。

5.2 软链接

        软链接是通过名字引用另外⼀个文件,但实际上,新的文件和被引用的文件的inode不同,应用场景上可以想象成⼀个快捷方式。

[root@localhost linux]# ln -s abc.s abc

软硬连接对比:
        软连接是独立文件,其内容存储的是“目标文件的路径字符串”,如果目标文件被删除,软链接就“断裂”(dangling),变成死链接,软链接可以跨分区。
        硬链接只是文件名和目标文件inode的映射关系,.和. .就是硬链接,硬链接不能跨分区,且禁止用户对目录建立硬链接(避免造成环形路径)!

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐