美文网首页
第四部分 存储管理 文件系统

第四部分 存储管理 文件系统

作者: CocoAdapter | 来源:发表于2020-01-09 16:21 被阅读0次

第十章 文件系统

这部分主要是一些较高层的策略问题,主要是面向上层开发人员,而不涉及太多具体实现。

1. 文件操作

1.1 基本操作集合

文件为抽象数据类型。操作系统提供 6 个系统调用构成操作文件的最小集合,包括

  • 创建 create,首先在文件系统中为文件找到空间,其次,在目录中创建新文件的条目
  • 写入 write,指定文件名称和要写入文件的信息,系统根据文件名称搜索目录已查找文件位置。系统应维护写指针,用于指向需要进行下次写操作的文件位置。写的时候同时更新写指针。
  • 读取 read,指明文件名称和文件的内容应读到哪里。系统根据文件名称定位文件内容,同时需要维护一个读指针,指向要进行下一次读取操作的文件位置。同样,读操作需要同时更新读指针。

读写操作可以使用相同的指针。称为 当前文件位置指针。这个指针的初始化受文件访问模式影响,APPEND 会置于末尾,否则是文件头。

  • 重新定位 seek,移动当前文件位置指针到指定位置,不涉及实际 I/O 操作。
  • 删除 delete, 搜索给定文件名,找到对应的目录条目,释放占用的文件空间,并删除对应条目。
  • 截断 truncate 文件。用户可能想删除文件的内容,但保留它的属性。实际上就是只改动文件长度属性。

1.2 打开文件表

以上操作大多涉及搜索目录,得到文件的相关条目,为了避免这种搜索,OS 一般要求访问文件前需要先 open()。OS 有个打开文件表,维护所有打开文件的信息。如果不用了再 close()。create/delete 操作的是未 open() 的。
open() 函数返回一个文件句柄 (Windows) 或者称为文件描述符 (UNIX-like),后续操作都以这个值作为指针,避免重复搜索。同时,open() 一般也支持访问模式信息,如 CREATE, APPEND 等。OS 会根据文件系统的权限子系统检查 open() 是否合法。

文件系统的设计哲学显然是一种 “有状态” 通信。有状态和无状态的区别主要是是否单次操作需要携带所有必要信息。如果是无状态,那么每次都要指定文件名、访问模式、当前文件指针等等,NFS 就是这么设计的;有状态就得把状态维护起来,放在一个数据结构里,每次操作只需要携带一个指针以让系统知道操作的是哪个项。这两种设计哲学广泛分布于计算机领域,如 TCP 和 UDP,TCP 有状态,每条链接的状态信息由 OS 维护,而 UDP 每次读写数据互不干扰,OS 并没有 UDP 的状态信息(可能需要维护其它信息,如端口信息)

通常,OS 采用两级内部表:单独进程表系统总表

  • 单独进程表跟踪进程打开的所有文件,存储了当前文件位置指针、权限、访问模式等信息。
  • 系统表包含与进程无关的信息,如文件在磁盘上的位置,mtime/ctime/atime,文件大小等。一旦进程打开了一个文件就会产生一个条目,如后续再有进程 open(),只需要在打开计数上加 1 即可。每次 close() 会减 1,OS 会回收计数为 0 的条目。所以 open() 后的文件不用了一定要 close()。

1.3 同步

OS 可以提供强制 (Windows) 或建议(UNIX-like) 的文件锁定机制。强制锁会要求文件操作的所有 API 都检查锁,也即一个进程加了锁,OS 会确保其它进程调用 open() 这类函数的时候会被阻塞。这是一个内核级别的实现。而建议锁是指,一个进程加了锁,其它进程需要主动去检查这个锁的状态,再决定是否操作文件。Linux 文件锁 的实现可以参考这篇文章。
Java 实现文件互斥访问时通过要访问文件的 FileChannel 的 lock() 取得 FileLock 来做得。FileLock 支持共享和非共享,同时支持字节级别的锁定。

public abstract FileLock lock(long position, long size, boolean shared)
        throws IOException;

2. 文件类型与结构

实现文件类型的常见技术是将类型作为文件名的一部分,即放在扩展名里。UNIX 系统采用位于某些文件开始部分的魔数(magic number)大致表明文件类型,但不是所有文件都有魔数。Linux 的可执行文件是依赖于其执行权限而不是拓展名。所以综上,文件类型的区分方法千奇百怪。
文件类型决定文件结构。OS 必须支持至少一种结构,即可执行文件的结构,以便系统能加载和运行程序。但是,OS 一般也不应该规定太多文件结构,谁支持谁就得负责,这个让 OS 来做 OS 就很亏。

