OS-文件系统

概论

  • 文件是进程创建的信息逻辑单元。可以把每一个文件看作一个地址空间。
  • 存储在文件的信息必须是持久的,不会因为进程的创建和终止而影响。
  • 文件受操作系统管理,操作系统中处理文件的部分为文件系统

文件

文件命名

  • 文件名以.分割为两部分,前面为文件名,后面为文件扩展名。
  • 文件扩展名表示文件的一些信息,如文件类型等。

文件结构

fXcHkhGNpIyx75J.png

字节序列

  • 把文件看成字节序列/字节流。为操作系统提供了很大的灵活性。
  • 文件中的数据由一系列二进制流或字符流组成,“流式文件”,也称无结构文件

记录序列

  • 文件是具有固定长度记录的序列。每个记录都有内部结构。操作时以一个记录为单位进行操作。
  • 根据记录长度是否相等,分为定长记录可变长记录
  • 每条记录有一个数据项可作为关键字

记录树

  • 文件由一棵记录树构成。每个记录不必有固定的长度。 记录的固定位置上有一个键字段。这棵树按键字段进行排序,从而可以对键字段进行快速查找。

文件类型

  • 普通文件:包含有用户信息的文件。(ascii文件,二进制文件)
  • 目录:管理文件系统结构的系统文件。
  • 字符特殊文件:用于串行IO设备
  • 块特殊文件:用于磁盘类文件

文件属性

  • 文件的附加信息:文件的创建日期,大小等,称为文件属性,也称为元数据
    b38IzOJpfhHcaRP.png
  • 前4个属性与文件保护有关。
  • 标志是一些位或短字段,用于控制或启用某些特殊属性。
  • 记录长度,键的位置,键的长度只能出现在用关键字查找记录的文件里,它们提供了查找关键字所需的信息。

文件访问

顺序访问

  • 从头按顺序存取文件的全部字节或记录。
  • 不能跳过某些内容,可以返回到起点,或者多次存取。
  • 当存储介质为磁带时,顺序存取方式非常合适的。

随机访问

  • 可以以任何次序存取文件的字节或者记录。(不按顺序地读取,或按关键字访问记录)
  • 对于数据库系统,这种存取方式是必不可少的
  • 有两种方法可以指示从何处开始读写文件:
    • 每次读写操作都给出开始读写文件的位置
    • 用一个seek操作设置当前读写位置,然后从此位置开始顺序读写文件。

文件操作

  • Create:创建不包含任何数据的文件。
    • 在外存中找到文件所需的空间(结合空闲链表法、位示图、成组链接法等管理策略,找到空闲空间) ,根据文件存放路径的信息找到该目录对应的目录文件在目录中创建该文件对应的目录项。目录项中包含了文件名、文件在外存中的存放位置等信息。
  • Delete:删除文件释放磁盘空间
    • 根据文件存放路径找到相应的目录文件,从目录中找到文件名对应的目录项
    • 根据该目录项记录的文件在外存的存放位置、文件大小等信息,回收文件占用的磁盘块。(回收磁盘块时,根据空闲表法、空闲链表法、位图法等管理策略的不同,需要做不同的处理)
    • 从目录表中删除文件对应的目录项
  • Open:在使用文件之前,必须先打开文件。
    • 根据文件存放路径找到相应的目录文件,从目录中找到文件名对应的目录项,并检查该用户是否有指定的操作权限
    • 将目录项复制到内存中的“打开文件表”中。并将对应表目的编号返回给用户文件描述符)。
    • 之后用户使用打开文件表的编号(文件描述符)来指明要操作的文件。
  • 进程打开文件表中特有的属性:读写指针访问权限(只读?读写?)
  • 系统打开文件表中特有的属性:计数器(有多少个进程打开了该文件)
  • Close:访问结束后,关闭文件释放内部表空间
    • 将进程的打开文件表中相应表项删除
    • 回收分配给该文件的内存空间等资源
    • 系统打开文件表的计数器count减1,若count= 0,则删除对应表项。
  • Read:在文件中读取数据
    • 操作系统在处理read系统调用时,会从读指针指向的外存中,将用户指定大小的数据读入用户指定的内存区域中
  • Write:向文件写数据
    • 操作系统在处理write系统调用时,会从用户指定的内存区域中,将指定大小的数据写回写指针指向的外存。
  • Append:在文件末尾追加数据
  • Seek:指定从何处开始读取数据
  • Get Attributes:读取文件属性
  • Set Attributes:设置文件属性
  • Rename:重命名

目录

单级目录系统

  • 目录的最简单形式是在一个目录中包含所有的文件,称为根目录。
  • 单级目录实现了“按名存取”,但是不允许文件重名
  • 在创建一个文件时,需要先检查目录表中有没有重名文件,确定不重名后才能允许建立文件,并将新文件对应的目录项插入目录表中。
  • 显然,单级目录结构不适用于多用户操作系统

kfJHEonBCWPLmul.png

二级目录结构

  • 分为主文件目录用户文件目录
  • 主文件目录:记录用户名及相应用户文件目录的存放位置
  • 用户文件目录由该用户的文件FCB组成
  • 允许不同用户的文件重名。文件名虽然相同,但是对应的其实是不同的文件。

层次目录系统

  • 可以用很多目录对文件进行分组。
  • 不同目录下的文件可以重名
    FnZhcvqu4z3Qwm9.png

无环图目录结构

  • 在树形目录结构的基础上,增加了一些指向同一个节点的有向边,目录成为有向无环图
  • 可以更方便的实现多个用户之间的目录共享
  • 可以用不同的文件名指向同一个文件,甚至可以指向同一个目录(共享同一目录下的所有内容)。
  • 需要为每个共享结点设置一个共享计数器,用于记录此时有多少个地方在共享该结点。用户提出删除结点的请求时,只是删除该用户的FCB、并使共享计数器减1,并不会直接删除共享结点。只有计数器为0时,,才会真正的删除。

索引节点

  • 索引节点i-node,包含了FCB中除了文件名之外的文件描述信息
  • 此时目录项只要存储每个文件名和对应的索引节点指针,使得一个盘块中的目录项更多,,减少了磁盘IO次数,加快了文件检索速度
  • 当找到文件名对应的目录项时,才需要将索引结点调入内存,索引结点中记录了文件的各种信息,包括文件在外存中的存放位置,根据“存放位置”即可找到文件。
  • 存放在外存中的索引结点称为“磁盘索引结点”,当索引结点放入内存后称为“内存索引结点”。
  • 相比之下内存索引结点中需要增加一些信息,比如:文件是否被修改、此时有几个进程正在访问该文件等。

路径名

  • 用目录树组织文件系统时,需要用路径名指向特定文件。
  • 绝对路径从根目录到文件的路径组成,如:/usr/jim/aaa
  • 相对路径即相对于当前工作目录的路径。如果当前工作目录为/usr/ast,则/usr/ast/aaa的文件路径名可以写成相对路径aaa./aaa

目录的实现

