4.3 文件系统的实现
4.3.1 文件系统布局
磁盘的0号扇区称为主引导记录(Master Boot Record,MBR),用来引导计算机。
MBR之后是分区表,该表记录的每个分区的起始和结束地址。表中的一个分区被标记为活动分区。在计算机被引导的时候,BIOS读入并执行MBR。
MBR做的第一件事是确定活动分区,读入它第一个块,称为引导块(boot block),并执行之。
引导块中程序装在该分区中的操作系统,为了统一起见,每个分区都从一个启动块开始,即使它不还有一个可启动的操作系统。
超级块,包含文件系统的所有关键参数,在计算机启动时,或者该文件系统首次使用时,把超级块读入内存。典型信息包括:确定文件系统用的魔数、文件系统中数据块的数量及其他重要的管理信息。
空闲空间管理,比如用位图或指针列表
一组i节点,是一个数据结构数组,每个文件一个,i节点说明了文件的各个方面
根目录,存在文件系统目录的根部
其他所有文件和目录
4.3.2 文件的实现
记录各个文件分别用哪些磁盘快?
- 连续分配
每个文件都从一个新的块开始。
优点是实现简单:每个文件只需要一个起始地址和长度即可。读性能好,因为单操作就可以从磁盘上读取整个文件。只需要一次寻找一个块。
缺点是磁盘会变的零碎,当磁盘用完了就需要填充空穴比较麻烦。
但也不是毫无用处,比如cd和dvd就是用这样方式。 - 链表分配
每个块的第一个字节作为指向下一块的指针,块的其他部分存放数据。
优点:可以充分利用每个磁盘块。
缺点:随机存取却非常缓慢,因为必须读前面的n块 - 在内存中采用表的链表分配
如果取出每个磁盘块的指针字,把它放入内存的一个表中就可以解决上述两个问题。这个表称为文件分配表(File Allocation Table,FAT)
缺点是如果硬盘很大,会占用很多内存。 - i节点
记录各个文件分别包含哪些磁盘块的方法是给每个文件赋予一个称为i节点(index-node)的数据结构。其中列出了文件属性和文件块的磁盘地址。
打开文件而保留i节点的数字所占用的内存要小很多。也就是所不是讲整个硬盘每个块的信息存入内存,而是将正在使用的文件的块的信息放入内存。
i节点的一个问题是如果i节点只能储存固定数量的磁盘地址,那么当一个文件所含的磁盘块数目超出了i节点所能容纳的数目。通常只有将最后一个磁盘地址不指向数据块,而是指向一个包含磁盘块地址的地址。
4.3.3 目录的实现
操作系统利用用户给出的路径名找到相应目录项。目录项中提供了查找文件磁盘块所需要的信息。
因系统而异,这些信息有可能是整个文件的磁盘地址,第一个块的编号或者i节点。
目录系统的主要功能时把ASCII文件名映射成定位文件数据所需的信息。
与此密切相关的问题是在何处存放文件属性。
1. 把文件属性直接存放在目录项中。很多系统确实是这样做的。目录中有一个固定大小的目录项列表。每个文件对应一项,其中包含一个固定长度文件名、一个文件属性结构以及用于说明磁盘块位置的一个或多个地址。
2. 当然文件属性也可以放在i节点中。这样的情况下目录项会更短。只有文件名和节点名。
还有一个可能是如何实现现代操作系统都支持可变长度的长文件名。
- 给每个目录项一个固定的最长长度限制
- 采用变长的目录项,会产生空穴和碎片
- 目录项自身采用固定长度,但是文件名放在目录后面的堆中。这样即使文件移走,另一个文件的目录总是可以适合这个空隙的。
加快查找速度的方式
- 顺序查找
- 每个目录中使用散列表。同名文件使用链表。对于查找文件也适用。
4.3.4 共享文件
当几个用户同在一个项目里工作,他们常常需要共享文件。B的目录与该共享文件的联系称为一个连接。
- 磁盘块不列入目录,而是列入一个与文件本身关联的小型数据结构中(i节点)。目录指向这个消息数据结构,Unix的方案。(硬链接)
缺点:有所有者概念,如果a目录创建文件,b目录添加连接,然后a删除文件,但是文件的所有者仍然是a,计数从1-2-1。 - 让系统建立一个类型为Link的新文件。并不该文件放在b目录下,使得b和c的文件存在连接。(符号链接)
缺点:
需要额外的开销,必须读取包含路径的文件,然后要一步一步的扫描知道找到节点i。
优点:
只要听一个机器的网络地址以及文件在该机器上驻留的路径,就可以连接全局机器上的文件。
连接的另一个问题是在如果复制或者移动的时候,连接可能会多次复制一个文件。需要转储程序有相当的智能。
4.3.5 日志结构文件系统
日志结构文件系统(Log-structured File System)LFS
设计这样系统的主要理由是,CPU运行速度越来越快、RAM内存容量变大,高速缓存变大,进而不需要磁盘访问操作,就有可能满足直接来自文件系统高速缓存的很大一部读请求。因此可以推出多数的磁盘访问是写操作。这样的系统对于数量众多的零碎写操作可能更有效。
基本思想是将整个磁盘结构化为一个日志。每隔一段时间,或是有特殊需求时,被缓存在内存中的所有未决的写操作都是被放到一个单独的段中,作为在日志末尾的一个邻接段写入磁盘。一个单独的段可能会包括i节点、目录块、数据块或者都有。每一个段的开始都是该段的摘要,说明该段中都包含哪些内容。如果所有的段平均在1mb左右,那么几乎可以利用磁盘的完整带宽。
总之,所有的写操作最初都被缓冲在内存中,然后周期性地把所有已缓存的写作为一个单独段,在日志的末尾处写入磁盘。
为了解决日志占用满硬盘需要一个清理线程,周期地扫描日志进行磁盘压缩。
4.3.6 日志文件系统
NTFS和ext3
日志文件系统先写一个日志项,列出需要完成的都打给你做,然后日志写入磁盘,完成后根据日志,然后验证完整性。
写入日志的操作必须是幂等的,另外需要引入原子事务概念。
4.3.7 虚拟文件系统
绝大多数UNIX系统都是用虚拟文件系统(Virtual File System,VFS)概念将多种文件系统统一成一个有序的框架。
所有文件相关的系统调用在处处的处理上都指向虚拟文件系统。来自用户进程的调用,都是标准的POSIX调用。因此虚拟文件系统对用户进程有一个更高层接口就是POSIX接口。
VFS也有一个对于实际文件系统的更底层的接口,这个接口包含许多功能调用,这样vfs可以使每个系统完成任务。因此当创造一个新的文件系统和vfs一起工作的时候,文件系统的设计者需要确定它提供的vfs所需要的功能调用。
VFS最初的动机是为了支持NFS(Network File System)。