Linux 大致有这些文件类型:普通文件、目录、字符设备、块设备、套接字、符号链接

3. 访问方法

  • 顺序访问
    文件信息按顺序加以处理,编辑器和编译器通常以这种方式访问文件。适合磁带
  • 直接访问
    文件由固定长度的逻辑记录组成,以允许程序按任意顺序进行随机访问。数据库通常是这种类型的。直接访问可以由顺序访问进行模拟,但是效率低下。适合磁盘等。
  • 其它访问方法
    其它访问方法通常建立在直接访问之上,并涉及建立文件索引。现代通用 OS 都是这种。

4. 目录与磁盘的结构

文件系统底层是磁盘,向上提供前面提到的 API,那么必须维护目录来组织这些文件。

4.1 一些概念

  1. 磁盘 磁盘是一块单独的物理设备
  2. 分区 磁盘格式化后才能使用,格式化实际上就是对磁盘进行分区,分为不同的部分,每个部分有些元数据表明物理存储的一些信息,也就是文件系统的元数据。按传统分区技术,分区一般分为主分区和扩展分区;分区表格式有主引导分区(Master Boot Record, MBR,已逐渐淘汰) 和 GPT(GUID Partition Table) 分区。比如 Windows 的 C 盘、D 盘就是分完区后的结果。详见鸟哥的 Linux 私房菜磁盘部分
  3. 文件系统 文件系统和分区不是一个概念,分区一般说的是物理存储设备,文件系统是描述存储设备的内容格式。
  4. LVM Logical Volume Manager Linux 下的逻辑卷管理器,向上屏蔽掉底层物理磁盘的细节。实际上就是让 OS 能操作多块磁盘以及支持扩容、缩容等操作。传统分区有很多限制。
LVM

PV(Physical Volume):物理卷,处于LVM最底层,可以是物理硬盘或者分区。
PE(Physical Extend):物理区域,PV中可以用于分配的最小存储单元,可以在创建PV的时候制定(默认为4MB),如1M, 2M, 4M, 8M, 32M, 64M…组成同一VG中所有PV的PE大小应该相同。
VG(Volume Group):卷组,建立在PV之上,可以含有一个到多个PV。
LV(Logical Volume):逻辑卷,建立在VG之上,相当于原来分区的概念。不过大小可以动态改变。

  1. VFS Linux 支持多种文件系统,为了屏蔽底层细节,VFS 扮演了中间层的角色

4.2 目录

目录(directory)可以理解为一个字典,记录的是文件名到磁盘存储位置的映射。目录可以分为单级目录、两级目录、树形目录(主流目录结构)、无环图目录(为了共享子目录)。

UNIX-Like 既支持基于软链接的共享,这个时候文件系统并不把软连解当作实际节点,删了之后也依然保留,需要程序员自己意识到被删了;也支持基于硬链接的共享,后者修改了 inode 的内容,会由文件系统管理节点。详见相关博客。

4.3 文件系统挂载

文件系统在使用前得先挂在到某个目录,挂载位置称为挂载点,被挂载的称为设备。比如, Linux 中,/home 一般是个单独的分区,内核会在启动的时候将其挂载到根目录下的 /home 处。然后我们才可以通过 /home/test.c 直接访问 test.c 文件。

Linux 默认只有一个分区,即 /,这个分区上运行着发行版默认的文件系统。这个 / 里面可以装文件,也可以挂载其它文件系统。这里首先得认识到 OS 目录树和文件系统不是一个概念。你可以在 /test1 挂载一个 ext3 文件系统,可以在 /test2 挂载一个 xfs 文件系统,可以在 /test3 放一个普通文件。挂载可以看作根目录的某个节点被另外一颗树替代。Windows 会在启动时自动安装所有文件系统(卷 C, D...),如果我们插入 U 盘,Windows 会识别并把 U 盘挂载到目录树上,只不过这个目录树是两级的。Linux 的挂载点一般可以是任何位置,Windows 的新版本似乎也可以,但不清楚。

4.4 保护

UNIX-like 文件系统的一般权限 和 SUID, SGID, SBIT等

第十一章 文件系统实现

1. 文件系统的结构

文件系统一般分层实现,从上往下:

  • 逻辑文件系统。管理元数据信息,包括文件系统的所有结构,但不包括实际数据。通过文件控制块(File Control Block, FCB) 包含文件的信息,如权限、位置等
  • 文件组织模块。负责映射文件的逻辑块到物理块地址。每个文件的逻辑块是连续编号的,但是物理块不是。
  • 基本文件系统。负责向设备驱动程序发送命令,读写物理块,维护缓存等。
  • I/O 控制。负责底层硬件通信

内存和磁盘之间的 I/O 以块 (block) 为单位,块的大小与磁盘扇区大小无关。同时,上述模块在实际实现中可能揉在一起。

2. 文件系统的实现

2.1 元数据

首先考虑一下文件系统需要维护哪些信息。分别从磁盘上和内存中来考虑(下述不区分卷和分区的概念,因为卷实际上就是个逻辑分区):

2.1.1 磁盘上
  • 每个分区的引导控制块 (boot control block)。如果分区不包含 OS,则这块为空,也即一个 OS 有多个分区。它通常为卷的第一块,有自己的格式。因为在引导式系统没有加载文件系统代码,不能解释文件系统的格式。每个分区都可以有引导控制块,这是多重引导,即多系统的基础。个人感觉这部分不属于文件系统的一部分

BIOS/UEFI 是启动检测程序。他们运行后,会去读 MBR/GPT 分区,然后 MBR/GPT 分区可以直接加载内核文件来启动 OS,也可以将控制流转到其它引导控制块。

  • 分区控制块。存储了分区内的详细信息,如分区内的块的数量、大小、空闲块和指针、空闲的 FCB 数量和指针等。也即 UNIX-like 中的超级块 (superblock)
  • 目录结构。在 Linux 中,包括文件名和 inode 号码。
  • 每个文件的 FCB 包括该文件的许多详细信息。也即 Linux 中的 inode。
2.1.2 内存中
  • 挂载表。包含设备的挂载信息,条目包括一个指向该设备的文件系统超级块的指针。
  • 目录结构缓存。
  • 系统打开文件表。
  • 进程打开文件表。
  • 缓冲区。保存文件系统的块。

2.2 操作函数

2.2.1 创建文件/文件夹

创建文件时,应用程序调用逻辑文件系统,由其分配一个新的 FCB。如果文件系统的实现在文件系统创建时(格式化的时候)已经创建了所有 FCB,如 ext 文件系统,则可从空闲的 FCB 集合中分配一个可用的 FCB。然后逻辑文件系统用文件名和新的 FCB 去更新其对应的目录文件,并写回磁盘,这个过程会调用下面的文件组织模块。
UNIX 不区分文件和文件目录的处理,而 Windows 提供两套系统调用。

2.2.2 打开文件

文件使用前需要先打开。open() 首先需要搜索系统打开文件表查询是否已经打开,如是,直接在进程打开文件表中新建条目并指向系统打开表中的条目,同时更新计数;否则,需要搜索文件所在目录,这个通常缓存在内存中,找到文件后,将其 FCB 复制到系统打开文件表,并在进程打开文件表中重复前面相同的动作。
进程打开文件表中的条目一般包含一个指针指向系统表,所有文件操作通过这个指针进行,所以文件表不需要包含文件名,因为可以通过指针访问 FCB。这个在 UNIX 中称为 文件描述符(file descriptor),在 Windows 中称为 文件句柄 (file handle)。

2.2.3 读写文件

读文件需要指定文件描述符,然后内核访问进程的打开文件表,获取系统打开文件表的索引,再根据系统文件表中存储的 FCB(可能在内存中也可能是一个指向磁盘的指针),然后获取文件的元数据,包括数据存储位置、当前位置指针等。然后向下发送读取指令,最后由 I/O 设备完成读取。读到的内容放入指定的缓冲中。
写文件类似,只不过一般都是写缓冲,落盘的策略一般由文件系统自行安排或者应用程序手动调用 fsync。

2.2.4 关闭文件

关闭文件需要删除进程打开文件表中的条目,并递减系统打开文件表中的计数。当所有打开该文件的用户关闭它时,任何更新的元数据落盘,并且删除系统表中的条目。

2.3 分区与挂载

分区并不一定得有文件系统,没有格式化的分区也可以使用,称为裸盘,原始磁盘 (raw disk)。例如,UNIX 的交换空间就是使用的裸盘,数据库也经常使用。
根分区,包括 OS 内核和其它系统文件在启动时挂载。其它卷可以在引导时自动挂载或在启动后手动挂载,取决于 OS。
挂载的实现一般时标记某个 FCB 为挂载点,同时在其里写入安装表中条目的指针,即完成挂载。

2.4 虚拟文件系统

VFS 实际上就是 OOP 技术的一种体现,通过中间层来屏蔽底层多种实现。

3. 目录实现

  • 线性表。主要是复杂度偏高
  • 哈希表。主要是扩容,可能需要借助一致性哈希算法
  • 平衡树。

4. 分配方法

4.1 连续分配

连续分配的好处是,简单,支持顺序访问和直接访问,对于磁盘结构,读取效率高。缺点是,本质上是一个动态存储分配问题,也就存在这类型问题的所有问题,如外部碎片,如何快速找到一个最好的可用分配,文件增大怎么做。
外部碎片可以通过复制-整理算法来做,这个技术在垃圾回收算法中常用。存在 STW 问题,而且需要额外存储空间来做整理。文件增大一般通过扩展来解决,即一开始先分配一块,如果增大,再分配一块,第一块链接到后一块上。

4.2 链接分配

链接分配解决了上述所有问题,但是不能有效支持文件的随机访问。顺序访问的情况下,与索引分配一样,对于磁盘结构,可能效率低下,因为要不停旋转磁盘头。同时,因为链接指针需要空间,所以有效存储率变低了。
链接分配的一个重要变种就是文件分配表(File Allocation Table, FAT),广泛用于 MS-DOS。每个卷的开头部分的磁盘存储一张表,表里存储了卷上的所有块的指针,从而解决随机访问的问题。

4.3 索引分配

索引分配将所有指针放在一起形成索引块,解决随机访问的问题。每个 FCB 里存储了各自的索引块。
UNIX 文件系统使用一种组合策略来优化索引的存储,inode 的前12 个指针是直接索引,直接指向文件的数据块的地址;接下来的 3 个指针指向间接块,分别是一级间接块、二级间接块、三级间接块,对应二级、三级、四级索引。

5. 空闲空间管理

为了跟踪空闲磁盘空间,需要维护一个空闲空间列表。创建和删除文件块都需要修改这个空闲空间列表。一般通过位图来实现,例如 UNIX;也可以通过链表等。

后面还包括对效率、性能、恢复、一致性、NFS 的讨论,这里不再总结。

相关文章

  • 第四部分 存储管理 文件系统

    第十章 文件系统 这部分主要是一些较高层的策略问题,主要是面向上层开发人员,而不涉及太多具体实现。 1. 文件操作...

  • Linux磁盘与文件管理系统

    文件系统简介 什么是文件系统? 操作系统中负责管理和存储文件信息的软件称为文件管理系统,即文件系统。 主要职能: ...

  • 大数据工具Hadoop快速入门4-HDFS读写教程

    什么是HDFS? 管理跨网络的存储特定操作的文件系统称为分布式文件系统。 HDFS是一种分布式文件系统,用于存储在...

  • NoSQL-HDFS-基本概念

    Hadoop Map-Reduce HDFS 正文 分布式文件系统 文件系统:文件系统是用来存储和管理文件,并且提...

  • i-node节点与链接

    文件系统 文件系统存储文件属性、文件内容和目录。这些内容是怎样在文件系统中存储的呢?Linux将磁盘块分成了3部分...

  • Unix文件系统傻傻搞不懂?这里文章虽长,但是你细品就够了

    Unix文件系统是一种以易于管理的方式组织和存储大量信息的逻辑方法。 文件是存储信息的最小单位。 Unix文件系统...

  • docker.管理应用程序数据

    第四节.docker管理应用程序数据 voluems:docker管理数组机文件系统的一部分(var/lib/do...

  • 八、磁盘分区和挂载

    存储管理 一 一 .存储管理 主要知识点: 基本分区、逻辑卷LVM、EXT3/4/XFS文件系统、RAID 1....

  • ASP.NET Core WebApi+EF Core轻量级文件

    一、课程介绍 1、什么是文件系统? 操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。文件...

  • 文件管理

    一、文件和文件系统 现代OS几乎都是通过文件系统来组织和管理在计算机中所存储的大量程序和数据的。文件系统的管理功能...

网友评论

      本文标题:第四部分 存储管理 文件系统

      本文链接:https://www.haomeiwen.com/subject/rtmooctx.html