2

UNIX 简单文件系统

 2 years ago
source link: http://www.kongjunblog.xyz/2021/04/unix.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.


OSTEP 通过介绍传统UNIX文件系统 vsfs(very simple file system)来介绍文件系统的基本模型。本文是该节的读书笔记。

文件系统的核心要点在于:组织数据的数据结构和访问数据的方式。OSTEP 从这两个方面介绍了 vsfs,所以笔记也从这两个角度总结。

VSFS 基本机结构

UNIX 上第一个文件系统是由 UNIX 发明人 Ken Thompson 实现的,这个文件系统是文件系统技术的巨大突破,该文件系统一改之前单层次多文件的设计,实现了多层次的目录结构和基本的文件系统抽象。

文件系统的根本功能是持久地存储数据,所以磁盘的主要部分应该作为数据区存放数据。通常,文件不仅具有一般数据(文件内容)也有各种属性(元数据),因此文件系统还需特定文件属性结构记录文件的元数据。文件在磁盘中的具体存储位置对于用户不可见,但是也是文件必须的属性,vsfs 将元数据和文件的存储位置放到了同一个结构中,这个结构被称为 inode(index node)。vsfs 中包含一个 inode 向量,给该向量一个索引即可通过 inode 访问对应的文件,因此文件对应的 inode 在表中的索引也称为该文件的底层文件名。因为文件存储了访问该文件所需的全部信息,inode 完全成了文件的代名词,inode 的最大数量也就是该文件系统能存储的最大文件数量。

inode 中的文件通常被存储为多个块,因此无法使用一个地址来确定整个文件的位置。inode 中有 12 个直接指针直接指向存储文件的磁盘块地址,同时设置了 1 个间接指针,指向一个包含 1024 个直接指针的磁盘块。如果添加了间接指针还无法适应文件大小,还可以再添加三级指针。这种多级索引结构相当高效,仅在 inode 中增加了一个间接指针,就让能存储的文件大小增加 了 1024 * blocksize 字节

磁盘中一部分区域被分配给数据区,一部分被分配给 inode table,系统必须跟踪磁盘中块(扇区)的使用情况,记录某块是否被分配,以便在删除、创建、写入文件时分配、回收磁盘块。所以文件系统还需要一个分配结构,在 vsfs 中使用的是 bitmap结构,通过数据的位来判断对应的磁盘块是否被分配(1代表已分配,0代表未分配)。因为 vsfs 中可能需要分配、回收的块要么用作 inode,要么作为数据区,所以 bitmap 分配 inode bitmap 和 数据区bitmap,分别记录 inode table 和数据区的分配情况。

inode table、数据区、bitmap都是 vsfs 的内部实现,文件系统应该还需要一个描述整体信息的结构(文件系统大小、类型等),vsfs 为此创建了 superblock 描述文件系统整体信息。

UNIX 中的目录

在 UNIX 中,目录是一种特殊的文件,称为目录文件。目录文件中存储着该目录中的所有文件的文件名和对应的 inode table 索引。通过这种方法,文件系统可以以相同的方式对待文件和目录。

从 vsfs 看文件系统基本结构

vsfs 是极其经典的文件系统实现,体现了文件系统的基本模型。虽然文件系统的杰布结构各有各的不同,但是都具有 vsfs 的基本结构:

  1. 描述文件系统整体的结构:vsfs 中的 superblock
  2. 描述文件的结构:vsfs 中的 inode
  3. 记录磁盘块的分配结构: vsfs 中的 bitmap
  4. 存储数据的数据区: vsfs 中的数据区

inode 虽然是古老的 vsfs 中的结构,但许多现代操作系统也保留了 inode 结构。当然,inode 的内部结构肯定有所不同。

文件记录方式

inode 有不同的文件记录方式。

多级索引结构

inode 的多级索引结构是一种高效的文件块记录方式。如果文件比较小(不需要访问简介指针),访问文件时可以从 inode 直接读取到文件所有块的地址;即使需要读取间接指针或三级指针,也只需要通过 inode 在一两个磁盘块中读取文件块地址。这种多级索引结构保证系统能够快速查找到文件中的任意一块。大多数文件都是小文件(2KB),inode 中的多级索引结构可以看成一种不平衡的树,这种结构保证了对于小文件既高效又节省空间。

