《现代操作系统》读书笔记——文件系统

发布于 10 天前  41 次阅读


文件系统

前言

长期存储信息有三个基本要求:

  • 能够存储大量信息。
  • 使用信息的进程终止时,信息仍旧存在。
  • 必须能使多个进程并发存取有关信息。

目前我们可以先把磁盘当作一种固定块大小的线性序列,并且支持如下两种操作:

  • 读块k;
  • 写块k。

存在的一些问题

  • 如何找到信息?
  • 如何防止一个用户读取另一个用户的数据?
  • 如何知道哪些块是空闲的?

就像我们看到的操作系统提取处理器的概念来建立进程的抽象,以及提取物理存储器的概念来建立进程(虚拟)地址空间的抽象那样,我们可以用一个新的抽象——文件来解决这个问题。进程(与线程)、地址空间和文件,这些抽象概念均是操作系统中最重要的概念。

文件是受操作系统管理的。有关文件的构造命名存取使用保护实现管理方法都是操作系统设计的主要内容。从总体上看,操作系统中处理文件的部分称为文件系统(file system)

文件

文件命名

在进程创建文件时,它给文件命名。在进程终止时,该文件仍旧存在,并且其他进程可以通过这个文件名对它进行访问。

文件的具体命名规则在各个系统中是不同的。

许多操作系统支持文件名用圆点隔开分为两部分,如文件名prog.c。圆点后面的部分称为文件扩展名(file extension),文件扩展名通常表示文件的一些信息,在某些系统中(如UNIX),文件扩展名只是一种约定。

文件结构

无结构的字节序列

把文件看成字节序列为操作系统提供了最大的灵活性。用户程序可以向文件中加入任何内容,并以任何方便的形式命名。操作系统不提供任何帮助,但也不会构成阻碍。

固定长度记录的序列

文件是具有固定长度记录的序列,每个记录都有其内部结构。把文件作为记录序列的中心思想是:读操作返回一个记录,而写操作重写或追加一个记录。

记录树

每个记录并不具有同样的长度,而记录的固定位置上有一个“键”字段。这棵树按“键”字段进行排序,从而可以对特定“键”进行快速查找。

文件类型

文件
  • 普通文件

    • ascii文件

    文本文件,可以阅读

    • 二进制文件

    二进制文件有一定的内部结构,使用该文件的程序才了解这种结构。

  • 字符文件

  • 块文件

目录

目录(directory)是管理文件系统结构的系统文件

文件访问

顺序访问

进程在这些系统中可从头顺序读取文件的全部字节或记录,但不能跳过某一些内容,也不能不按顺序读取。

随机访问

可以不按顺序地读取文件中的字节或记录,或者按照关键字而不是位置来存取记录。这种能够以任何次序读取其中字节或记录的文件称作随机存取文件(random access file)。

文件属性

文件都有文件名和数据。另外,所有的操作系统还会保存其他与文件相关的信息,如文件创建的日期和时间、文件大小等。这些附加信息称为文件属性(attribute),有些人称之为元数据(metadata)

目录

文件系统通常提供目录或文件夹用于记录文件,在很多系统中目录本身也是文件。

一级目录系统

目录系统的最简单形式是在一个目录中包含所有的文件。

层次目录系统

目录树。

路径名

  • 相对路径(工作目录)
  • 绝对路径

文件系统的实现

文件系统布局

文件系统存放在磁盘上。多数磁盘划分为一个或多个分区,每个分区中有一个独立的文件系统。磁盘的0号扇区称为主引导记录(MasterBootRecord,MBR),用来引导计算机。在MBR的结尾是分区表。该表给出了每个分区的起始和结束地址。表中的一个分区被标记为活动分区。在计算机被引导时,BIOS读入并执行MBR。MBR做的第一件事是确定活动分区,读入它的第一个块,称为引导块(bootblock),并执行之。引导块中的程序将装载该分区中的操作系统。为统一起见,每个分区都从一个启动块开始,即使它不含有一个可启动的操作系统。不过,在将来这个分区也许会有一个操作系统的。
除了从引导块开始之外,磁盘分区的布局是随着文件系统的不同而变化的。文件系统经常包含有如图4-9所列的一些项目。第一个是超级块(superblock),超级块包含文件系统的所有关键参数,在计算机启动时,或者在该文件系统首次使用时,把超级块读入内存。超级块中的典型信息包括:确定文件系统类型用的魔数、文件系统中数据块的数量以及其他重要的管理信息。

image-20200701213717435

接着是文件系统中空闲块的信息,例如,可以用位图或指针列表的形式给出。后面也许跟随的是一组i节点,这是一个数据结构数组,每个文件一个,i节点(inode)说明了文件的方方面面。接着可能是根目录,它存放文件系统目录树的根部。最后,磁盘的其他部分存放了其他所有的目录和文件。

文件系统的实现

文件存储的实现的关键问题是记录各个文件分别用到哪些磁盘块。

连续分配

最简单的分配方案是把每个文件作为一连串连续数据块存储在磁盘上。

优势
  • 实现简单,记录每个文件用到的磁盘块简化为只需记住两个数字即可:第一块的磁盘地址和文件的 块数。给定了第一块的编号,一个简单的加法就可以找到任何其他块的编号。
  • 读操作性能较好,因为在单个操作中就可以从磁盘上读出整个文 件。只需要一次寻找(对第一个块)。之后不再需要寻道和旋转延迟,所以,数据以磁盘全带宽的速率输入。可见连续分配实现简单且具有高的性能。
劣势
  • 随着时间的推移,磁盘会变得零碎。
链表分配

存储文件的第二种方法是为每个文件构造磁盘块链表,如图4-11所示。每个块的第一个字作为指向下一块的指针,块的其他部分存放数据。

image-20200701221353063

优势
  • 与连续分配方案不同,这一方法可以充分利用每个磁盘块。不会因为磁盘碎片(除了最后一块中的内部碎片)而浪费存储空间。同样,在目录 项中,只需要存放第一块的磁盘地址,文件的其他块就可以从这个首块地址查找到。
劣势
  • 尽管顺序读文件非常方便,但是随机存取却相当缓慢。要获得块n,操作系统每一次都必须从头开始,并且要 先读前面的n-1块。显然,进行如此多的读操作太慢了。
在内存中使用链表

如果取出每个磁盘块的指针字,把它放在内存的一个表中,就可以解决上述链表的两个不足。内存中的这样一个表格称为文件分配表(File Allocation Table,FAT)

优势
  • 按这类方式组织,整个块都可以存放数据。进而,随机存取也容易得 多。虽然仍要顺着链在文件中查找给定的偏移量,但是整个链表都存放 在内存中,所以不需要任何磁盘引用。与前面的方法相同,不管文件有多大,在目录项中只需记录一个整数(起始块号),按照它就可以找到文件的全部块。
劣势
  • 必须把整个表都存放在内存中。

i节点

最后一个记录各个文件分别包含哪些磁盘块的方法是给每个文件赋予一个称为i节点(index-node)的数据结构,其中列出了文件属性和文件块的磁盘地址。

image-20200701214530949

优势
  • 相对于在内存中采用表的方式而言,这种机制具有很大的优势,即只有在对应文件打开时,其i节点才在内存中。如果每个i 节点占有n个字节,最多k个文件同时打开,那么为了打开文件而保留i节点的数组所占据的全部内存仅仅是kn个字节。只需要提前保留少量的空间。
劣势
  • 如果每个i节点只能存储固定数量的磁盘地址,那么当一个文件所含的磁盘块的数目超出了i节点所能容纳的数目怎么办?一个解决方案是最后一个“磁盘地址”不指向数据块,而是指向一个包含磁盘块地址的块的地址(多级)

目录的实现

在读文件前,必须先打开文件。打开文件时,操作系统利用用户给出的路径名找到相应目录项。目录项中提供了查找文件磁盘块所需要的信息。因系统而异,这些信息有可能是整个文件的磁盘地址(对于连续分配方案)、第一个块的编号(对于两种链表分配方案)或者是i节点号。无论怎样,目录系统的主要功能是把ASCII文件名映射成定位文件数据所需的信息。

何处存放文件属性

  • 直接存放在目录项中

  • 把文件属性存放在i节点中而不是目录项中。在这种情形下,目录项会更短:只有文件名和i节点号

    image-20200701215043211

共享文件

硬连接
  • 直接指向磁盘地址
软连接(符号链接)
  • 只是记录目标文件路径

文件系统管理和优化

磁盘空间管理

存储n个字节的文件可以有两种策略:分配n个字节的连续磁盘空间,或者把文件分成很多个连续(或并不一定连续)的块。

正如我们已经见到的,按连续字节序列存储文件有一个明显问题,当文件扩大时,有可能需要在磁盘上移动文件。内存中分段也有同样的问题。不同的是,相对于把文件从磁盘的一个位置移动到另一个位置,内存中段的移动操作要快得多。因此,几乎所有的文件系统都把文件分割成固定大小的块来存储,各块之间不一定相邻。

块大小

一旦决定把文件按固定大小的块来存储,就会出现一个问题:块的大小应该是多少?按照磁盘组织方式,扇区、磁道和柱面显然都可以作为分配单位(虽然它们都与设备相关,这是一种负面因素)。在分页系统中,页面大小也是主要讨论的问题之一。

image-20200701215812458

记录空闲块
  • 链表
  • 位图

文件系统性能

高速缓存

最常用的减少磁盘访问次数技术是块高速缓存(block cache)或者缓冲区高速缓存(buffer cache)。在本书中,高速缓存指的是一系列的块,它们在逻辑上属于磁盘,但实际上基于性能的考虑被保存在内存中。

块提前读

第二个明显提高文件系统性能的技术是:在需要用到块之前,试图提前 将其写入高速缓存,从而提高命中率。特别地,许多文件都是顺序读的。如果请求文件系统在某个文件中生成块k,文件系统执行相关操作且在完成之后,会在用户不察觉的情形下检查高速缓存,以便确定块k+1是否已经在高速缓存。如果还不在,文件系统会为块k+1安排一个预读,因为文件系统希望在需要用到该块时,它已经在高速缓存或者至少马上就要在高速缓存中了。

减少盘臂运动

把有可能顺序存取的块放在一起,当然最好是在同一个柱面上,从而减少磁盘臂的移动次数。当写一个输出文件时,文件系统就必须按照要求一次一次地分配磁盘块。如果用位图来记录空闲块,并且整个位图在内存中,那么选择与前一块最近的空闲块是很容易的。如果用空闲表,并且链表的一部分存在磁盘上,要分配紧邻着的空闲块就困难得多。


面向ACG编程