文件系统

55 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

一、理解文件系统

从头到位我们都在说打开的文件,磁盘中包含了上百万个文件,肯定不可能都是以打开的方式存在,其实文件包含打开的文件和普通的未打开的文件,接下来我们重点谈未打开的文件。我们知道打开的文件是通过操作系统被进程打开,一旦打开,操作系统就要维护多个文件,所以它是需要被操作系统管理的,也就是说这种方式,磁盘上和内存上都有这个文件,它们不是完全一样的,内存中的文件更强调的是属性和方法,磁盘中的文件更强调的是数据,它们是通过缓冲区关联的;而普通的未打开的文件在磁盘上,未被加载到内存中,它当然也要被管理;其中管理打开的文件和管理未打开的文件在操作系统中有一个功能模块叫做文件系统。之前我们谈过进程 vs 程序,一个被打开的程序就是进程,只不过我们在解释进程时不是严格把它当作文件来解释,需要明白的是进程是要被加载到内存的,程序就是一个磁盘文件,打开的文件是进程,而普通未打开的文件是程序。

在这里插入图片描述

💦 磁盘

  • ls -l 可以看到当前路径下文件的元数据,也就是文件的属性。其中这里的硬链接数我们还没有谈过,一会我们会谈。

    在这里插入图片描述

    这里在命令行上输入 ls -l,bash 解析 ls -l,fork 创建子进程,让子进程通过进程替换执行 ls -l,ls -l 会在当前路径下把文件的属性通过磁盘读到内核,再由内核读到用户空间显示出来。stat 命令还可以查看更详细的信息。

    在这里插入图片描述

    如果不深入的话,这块也没啥价值,谁不知道文件在磁盘上,谁不知道 ls -l 读取当前路径下文件的属性,所以我们还要研究它的原理。但是在此之前,我们需要先认识磁盘,关于磁盘这个话题,还会在数据库中再谈一次。

  • 众所周知,磁盘分为机械硬盘 (HDD) 和固态硬盘 (SSD),现在很多的电脑都是机械硬盘和固态硬盘组合使用,但服务器上大多都是固态硬盘,只有一些高效率的存储集群会用到固态硬盘,机械硬盘和固态硬盘在存储技术上肯定是不同的,而我们主要了解机械硬盘,因为它多用于服务器上,其次虽然固态硬盘要比机械硬盘快不少,但在 CPU 看来,都很慢,所以我们就了解最慢的。

    如下图,虽然磁盘的盘面看起来很光滑,但是它上面有一些同心圆,这些同心圆用圆白线划分,每一圈叫做磁道,数据写在这些有颜色的区域上。实际上你并不是把一圈的空间都用完,所以这里还使用了一些直白线划分,被圆白线和直白线划分出来的区域叫做扇区。所以当盘片在旋转、磁头摆动就可以找到这个盘面的任何一个扇区进行读写。

    在这里插入图片描述

    盘面是有两面的,且两面都是同心圆,根据配置不同,有些磁盘可能还有多组盘片,我们可以从上至下的分为不同的盘面,也叫做你是第几个盘面。

    在这里插入图片描述

  • 虽然在 C 语言中我们知道访问内存的基本单位是字节,但是在操作系统的角度认为内存的基本单位一般是 4kb,在操作系统看来,内存就是一个数组,每一个元素是 4kb,之前在谈进程地址空间时也说过它叫做页框,4kb 是页帧,所以操作系统申请内存时是按 4kb 为单位进行分配和加载的,语言层面上并不关心底层是怎么做的。磁盘存储也有基本单位,一个基本单位是一个扇区,它是磁盘读取的最小单元,大部分磁盘的一个扇区是 512byte,你会发现虽然这里好像越靠近圆心,扇区越小,其实它们都是 512byte,原因是越靠近圆心的虽然扇区越小,但是比特位也相对外圈更密集。内存和磁盘之间也是有交互的,它们之间的交互我们称为 output、input,也叫做 IO,一般内存和磁盘之间 IO 交互时,不是纯硬件级别的交互,而是要通过文件系统完成,也就是通过操作系统。这里用户和内存之间交互的基本单元大小是 1byte,一般内存和磁盘之间交互时的基本单元大小是 4kb,所以文件系统在往磁盘读数据时,要读 8 个扇区,这就是数据由磁盘加载到内存的过程。

    在这里插入图片描述

  • 其中我们再看 stat 中展示的信息,我们把内存和磁盘之间交互时的基本单元大小 4kb 叫 Blocks,这里的 IO Block:4096 就是 8 × 512。

  • 一般像这样的机械硬盘,物理上是圆状,操作系统很难去管理它,因为操作系统如果不对它进行抽象化处理,那么操作系统中的代码可能就是 read(盘面,磁道,扇区),操作系统需要知道这三个参数的话,那么一定要在操作系统读取磁盘的代码中以硬编码的形式写到操作系统中。如果有一天,你给自己的电脑加了一块固态硬盘,你要对固态硬盘进行读操作,就不能再用以前的方法了,因为固态硬盘与机械硬盘的结构不一样,它没有盘面、磁道、扇区,所以操作系统中曾经设计好的代码就得修改。很显然,这样的设计导致它们之间出现了强耦合,这是很不合理的。

    在这里插入图片描述

    所以我们需要对磁盘抽象化处理,将圆状结构的磁盘空间抽象成线性结构的磁盘空间,很多人就纳闷了,这里举两个例子方便理解,a) 其实在 C 语言中我们见过的 int arr[3][4] 二维数组就是把线性的数据结构抽象成了好理解的有行有列的结构。 b) 曾经风靡一时的磁带是把数据存储于那条黑色的带子上,可能是为了空间的原因,把带子卷起来形成一个圆状,所以磁带在物理上,既可以是圆状,也可以是线状。

    在这里插入图片描述

    同样的,也能把磁盘抽象成线性结构。把磁盘上的磁道抽象成线性形状,比如磁盘的所有磁道被我们抽象成了一条 500GB 的线性空间,我们可以把它看作一个很大的数组 —— 扇区 array[NUM],其中每一个元素是 512byte,操作系统要申请 4kb,那就给数组的 8 个元素。所以将磁盘抽象后,操作系统就摆脱盘面、磁道、扇区的束缚了,操作系统只关心你想访问的哪个下标,这里的地址我们称为逻辑区块地址(Logical Block Address, LBA),这里抽象出来的数组下标是和机械硬盘中盘面、磁道、扇区构成映射关系的,这里的映射关系是由对应的机械磁盘驱动维护的,操作系统想往 2 下标处写数据,最终 2 下标一定是能对应到具体磁盘中某个扇区上。如果要往固态硬盘中写数据,也是把它抽象成线性的数组,它也有自己的固态硬盘驱动维护数组下标和固态硬盘之间的映射关系。至此,通过抽象的方法,就完成了操作系统和磁盘之间的解耦。所以最终操作系统对磁盘的管理,转换成了对数组的管理。

    在这里插入图片描述