使用链接结构时,只需要在 inode 设置一个链表,每个链表的元素包含要访问的数据块地址。访问文件时,通过 inode 访问到链表第一个元素(文件块),再访问之后元素中记录的文件块。

这种结构的缺陷在于链表只能顺序访问,如果要访问文件的第 N 个文件块,就必须再链表中顺序查找,知道第 N 块。

旧的 Windows 文件系统 FAT 使用的就是链接结构。FAT 的全称 file allocation table 形象的说明了 FAT 的内部结构。为了性能,FAT 再内存中用一个向量建立个静态存储的链表结构,向量中的元素记录文件块的地址和下一个元素在表中的索引。

vsfs 的多级索引结构有些类似与虚拟内存中的分页机制,将文件划分成了一个个块,通过多级索引结构访问。范围结构(extent)类似与虚拟内存中的分段机制类似,将文件划分成一个个段,每一段通过一个基地址指针和长度变量记录,并将所有的记录存放在向量中。

这种结构将文件划分为多个连续的磁盘块,没有多级索引结构灵活,但比多级索引结构更加紧凑,在有大块空闲磁盘块的情形下性能很好。

磁盘空闲空间管理

磁盘空闲空间管理类似与空闲内存管理,内存管理中的多种方法都能应用到磁盘中。vsfs 使用的不过是其中最简单的一种,除了 bitmap,还可以使用空闲链表、B 树等结构。

文件的访问过程

文件系统提供了一个多层次的目录树,每次访问目录或文件都要从父目录查找欲访问的文件,一层一层地查找到目标文件。比如 /usr/local/bin/vim,先读取根目录 / 查找到目录文件usr,读取usr查找目录文件local,再查找并读取目录文件bin,再查找执行文件vim。可以看到访问某文件前必须查找到该文件,查找该文件的过程中进行多次磁盘IO,因此查找文件的成本与该该文件再目录树中的深度正相关。根目录没有父目录,所以根目录的位置直接记录在系统中,不需要查找。

读取文件的过程中涉及到文件状态的修改,所以除了读取操作还有写入操作。以顺序读取 /foo/bar为例,说明读取文件的过程。

1954702-20200604214802868-164065417.jpg

  1. 查找 bar:读取根目录 inode,读取根目录,查找 foo;读取 foo 的 inode,读取 foo;查找 bar,读取 bar 的 inode。
  2. 读取 inode:读取 inode,取得第一块的地址;读取数据;修改 inode 中的状态信息。

写入文件的过程与写入文件相似,但是涉及空闲空间的分配,操作更复杂。

1954702-20200604215752122-1572621600.png

查找、读取、写入文件的过程中会发生大量的磁盘IO降低系统性能。为了减少磁盘IO的次数,自然想到了缓冲机制。

在内存中分配一块区域作为 IO 缓冲区,使用 LRU 等策略,缓存最近访问的文件和 inode 等结构。IO 缓冲区不仅存在于内核态,也存在于应用态。比如标准 IO 库,fscanf系列函数向文件写入数据,实际上经过两个过程:

  1. 将数据写入标准 IO 的缓冲区(用户级)
  2. 将用户态缓冲区中的数据刷入内核缓冲区
  3. 内核缓冲区的数据刷入磁盘中

IO 缓冲区的空间是有限的,当占用的空间到达阈值时,就要使用特定的驱逐策略驱逐缓冲块,其中使用的策略跟虚拟内存系统中使用的策略基本相同。

IO 缓冲区作为内存和磁盘间的中间层,访问文件时 IO 缓冲区时透明的,会出现类似访问内存时高速缓冲不命中的情况。IO 缓冲区由软件实现,能够使用更加复杂的策略,而且磁盘IO的成本远高于内存访问,所以 IO 缓冲区一般使用写回和写分配的策略。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK