zoukankan      html  css  js  c++  java
  • 文件系统实现

    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为例,说明读取文件的过程。

    1. 查找 bar:读取根目录 inode,读取根目录,查找 foo;读取 foo 的 inode,读取 foo;查找 bar,读取 bar 的 inode。

    2. 读取 inode:读取 inode,取得第一块的地址;读取数据;修改 inode 中的状态信息。

    写入文件

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

    文件缓冲

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

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

    1. 将数据写入标准 IO 的缓冲区(用户级)

    2. 将用户态缓冲区中的数据刷入内核缓冲区

    3. 内核缓冲区的数据刷入磁盘中

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

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

  • 相关阅读:
    Python数据分析与机器学习-Matplot_2
    Python数据分析与机器学习-Matplot_1
    1008. 数组元素循环右移问题 (20)
    Latex小技巧
    执行PowerShell脚本的时候出现"在此系 统上禁止运行脚本"错误
    Linux使用MentoHust联网线上校园网, 回到普通有线网络却连不上?
    Re:uxul
    Linux下nautilus的右键快捷菜单项设置
    从入门到入狱——搭讪技巧
    Latex命令
  • 原文地址:https://www.cnblogs.com/kongj/p/13047010.html
Copyright © 2011-2022 走看看