文件控制块

  • FCB的有序集合称为“文件目录”,一个FCB就是一个文件目录项。

  • FCB中包含了文件的基本信息(文件名、物理地址、逻辑结构、物理结构等),存取控制信息(是否可读/可写、禁止访问的用户名单等),使用信息(如文件的建立时间、修改时间等

  • 目录系统的主要功能是把文件名映射成定位文件数据所需的信息。

VBrNmM78RDlEz2H.png

简单设计方案(a)

  • 目录有一个固定大小的目录项列表,每个文件对应一项(FCB)。包含一个固定长度的文件名,一个文件属性的结构体,以及用以说明磁盘块位置的磁盘地址。
  • 特点:
    • 固定大小的目录项
    • 目录项中包含文件的所有属性

索引节点(i-node)方案(b)

  • 文件名和其他属性分离,目录项中只有文件名和文件索引节点号
  • 文件的属性均在其对应的索引节点中

文件的查找流程

  • 系统读取目录文件对应的数据块。
  • 在内存中线性遍历目录文件中的所有目录项。
  • 找到文件名匹配的条目。
  • 读取下一级目录的物理地址。
  • 循环执行流程,直到读取对应文件的内容。
  • 索引节点方案的优点
  • 平均 I/O 次数大大减少:
    • 因为目录项只有“名字+编号”,非常短(可能只有 16 字节)。
    • 一个磁盘块可以存下更多的目录项。
    • 在搜索文件名时,绝大多数目录的数据都能在一个或很少的几个磁盘块中读完。系统不需要把庞大的 FCB 读入内存,只想找个名字而已。

文件系统布局

FbAaONQtz34cEvf.png

  • 磁盘的0号扇区为主引导目录(MBR),用来引导计算机。
  • 在MBR的结尾是分区表,给出了每个分区的起始和结束地址。 表中的一个分区为活动分区。
  • 在计算机被引导时,BIOS读入并执行MBR,MBR做的第一件事是确定活动分区,读入它的第一个块,称为引导块,并执行。引导块中的程序装载该分区的操作系统。
  • 超级块(superblock):包含文件系统的所有关键参数。
  • 空闲块信息块:位图或指针列表
  • i节点:说明了文件的各种信息,属性。
  • 根目录/文件与目录。

文件的逻辑结构(用户级结构)

顺序文件

hzQnUoturCTSLiJ.png

  • 文件中的记录一个接一个顺序排列。(逻辑上)
  • 串结构:记录之间的顺序与关键字无关。
  • 顺序结构:记录之间的顺序按关键字排列。

索引文件

Z8e15GkQpqTlShf.png

  • 建立索引表,每条记录对应一个索引项。
  • 索引表本身是定长记录的顺序文件,可以随机访问,具有很快的检索速度。
  • 可以将关键字作为索引表内容,用多个不同的数据项建立多个索引表。

索引顺序文件

Ted6SFMoHlmpDgU.png

  • 将记录分组,建立索引表,为一组记录设置一个索引表项
  • 每个分组是一个顺序文件

多级索引顺序文件

  • 当文件记录很多时,为了进一步提高检索效率,可以建立多级索引表。
    r8XfNdKWmT71RDi.png

文件的实现(物理级结构)

磁盘块

类似于内存分页,磁盘中的存储单元也会被分为一个个“块/磁盘块/物理块”。很多操作系统中,磁盘块的大小与内存块、页面的大小相同

文件块

在外存管理中,为了方便对文件数据的管理,文件的逻辑地址空间也被分为了一个一个的文件“块”。于是文件的逻辑地址也可以表示为(逻辑块号,块内地址)的形式。

  • 操作系统为文件分配存储空间都是以块为单位的
  • 用户通过逻辑地址来操作自己的文件,操作系统要负责实现从逻辑地址到物理地址的映射

文件的实现

  • 文件的物理结构是指文件数据盘块在硬盘等存储介质中的组织方式,有连续存储方式和离散存储方式两种。

连续分配

qU6DFLbYVmMek8s.png

  • 把每一个文件作为一连串连续数据块存储在磁盘上。
  • 优点
    • 实现简单,只需记录第一块的磁盘地址和文件块数即可。
    • 物理块号=起始块号+逻辑块号
    • 相对于其他文件物理结构,存取速度最快。连续分配的文件在顺序读/写时速度最快。
    • 支持随机访问
  • 缺点
    • 物理上采用连续分配,存储空间利用率低,会产生难以利用的磁盘碎片。可以用紧凑来处理碎片,但是需要耗费很大的时间代价。
    • 不支持文件的动态增长

链表分配

rNlU8pEdDmcFMfO.png

  • 为每个文件构造磁盘块链表每个块的第一个字作为指向下一块的指针,其他部分存放数据。
  • 文件目录中记录了文件存放的起始块号。当然,也可以增加一个字段来表示文件的长度
  • 优点
    • 采用离散式分配,能充分利用磁盘空间(不产生外部碎片,文件的最后一块可能有内部空余)。
    • 支持文件的动态增长
  • 缺点
    • 不支持随机访问,因为每一次必须从头开始一直读到想要读的那一块。

使用内存中的表(文件分配表)进行分配

IJa6Efx315KewyU.png

  • 取出每个磁盘块的指针字,把它们放在内存的一个表中。 这个表就是文件分配表(File Allocation Table,FAT)
  • 目录只要记录文件的起始块即可
  • 一个磁盘仅设置一张FAT开机时,将FAT读入内存,并常驻内存。FAT的各个表项在物理上连续存储,且每一个表项长度相同,因此“物理块号”字段可以是隐含的。
  • 利用文件分配表,可以直接找到对应文件的起始块,顺着链往下找即可找到全部文件。
  • 逻辑块号转化为物理块号:从目录项中找到起始块号,若i>O,则查询内存中的文件分配表FAT,往后找到i号逻辑块对应的物理块号,i--
  • 逻辑块号转换成物理块号的过程不需要读磁盘操作。
  • 优点
    • 支持随机访问,访问效率高。不产生外部碎片,支持文件的动态增长
  • 缺点
    • 必须把整个表存在内存中对大型磁盘不能较好支持

索引文件分配

tQqC2iJglvNzAcn.png

  • 给每个文件建立一个索引文件(索引表),记录了每个文件块的磁盘地址(物理块号)。

  • 索引块:存放索引表的磁盘块。

  • 数据块:存放文件数据的磁盘块。

  • 目录需要记录文件的索引块的物理块号

  • 根据索引文件,就可以找到文件的所有文件块。

  • 访问逻辑块号i:从FCB找到文件索引表的位置,查索引表的第i项即可找到物理块号。

  • 假设物理块大小为4KB,索引表指针为4个字节

    • 能管理的最大文件1K(4KB/4个目录项)*4KB(每个目录项对应的物理块大小)=4MB
    • 磁盘IO操作次数:1(访问索引文件)+1(访问物理块)
  • 类似地,索引文件也可以建立多级索引文件。

Q5vVt6DLBqmKU2E.png

  • 假设物理块大小为4KB,索引表指针为4个字节
    • 能管理的最大文件1K(4KB/4 个二级索引项)*1K(4KB/4 个目录项)*4KB(每个目录项对应的物理块大小)=4GB
    • 磁盘IO操作次数:1(访问一级索引)+1(访问二级索引文件)+1(访问物理块)
  • 使用二级索引时,先算出需要查找的一级索引表的表项(逻辑块号/索引表长度)和二级索引表表项(逻辑块号%索引表长度),访问一次一级索引表和一次二级索引表,得到物理块号,访问对应的存储单元,一共需要3次磁盘IO(读入内存)
  • 采用K层索引结构,且顶级索引表未调入内存,则访问一个数据块只需要K+1次读磁盘操作

UNIX文件

  • 直接间接混合寻址方式
  • 一共分为13个块:前10个块为直接索引,第11为一级索引12为二级索引13为三级索引
    SOBc6irVyHMQwP4.png

文件的存取方法

1HdfEQ3n8M6PwyX.png

文件存储空间管理

存储空间的划分和初始化

  • 存储空间的划分:将物理磁盘划分为一个个文件卷(逻辑卷、逻辑盘)
  • 存储空间的初始化:将各个文件卷划分为目录区、文件区
  • 目录区:存储文件目录信息磁盘存储管理所用信息
  • 文件区:存放文件数据

磁盘空间管理

磁盘块大小的影响

gZFV9iE5SXTpGQ1.png

  • 实线数据传输率 (Data rate)
    • 趋势: 随着块大小变大,传输率显著上升。块越大,读取同样大小的文件所需的块数量就越少,减少寻道次数。
  • 虚线磁盘空间利用率 (Disk space utilization)
    • 趋势: 随着块大小变大,空间利用率急剧下降(特别是超过 2K 以后)。原因是内部碎片的产生。当块大小增加到 8K、16K 时,对于大量的小文件(如几十字节的日志、配置文件),每个文件都要独占一个巨大的块,内部碎片巨大,导致大部分磁盘空间被浪费。
  • 不存在完美的块大小,只能找平衡点 (Trade-off)。(如 4KB)

位示图法

Jmir8u5PVwBhTvL.png

  • 每个二进制位对应一个盘块。在本例中,“0”代表盘块空闲,“1”代表盘块已分配
  • 位示图一般用连续的“字”来表示,如,一个字的字长是8位,字中的每一位对应一个盘块。因此可以用(字号,位号)对应一个盘块号。当然有的题目中也描述为(行号,列号)
  • 数组元素\(b_{ij}\)所代表的块号为:\(x=m*i+j\)   (\(m\)为矩阵的列数,或字长)
  • b号盘块对应的字号i=b/n,位号j=b%n
  • 分配:若文件需要k个块,顺序扫描位示图,找到k个相邻或不相邻的“0”;根据字号、位号算出对应的盘块号,将相应盘块分配给文件;将相应位设置为“1”
  • 回收:根据回收的盘块号计算出对应的字号、位号;将相应二进制位设为“0”

空闲表法

HDL2V5q3TprivYt.png

  • 记录了一段连续的空闲盘块区间的起始位置和长度
  • 分配磁盘块:首次适应,最佳适应,最坏适应等
  • 回收磁盘块:注意回收区前后相邻是否有空闲区,注意空闲区的合并

空闲链表法

以盘块为单位

xFs7vgbCowfMeWA.png

  • 操作系统保存头指针即可
  • 分配:若某文件申请K个盘块,则从链头开始依次摘下K个盘块分配,并修改空闲链的链头指针
  • 回收:回收的盘块依次挂到链尾

以连续的空闲块成组为单位

  • 操作系统记录头指针。
  • 分配
    • 若某文件申请K个盘块,则可以采用首次适应、最佳适应等算法,从链头开始检索,按照算法规则找到一个大小符合要求的空闲盘区,分配给文件。
    • 若没有合适的连续空闲块,也可以将不同盘区的盘块同时分配给一个文件,注意分配后可能要修改相应的链指针、盘区大小等数据。
  • 回收
    • 若回收区和某个空闲盘区相邻,则需要将回收区合并到空闲盘区中。
    • 若回收区没有和任何空闲区相邻,将回收区作为单独的一个空闲盘区挂到链尾

成组连接法

WdQxtDl3OwykaMC.png

  • 核心思路:将空闲块分成若干组,每组 100 个(假设),并像链条一样把这些组串起来,但只在内存中维护“当前这一组”的信息。

  • 文件卷的目录区中专门用一个磁盘块作为“超级块”,当系统启动时需要将超级块读入内存。并且要保证内存与外存中的“超级块”数据一致。

  • 系统启动时,将第一组的信息(包含 100 个空闲块号的栈)读入内存中的专用区域——超级块。所有的分配和回收操作,优先在超级块的内存中进行,速度极快。

  • 在内存的超级块中,维护着两个关键数据(可以理解为一个):

    1. s_n (计数器): 记录当前组中可用的空闲块数量
    2. s_free[100] (地址栈): 一个数组,存放当前这一组所有空闲块的物理地址。
    3. 这一组的第一个磁盘块存放着下一组的块的地址信息
  • 分配:

    • 若当前分组的空闲块数足够: 从 s_free 数组中取出一个地址分配给用户, 将 s_n 减 1。
    • 当前组只剩最后一个块 (s_n == 1) 这是临界情况:读取该块(第一块)的内容(即下一组的索引表),将读取到的下一组信息复制(计数器和地址数组)覆盖到内存的超级块中,将第一块分配给用户使用。
  • 回收:

    • 当前组未满:将释放的块号填入当前分组, 将 s_n 加 1。
    • 当前组已满:将内存超级块中现有的 s_ns_free 数据,写入到刚刚释放的这个新块中,将超级块的 s_n 重置为 1,第一个盘块指向这个刚刚释放(现在已存满索引信息)的块号。

共享文件

多个用户共享同一个文件,意味着系统中只有“一份”文件数据。并且只要某个用户修改了该文件的数据,其他用户也可以看到文件数据的变化。(区别于复制文件的概念)

硬链接

  • 基于索引节点,索引结点中设置一个链接计数变量count,用于表示链接到本索引结点上的用户目录项数
  • 以不同的文件路径名共享一个文件副本,不额外占用磁盘空间
  • 不允许给目录创建硬链接
  • 硬链接只有在同一个文件系统(同一分区内)中才能创建
  • ln 源文件 目标文件
    WzhLDnYa9XBUilA.png

软链接(符号链接)

  • 建立一个Link类型的文件,记录了文件的存放路径。类似于windos快捷方式。
  • 当User3访问“ccc”时,操作系统判断文件“ccc”属于Link类型文件,于是会根据其中记录的路径层层查找目录,最终找到User1的目录表中的“aaa”表项,于是就找到了文件1的索引结点。
  • 以路径的形式存在,类似于Windows中的快捷方式。
  • 软链接可以跨文件系统 ,硬链接不可以。
  • 软链接可以对一个不存在的文件名进行链接
  • 软链接可以对目录进行链接
  • ln -s 源文件 目标文件

文件系统可靠性

文件系统可靠性主要解决三个层面的问题:

  1. 数据持久性 (Durability): 存进去的数据,过多久都在,不会莫名其妙消失(应对硬件故障)。
  2. 数据一致性 (Consistency): 无论发生什么(如突然断电),文件系统的元数据(目录树、位图)和实际数据必须对应得上,不能出现“有文件没数据”或“有数据找不到文件”的情况。
  3. 错误检测与恢复: 出错了能发现,发现了能修好。

文件备份

  • 文件备份是指将文件系统中的数据(包括文件本身和目录结构等元数据)复制到另一个存储介质(如磁带、外部硬盘、云存储或另一个服务器)上的过程。备份的主要目的是应对数据丢失系统故障

备份策略

  • 全量备份无论文件是否被修改,将系统中所有选定的文件完整地复制一份
    • 优点: 恢复最快、最简单(只需要最近一次的全量备份即可)。
    • 缺点: 备份时间最长,占用存储空间最大
  • 增量备份:仅备份自上一次备份(无论是全量还是增量)之后发生变化的文件
    • 优点: 备份速度最快,占用空间最小。
    • 缺点: 恢复最麻烦。需要先恢复最近的全量备份,然后按顺序恢复每一个增量备份。如果中间某一份增量备份损坏,后续数据可能无法恢复。
  • 物理转储 :不关心文件结构,直接从磁盘的第 0 块开始,按顺序将所有磁盘块复制到磁带或备份盘。(磁盘视角)
    • 优点: 速度极快,简单高效。
    • 缺点: 会备份未使用的空闲块(浪费空间)。 如果磁盘有坏块,备份可能会失败或包含坏数据。恢复特定文件极其困难(通常只能整盘恢复)。
  • 逻辑转储:操作系统从根目录开始,递归遍历文件系统树,读取每个文件并将其复制。(文件视角)
    • 优点只备份实际存在的文件(不备份空闲块)。 可以方便地恢复单个文件可以跨平台/跨文件系统恢复。
    • 缺点: 速度较慢,因为需要频繁查找目录和移动磁头。

需要备份的文件

Ajkq9WQvmF1EDUT.png

  • 方框表示目录,圆圈表示文件
  • 每个目录和文件用i-节点(i-nodes)号来表示
  • 有阴影的表示自上次备份后已修改的目录或者文件(Achive flag: 1)
  • 逻辑转储也需要备份已修改文件(或目录)的路径上的所有目录,原因如下:
    • 可能需要恢复文件到相应的目录中
    • 可能需要将被备份的文件或者目录恢复到另一个计算机的全新文件系统中。

对活动文件系统的备份

  • 活动文件系统:就是系统正在运行,用户正在不停地读写文件。
  • 核心问题:因为在转储过程中添加、删除或修改文件和目录可能会导致文件系统的不一致性
  • 停机备份
    • 暂停服务 (Quiesce): 通知所有应用程序暂停写入数据
    • 同步缓存 (Sync): 操作系统将内存中的缓存数据强制刷入磁盘(Flush buffer cache),确保磁盘上的数据是最新的。
    • 卸载/锁定 (Unmount/Lock): 将文件系统卸载(Unmount)或者设为“只读模式”。此时文件系统不再是“活动”的
    • 执行备份: 备份软件开始工作,从头到尾复制数据。
    • 恢复服务: 备份完成后,重新挂载文件系统,恢复读写权限,允许用户访问。
    • 通过强制手段把“活动”系统变成了“静止”系统。
  • 快照
    • 下达快照指令时,系统会极短暂地阻塞写请求(通常是毫秒级),强制将内存中的元数据刷入磁盘。
    • 系统不复制实际的文件数据,而是复制一份当前文件系统的“目录结构”和“索引指针”(Inode Map)。(这个过程极快,O(1)复杂度,因为它只抄了“目录”,没抄“内容”。)
    • 系统恢复正常读写。此时生成了一个“快照视图”(只读)和一个“活跃视图”(可读写)。
    • 备份软件通过“快照视图”去慢慢读取数据并传输到磁带或云端。用户此时在“活跃视图”中继续工作。(Copy-on-Write (CoW, 写时复制)
    • 备份结束后,删除快照,回收空间。

文件一致性

  • 文件系统的一致性是指:文件系统的元数据(Metadata)与实际存储的数据状态必须逻辑吻合,且元数据内部的不同记录之间必须互相印证。
  • 影响文件系统可靠性的另一个问题是文件系统的一致性。
  • 很多文件系统读取磁盘块,进行修改后,再写回磁盘。如果在修改过的磁盘块全部写回之前系统崩遗,则文件系统有可能处于不一致状态
  • 如果一些未被写回的块是节点块、目录块或者是包含有空闲表的块时,这个问题尤为严重。

一致性检测

块的一致性检查

  • 检查块分配与否的一致性
  • 构建两张表,每张表中为每个块设立一个计数器,都初始化为0。第一个表中的计数器跟踪该块在文件中的出现次数,第二个表中的计数器跟踪该块在空闲表或空闲位图中的出现次数
  • 接着检验程序使用原始设备读取全部的i节点忽略文件的结构,只返回从零开始的所有磁盘块。
  • 由i节点开始,可以建立相应文件中用到的全部块的块号表每当读到一个块号时,该块在第一个表中的计数器加1。(即有文件使用这个块)
  • 然后,该程序检查空闲表或位图,查找全部未使用的块。每当在空闲表中找到一个块时,就会使它在第二个表中的相应计数器加1。(即没有文件使用这个块)

8DlKFMzhN9BiXpw.png

  • 如图所示,分为不同情况:
    • 每一块或者在第一个表计数器中为1,或者在第二个表计数器中为1文件系统一致
    • 两张表均为0:没有出现在任何一张表中,此时该块出现块丢失,修复方案:把该块加入到空闲表中即可。
    • 在第一张表出现了两次:数据块重复,在两个或多个文件中出现同一个数据块。修复:分配一个新的空闲块,将块的内容复制进去。让其中一个文件指向这个新块,另一个文件保留指向旧块,让两个文件各自拥有独立的数据块,互不干扰。
    • 在第二张表出现了两次:空闲块重复,在空闲表中出现了两次,修复:重新建立空闲表
    • 在两张表均为1:矛盾块,为了保护数据,必须假设它正在被使用,修复:从空闲表中移除该块

文件的一致性检查

  • 检查文件链接数与实际访问路径数的一致性
  • 文件系统检验程序检查目录系统。此时也要用到一张计数器表,但这时是一个文件(而不是一个块)对应于一个计数器
  • 程序从根目录开始检验,沿着目录树递归下降,检查文件系统中的每个目录。对每个目录中的每个文件,将文件使用计数器加1
  • 要注意,由于存在硬链接,一个文件可能出现在两个或多个目录中。而遇到符号链接是不计数的,不会对目标文件的计数器加1。
  • 在检验程序全部完成后,得到一张由节点号索引的表,说明每个文件被多少个目录包含
  • 检验程序将这些数字与存储在文件节点中的链接数目相比较。
  • 假如某文件的链接数为a,实际访问路径数为b
    • \(a = b\),文件系统正常一致
    • \(a>b\),这时即使所有的文件都从目录中删除,这个计数仍是非0,文件不会被删除。该错误并不严重,却因为存在不属于任何目录的文件而浪费了磁盘空间,修正:将 Inode 里的计数修正为实际数。
    • \(a<b\),如果同一个文件链接两个目录项,但其节点链接计数只为1,如果删除了任何一个目录项,对应节点链接计数变为0。当节点计数为0时,文件系统标志该节点为“未使用”,并释放其全部块。这会导致其中一个目录指向一未使用的节点,而很有可能其块马上就被分配给其他文件。解决方法同样是把节点中链接计数设为目录项的实际个数值。

文件系统性能优化

块高速缓存

eLUMzvSBOICctEw.png

  • 高速缓存指的是一系列的块,它们在逻辑上属于磁盘,但实际上基于性能的考虑被保存在内存中
  • 检查全部的读请求,查看在高速缓存中是否有所需要的块。如果存在,可执行读操作而无须访问磁盘。如果该块不在高速缓存中,首先要把它读到高速缓存,再复制到所需地方。之后,对同一个块的请求都通过高速缓存完成。
  • 块缓冲区满时,再调入块,则需要块置换,可以采用一个修改的LRU方案
  • 盘块可根据其作用进行分类,如文件目录块、空闲块表块、i节点(i-nodes)块、索引块(间接寻址块)、全满数据块、部分满数据块等。对于某些块,如i节点块(i-nodes)极少可能在短时间内被引用两次,而满数据块再次被访问的可能性也相对较少,可以考虑将它们放在LRU前端。
  • 考虑到系统崩溃可能造成文件系统的不一致性,对于那些关系到文件系统一致性的关键块,被修改后,需要及时更新到磁盘
    • UNIX定期更新策略:Update(无限循环中不断执行syc调用,每两次调用之间休眠一定时间) + sync(强制性地把全部修改过的块立即写回磁盘)
    • Windos即改即更策略只要被写入高速缓存,就把每个被修改的块写回磁盘。 将高速缓存中所有被修改的块立即写回磁盘称为通写高速缓存。与非通写高速缓存相比,通写高速缓存需要更多的磁盘I/O

块提前读

  • 在对应块在被访问前预先调入高速缓存,以此增加高速缓存的块命中率。
  • 块提前读策略只适用于实际顺序读取的文件。对随机访问文件,提前读丝毫不起作用。
  • 许多文件都是顺序读的。如果请求文件系统在某个文件中生成块k,文件系统执行相关操作且在完成之后,会在用户不察觉的情形下检查高速缓存,以便确定块k+1是否已经在高速缓存如果还不在,文件系统会为块k+1安排一个预读,因为文件系统希望在需要用到该块时,它已经在高速缓存或者至少马上就要在高速缓存中了。
  • 可以通过跟踪每个打开文件的存取方式来确定是否需要块提前读

减少磁臂运动

  • 把有可能顺序存取的块放在一起,最好在同一个柱面上,从而减少磁臂移动的距离。(如果文件的数据块都在同一个柱面上,读完一块读下一块时,磁头根本不需要移动,减少寻道时间)

  • 将i节点(i-nodes)区放在磁盘的中间而不是开始处,这样i节点(i-nodes)与其对应文件数据块之间的平均距离减为原来的一半。

    • 老式布局(开始处): 如果 i-nodes 都在最外圈(Block 0)。读取 i-node(在最头) \(\rightarrow\) 读取数据(可能在最尾)。磁头需要横跨整个磁盘半径,距离最长。
    • 优化布局(中间处): 如果 i-nodes 在磁盘物理中间。无论数据是在最内圈还是最外圈,磁头从中间出发,最远只需要移动半径的一半
  • 将磁盘分成多个柱面组每个柱面组都有自己的i节点(i-nodes)区、数据块和空闲块表

  • 减少了i节点(i-nodes)与其对应文件数据块之间的平均距离,把大磁盘切分成很多个“小分区”(柱面组),把文件的内容(数据块)分配在和它的 i-node 同一个柱面组内。读取完 i-node 后,磁头只需要微调几步就能读到数据,不用长途奔袭。

  • 系统试图把同一个目录下的所有文件分配在同一个柱面组。当你浏览目录或批量操作文件时,磁头基本上就在这一小块区域内“转悠”,效率极高

  • 利用空间局部性,强制将元数据(i-node)与数据同一目录下的文件聚集在物理相邻区域,大幅降低了磁头在磁盘上的来回摆动幅度。
    o2PXWFHcLCuUjrn.png

posted @ 2026-01-10 17:01  NightRainLone  阅读(2)  评论(0)    收藏  举报