聊聊操作系统-文件系统

工作也有两年了,在研究很多项目时发现很多问题追根溯源都会到计算机底层的知识,也是越来越发现编程语言只是一层外壳,这个外壳需要去和操作系统协商使用后者管理的的计算机资源,包括存储资源和计算资源。如果计算机底层知识不牢靠,遇到一些问题还真是不好分析,也很容易成为职业上升的瓶颈。现在回想起大学时学习这么课时是比较抽象的,那时没有太多编程经验,知识很难落地,造成了只知其然不知其所以然的情况,所以现在重新梳理一下计算机操作系统的一些底层知识,在这里记录一下。

我们最常使用的各种文件是进程创建的信息逻辑单元,同时文件也是对磁盘的一种建模模型。

文件系统是对磁盘的抽象,他用于管理文件和磁盘的使用,一块磁盘有成千上万个文件,每个文件都是独立与其它文件的。

这里先简单的介绍一下磁盘的一些知识,磁盘盘片的示意图如下所示:

聊聊操作系统-文件系统

盘片:磁盘中圆形的盘片是主要记录数据的物理介质

扇区:为磁盘的最小物理存储单元,每个扇区的大小都是固定的,同时每个磁道的扇区数量都是相同的。

柱面:将扇区组成一个圆环就是柱面,柱面是磁盘分区的最小单位。

磁盘分区:就是在分区表中记录可以访问的柱面范围。

其中磁盘的第一个扇区是最重要的,这里面记录了硬盘的主引导记录(Master boot Record)MBR,以及记录磁盘的分区表。


我们常见的文件系统主要有FAT,ext2,ext3,NTFS等,下图为一个典型的文件系统的组织结构示意图。

聊聊操作系统-文件系统

我们知道在使用一块磁盘之前是一定要进行格式化的,而这个格式化的过程就是将文件系统的一些元数据写入到磁盘中,比如描述正文件管理系统的超级块,分区表信息,MBR信息,一旦格式化完成这些信息就很难修改了。磁盘属于块设备,每次操作系统读磁盘都是一块一块的从磁盘读取数据,下面我们主要通过linux系统下的ext2文件系统。

文件

在Linux系统中一个文件包含文件的权限(rwx)与文件属性(所有者,群组,时间参数),ext2文件系统将这些参数分别存放在不同的块中,权限与属相放置到inode中,至于实际的数据则放置到data block块中。在格式化磁盘分区格式化的时候,就为磁盘每个inode和block创建好了唯一的编号,每一个分区都有一个超级块,这个超级块记录了文件系统的整体信息,包括inode与block的总量,使用量,剩余量,同时在磁盘的使用过程中这些数据是实时变化的。

总结一下文件的存储结构:

  • super block:记录文件系统的总体信息,包括inode/block的总量、使用量、剩余量,以及文件系统的格式与相关信息。
  • inode:记录文件的属性,一个文件占用一个inode,同时记录此文件的数据所在的block编号。inode笃定大小为128bytes。
一个inode的大小是有限的,并且inode要存储的东西特别的多,如果我有一个400MB的文件,且每个block大小为4Kb那么我需要存10万条block编号,这个单个inode是不可能存的下的。我们通过看一下inode的结构来看一下是怎么解决这个问题的。
聊聊操作系统-文件系统
文件系统将inode记录block号码的区域定义为12个直接、1个间接、1个双间接、1个三间接的记录区。这样一个inode当block为1k时,所能支持的文件大小为12+256+256*256+256*256*256(K)=16G
  • block:实际记录文件的内容,根据文件大小不同,占用的block的个数也不同。block的大小有1k\2k\4k三种。

聊聊操作系统-文件系统


我们用上图来说明一下inode与block的关系,假如一个文件的属性与权限存在编号为4的inode上,inode记录了文件的存储位置为2、7、13、15这几个block上。这样操作系统就能在短时间内读取出全部的数据。

inode和block在磁盘格式化之后就已经规划好了,由于现在磁盘容量巨大,如果将所有的inode和block都放在一起是不容易管理的,所以,磁盘在格式化的时候是按照磁盘分区inode/block/super block分组进行格式化的。在本文的第一幅图中我们可以看到,磁盘分区为块组0、块组1、块组2、块组3等。


  • 组描述符:这项描述的是每个组块中block的起始编号。
  • block块位图:这个位图表是记录block的使用状态的,这里面的值是要随着添加和删除文件变化的。
  • inode位图:功能类似于block块位图。
目录
我们知道文件是用来组织管理实际数据的,那目录看起来是文件的一个容器而已,在linux系统中当创建一个目录时,ext2系统会分配一个inode和至少一个block给该目录,inode用来存储目录的权限和属性,并记录分配到的那个block编号,而block记录的是目录下文件的文件名与该文件名所占用的inode编号。

说完了文件和目录的读取之后,我们来看当创建一个文件和目录时文件系统的行为:
  • 先确定用户对于要添加文件的目录是否有w和x的权限
  • 根据根据inode bitmap和block bitmap找到没有使用的inode和block,然后将数据写入对于的磁盘位置
  • 同步更新使用的inode bitmap和block bitmap
一般情况下这个过程是没有问题的,但是当系统出现故障崩溃时有可能造成bitmap数据和实际数据不一致的情况,为了解决这个问题,日志文件系统被设计出来。
日志文件系统就是在文件系统中专门开辟了一块空间,用于记录写入和修改文件的操作,当出现不一致的情况时,文件系统只需要检查日志记录块就可以恢复数据一致性。

实际上一块磁盘上的不同分区可能有多个文件系统,用户并不能感知到正在使用的是那种文件系统,这要归功于Linux内核提供的虚拟文件系统VFS,VFS对不同的文件系统进行了抽象,来管理所有的文件系统。它的结构如下图所示:
聊聊操作系统-文件系统

最后结合上一篇博客聊聊操作系统-存储管理之虚拟内存中提到的,虚拟内存页表中有一项是记录该页是否被修改的字段。
同时从存储结构上看内存时磁盘的缓存,实际上操作系统与磁盘的数据交换,确实是在内存中为磁盘开辟了一块缓存,操作系统会将常用的数据放置到这块缓存中,当内存中这块数据被修改了之后,系统会不定时的将这些脏数据写回到磁盘,以保住数据的一致性。

从虚拟内存的角度来说,虚拟页和磁盘中的块映射起来,当虚拟页被加载到内存的物理页的时候,就由DMA把虚拟内存对应的磁盘块加载到内存的对应地址的物理页中。当物理页写回到磁盘时,也是由DMA把数据传输到磁盘控制器,由磁盘控制器写到磁盘块对应的扇区。内存和磁盘交换数据的时候实际采用了内存的缓冲区来加速磁盘的访问速度。

缓冲区的目的是适配两个速度不一致的设备,从磁盘的工作原理我们看到磁盘操作是一个很慢的操作,内存操作相比磁盘操作是一个很快的操作,为了让内存对磁盘的读写不必等待磁盘操作返回再返回,操作系统设置了内存缓冲区来加速对磁盘的访问速度。