一、基本概念
- 属于操作系统的一个子系统
- 完成的功能、如何实现读写与磁盘打交道、多磁盘管理
1、什么是文件系统与文件?
-
一种用于持久性存储的系统抽象【把数据更加方便的存储到我们的硬盘上】
-
文件系统中一个单元的相关数据在操作系统中的抽象
2、文件系统的基本功能是什么?
(1)磁盘存储层面
- 管理文件块(哪一块属于哪一个文件)
- 管理空闲空间(哪一块是空闲的)
- 空间分配算法
(2)用户层面
- 定位文件与内容
- 通过文件名找到文件的接口
- 最常见的形式为分层文件系统
- 针对不同的操作系统可能使用的文件系统类型不同
(3)系统特性层面
- 采用分层形式的文件系统,可以用来保护数据安全【不希望被其他进程访问】
- 可以保持文件的持久,就是发生崩溃、攻击错误等也能恢复【属于文件系统的可靠性与持久性】
(3)文件层面
- 文件应该具有 名称、类型、位置、大小、保护、创建者、创建时间、最近修改时间等文件属性
- 文件头应该包含以下信息
- 在存储元数据中保存了每个文件的信息
- 保存文件的属性
- 跟踪哪一块存储块属于逻辑上文件结构的哪个偏移
3、什么是文件描述符?
- 内核(kernel)利用文件描述符(file descriptor)来访问文件。
- 文件描述符是非负整数。【属于打开文件表的一个索引】
- 打开现存文件或新建文件时,内核会返回一个文件描述符。
- 读写文件也需要使用文件描述符来指定待读写的文件。
4、需要哪些数据来管理文件?【使用元数据管理打开文件】
-
文件指针:指向最近一次读写位置【位置指针】
-
文件打开计数:记录文件打开的次数,当最后一个进程关闭了文件时,允许将其从打开文件表中移除
-
文件磁盘位置:缓存数据访问信息
-
访问权限:每个程序访问模式信息
-
在各个层面,文件系统是如何体现的?
- 访问磁盘的基本单位是扇区
- 案例分析:简单读写几个字节
(1)用户要读取2-12字节空间,操作系统是如何处理的呢?
第一步,先获取字节所在的块,返回块内对应的部分【因为访问磁盘的基本单位是扇区,所以我们先去寻找数据所在块】
(2)用户如何要写2-23字节空间,操作系统又是如何处理的呢?
第一步还是获取块,第二步修改块内对应部分,第三步写回块【为什么要写回块呢?因为修改数据的这一过程是在内存中完成的】
(3)文件系统中的所有操作都是在整个块空间上进行的
5、用户如何访问文件?【访问文件的模式】
-
顺序访问:按字节依次读取【几乎所有的访问都是这种方式】
-
随机访问:从中间读写【整个过程是跳跃的,具体位置不固定】
(1)这种方式不常用,但是仍然具有重要意义【虚拟内存支持文件、内存页存储在文件中】
(2)如何加快随机访问的速度呢?
不需要获取文件中间的内容时,它也会先获取块内所有字节【最小操作单位为块,一次性加载完整个块】
-
基于内容访问:通过特征访问的一种形式【很多系统都没有,类似于数据库,通过指定字段可以在数据库访问需要的信息】
-
案例分析:一个是索引文件,一个是关系文件,通过存储的索引去寻找对应的关系
6、文件的内部结构是怎样的呢?
-
无结构:单词、比特的队列
-
简单记录结构:列、固定长度、可变长度
-
复杂结构:格式化的文档(Word、PDF等)、可执行文件
-
应用程序自动识别文件的格式,操作系统好应用程序的功能是分开的
7、多用户操作系统的文件访问控制:
-
访问控制
- 谁能够获得哪些文件的哪些访问权限
- 访问操作:读、写、执行、删除、列举等
-
文件访问控制列表(ACL)
- <文件实体,权限>
-
Unix 模式下,不同层级的文件权限
- <用户 | 组 | 所有人, 读 | 写 | 可执行> 【与Linux类似,Linux第三个字段为其他人】
- 通过用户ID识别用户,表明每个用户所允许的权限及保护模式
- 组ID允许用户组成组,并指定了组访问权限【对于这个组内的用户的组权限是相同的】
8、多用户或多个客户如何同时访问共享文件?
- 与过程同步算法相似
- 因磁盘 I/O 和网络延迟而设计简单
- 以Unix文件系统为例【UFS的语义】
- 对打开文件的写入内容立即对其他打开同一文件的其他用户可见
- 共享文件指针允许多用户同时读取和写入文件
- 会话语义
- 写入内容只有当文件关闭时可见
- 锁
- 一些操作系统和文件系统提供该功能
9、为什么需要目录?
- 当文件量特别大的时候,采用多层分级目录更方便文件管理
- 目录是一类特殊的文件:包含了一张表<name, pointer to file header>
- 早期的文件系统是扁平的,现在的目录和文件都是采用树型结构
10、目录都包含哪些操作?
-
搜索文件、创建文件、重命名文件、删除文件、枚举目录、在文件系统中遍历一个路径
-
操作系统应该只允许内核模式修改目录
- 确保映射的完整性
- 应用程序能够读目录
-
应用程序发出请求,实际由操作系统来完成
11、如何存储目录中的文件?
- 集合 >> 不同文件名 【采用一种集合的形式存储,用不同文件名来区分】
- 如果用简单的线性列表实现
- 只需要包含指向数据块的指针
- 编程简单
- 但执行比较耗费时间
- 采用hash表的线性表【哈希结构,key-value的映射形式】
- 减少目录的搜索时间
- 但是容易产生哈希冲突,需要添加额外的冲突处理【两个文件名的hash值相同】
- 文件大小固定
12、如何遍历路径的呢?
-
名字解析:逻辑名字转换成物理资源的过程【就是根据文件名去找到这个文件】
- 在文件系统中,找到实际文件的文件路径
- 遍历文件目录直到找到对应的目标文件
-
案例分析:比如要找到
/bin/ls
,给出的是完整路径及流程【root就是我们常说的根目录】- 读取root的文件头【在磁盘固定的位置】
- 读取root的数据块,搜索 bin 项
- 读取bin的文件头
- 读取bin的数据块,搜索 ls 项
- 读取ls的文件头
-
当前工作目录
- 每个进程都会指向一个文件目录用于解析文件名
- 允许用户指定相对路径来代替绝对路径
- 意思就是从当前进程的位置出发,去寻找我们的目标文件
-
路径遍历是文件系统中开销较大的部分
13、文件系统是如何完成挂载的?
- 一个文件系统需要先挂载才能被访问
- 一个未挂载的文件系统被挂载在挂载点上,如果不想用了还能卸载
- 我们通常说文件系统挂载到某个目录上,针对这个文件系统,它的挂载点【这个目录】就是这个文件系统的根目录
14、什么是文件别名?
- 一个文件有多个名字【多用户系统,有些用户给它命名不一样】
- 硬链接:多个文件项指向一个文件
- 软链接:以快捷方式指向其他文件
- 通过存储真实文件的逻辑名来实现
15、如果删除一个有别名的文件会如何呢?
- 这要取决于这个别名的实现方式:
- 如果采用的是软链接,别名会变成一个悬空指针,指针找到存在的文件
- 如果采用的是硬链接,只会导致文件的别名计数减一,只有当别名计数为零时,该文件才会被真的删除
- Backpointers 方案:
- 每个文件有一个包含多个backpointers的列表,所以删除所有的backpointers
- Backpointers使用菊花链管理
- 也可以添加一个间接层:目录项数据结构
- 链接:已存在文件的另一个名字【就是在这个中间层有个指针指向文件的实际存储位子】
- 链接处理:跟随指针来定位文件
16、如何保证路径不会出现死循环?
-
只允许到文件的链接,不允许到子目录的链接
-
每增加一个新的链接都可以用循环检测算法确定是否合理
-
也可以限制路径可遍历文件目录的数量,来防止系统陷入死循环
17、文件系统有哪些?
分布式文件系统
跨网络 >> 延迟大、访问时间不可控
18、分布式文件系统的补充:
- 文件可以通过网络被共享
- 文件位于远程服务器
- 客户端远程挂载服务器文件系统
- 标准系统文件访问被转换成远程访问
- 标准文件共享协议:NFS for Unix, CIFS for Windows
- 分布式文件系统可能存在的问题:
- 客户端和客户端上的用户辨别起来很复杂
- 例如,NFS是不安全的
- 一致性问题
- 错误处理模式
二、虚拟文件系统
1、什么是虚拟文件系统?
- 虚拟文件系统(VFS)是物理文件系统与服务之间的一个接口层,它对Linux的每个文件系统的所有细节进行抽象,使得不同的文件系统在Linux核心以及系统中运行的其他进程看来,都是相同的。
- 并非是实际的文件系统,它只存在于内存中,不存在任何外存空间
- VFS 在系统启动时建立,在系统关闭时消亡
2、虚拟文件系统的功能和目的?
- 目的:对所有不同文件系统的抽象
- 功能
- 提供相同的文件和文件系统接口
- 管理所有文件和文件系统关联的数据结构
- 高效查询例程,遍历文件系统
- 与特定文件系统模块的交互
3、虚拟文件系统由哪些部分组成的呢?
- 卷控制块(在Unix中为"superblock")
- 每个文件系统一个
- 文件系统详细信息
- 块、块大小、空余块、计数或指针等
- 文件控制块(在Unix:vnode or inode)
- 每个文件一个
- 文件详细信息
- 许可、拥有者、大小、数据库位置等
- 目录节点(Linux:dentry)
- 每个目录项一个(目录和文件)
- 将目录项数据结构及树型布局编码成树型数据结构
- 指向文件控制块、父节点、项目列表等
- 形成的图示如下:
4、虚拟文件系统怎么保存数据?
- 文件系统数据结构
- 卷控制块(每个文件系统一个)
- 文件控制块(每个文件一个)
- 目录节点(每个目录项一个)
- 持续存储在二级存储中
- 在分配在存储设备中的数据块中
- 当需要时加载进内存
- 卷控制模块:当文件系统挂载时进入内存
- 文件控制块:当文件被访问时进入每次
- 目录节点:在遍历一个文件路径时进入内存
三、数据缓存
-
将经常访问的和当前正在访问的加载到内存中
-
数据块按需读入内存
- 提供read()操作
- 预读:预选读取后面的数据块
-
数据块使用后被缓存
- 假设数据将会再次被使用
- 写操作可能被缓存和延迟写入
-
数据块缓存的两种方式
- 普通缓冲区缓存
- 页缓存:统一缓存数据块和内存页
- 分页要求:当需要一个页时才将其载入内存
- 支持存储:一个页(在虚拟地址空间中)可以被映射到一个本地文件中(在二级存储中)
- 文件数据块的页缓存
- 在虚拟内存中文件数据块被映射成页
- 文件的读/写操作被转换成对内存的访问
- 可能导致缺页和/或设置为脏页
- 问题:页置换——从进程或文件页缓存中?
四、打开文件的数据结构
- 打开文件的流程:找到文件 >> 打开文件【文件控制块的内容读取到内存中】
- 打开文件描述
- 每个被打开的文件一个
- 文件状态信息
- 目录项、当前文件指针、文件操作设置等
- 打开文件表
- 一个进程一个
- 一个系统级的
- 每个卷控制块也会保存一个列表
- 所以如果有文件被打开将不能被卸载
- 一些操作系统和文件系统提供该功能
- 调节对文件的访问
- 强制和劝告:
- 强制:根据锁保持情况和需求拒绝访问
- 劝告:进程可以查找锁的状态来决定怎么做
五、文件分配
- 背景:
- 大多数文件都很小
- 需要对小文件提供强力的支持
- 块空间不能太大
- 一些文件非常大
- 必须支持大文件(64-bit 文件偏移)
- 大文件访问需要相当高效
1、如何为一个文件分配数据块?
- 有三种分配方式:连续分配、链式分配、索引分配
- 评价分配方式好坏的指标:
- 高效:如何存储利用(外部碎片)
- 表现:访问速度如何
2、连续分配
3、链式分配
4、索引分配
- 如何处理大文件?
在早期的Unix系统中就采用了多级索引块方式,UFS多级索引分配:
六、空闲空间列表
1、如何管理这个空闲的空间列表?
- 可以采用位图代表空闲数据块列表:
- 1110111110101010101111
- 这个列表bit[i] 为零代表数据块i处于空闲状态,为一代表已经分配了
- 这个方式缺失简单,但是可能需要一个较大的容量
- 如果为160G的磁盘 -> 需要40M个数据块 > 需要5M的空闲列表【1bit表示八块】
- 如果空闲空间在磁盘中均匀分布,因为需要顺序访问,那么在找到 0 之前需要扫描 n / r,扫描速度取决于 n / r
- n 磁盘上数据块的总数
- r 空闲块的数目
2、有时候可能断电了,导致分配空间未使用但显示使用了的问题,那么如何保护数据存储的过程呢?
- 需要指向空闲列表的指针
- 位图
- 必须保存在磁盘上
- 在内存和磁盘拷贝可能有所不同
- 不允许block[i] 在内存中的状态为bit[i] = 1,而在磁盘中bit[i] = 0【内存显示使用了,但是磁盘中没用】
- 如何防止出现上述的问题呢?
- 在磁盘上设置 bit[i] = 1
- 分配block[i]
- 在内存中设置bit[i] = 1
- 这三个是按照从先到后的顺序来的,先去磁盘中存储
3、也可以采用其他数据结构表示空闲空间列表:
七、多磁盘管理 RAID
1、磁盘是如何寻道的呢?
- 一个分区是一个柱面的集合【下图的一层为一个柱面】
- 每个分区都是逻辑上独立的磁盘
- 磁盘就是通过分区来最大限度减少寻道时间的
2、磁盘的分区是怎么回事?
- 一个磁盘可以有多个分区,这些分区可以采用不同的文件系统
- 分区又可以定义为 硬件磁盘的一种适合操作系统指定格式的划分
- 对于使用相同文件系统的分区,我们可以用卷来统一管理起来
- 卷定义为 一个拥有一个文件系统实例的可访问的存储空间【通常在磁盘的单个分区上】
图中partition C 就是我们所说的第三点,那么我们思考两个问题?
(1)如果disk2、disk3并行执行,能够提高数据搜索速度?
(2)如果disk2、disk3存储相同的内容,是否可以提高数据的可靠性?
(3)引出我们的多磁盘管理机制
3、如何提高磁盘的可靠性以及速度?
-
使用多个并行磁盘来增加:
- 通过并行可以提高吞吐量
- 通过冗余可以提高可靠性和可用性
-
RAID:又称为冗余磁盘阵列
- 各种磁盘管理技术
- RAID level:不同的RAID分类【RAID-0、RAID-1、RAID-5】【不同磁盘阵列的组织形式】
-
实现:【分为软件和硬件两个层面】
- 在操作系统内核:存储/卷管理【软件层面就是在文件系统与磁盘间添加中间层】
- RAID硬件控制器(I/O)【硬件层面就是直接添加到芯片的主存中】
4、RAID-0是如何提高吞吐率的呢?
- 数据块分成多个子块,存储在独立的磁盘中 【类似于内存交叉】
- 通过更大的有效块大小来提供更大的磁盘带宽
5、RAID-1是如何提高可靠性的呢?
- 向两个磁盘写入,从其中任何一个读取【执行一次写操作时,实际写入到了两个硬盘中】
- 可靠性变为原来的两倍,读取性能也提高了
6、如何将RAID-1和RAID-2的性能结合起来呢?
- RAID-4 实现了他俩的功能,既能增加吞吐量,也提高了存储的可靠性
- 数据块级磁带配有专用奇偶校验磁盘【有一个数据恢复的磁盘,但是只能恢复一个】
- 允许从任意一个故障磁盘中恢复
- 类似于Parity Disk 进行同步操作,其他几个磁盘进行什么操作他就进行什么操作,这也就导致这个磁盘成为瓶颈
7、那么如何能把奇偶校验的块分布在不同磁盘中?
- RAID-5 实现了将Parity Disk 的任务分到普通磁盘中,使得每个块的开销是类似的
- 既保证了可靠性,也提升了访问效率,不过只能恢复一个磁盘【如果想恢复多个磁盘,要增加奇偶校验盘的个数】
8、RAID-6 可以实现多个故障磁盘的恢复:
-
采用一种特殊的编码方式,但是也只能恢复两个故障磁盘
-
条带化和奇偶校验按byte-by-byte或者bit-by-bit
- RAID-0/4/5:block-wise
- RAID-3:bit-wise
-
例如:在RAID-3系统中存储bit-string101
- 也可以整合RAID1、RAID0,采用分层嵌套的方式实现快速可靠的特性:
八、磁盘调度
1、磁盘调度都涉及到哪些概念?
- 读取或写入时,磁头必须被定为在期望的磁道,并从所期望的扇区的开始
- 寻道时间:定位到期望的磁道所花费的时间
- 旋转延迟:从扇区的开始处到到达目的处所花费的时间
- 平均旋转延迟时间 = 磁盘旋转一周的时间的一半
2、磁盘 I/ O 时间都有哪些分类?
3、如何减少寻道前后移动的开销?
- 寻道时间是性能上区别的原因
- 对单个磁盘,会有一个 I/O 请求数目
- 如果请求是随机的,那么表现会很差
- 接下来介绍一些 I / O 调度算法
4、先来先服务算法(FITO)
- 按顺序处理请求
- 公平对待所有进程
- 在有很多进程的情况下,接近随机调度的性能
- 这种磁头调度策略并不高效
- 因为具有随机性,如果磁头移动的总距离很长 >> 开销很大
5、最短服务优先算法(SSTF)
- 选择从磁臂当前位置需要移动量最少的 I / O 请求
- 总是选择最短寻道时间
- 但是也存在问题:这是一种不公平、不均匀的调度算法,因为可能会出现远距离请求饥饿的现象
6、扫描算法(SCAN)
- 磁臂在一个方向上移动,满足所有未完成的请求,直到磁臂到达该方向上最后的磁道
- 调换方向【假如是从a0 -> a5扫描,扫描到最后一个位置直接跳到a0重新扫描】
- 有时被称为电梯算法
7、循环扫描算法(C-SCAN)
- 限制了仅在一个方向上扫描
- 当最后一个磁道也被访问过了后,磁臂返回到磁盘的另外一端再次进行扫描
- 这个是扫描算法的一个改进,从当前末端位置调转方向,往回扫描
8、C-LOOK 算法
- 属于循环扫描算法的改进版本,并不走到该方向末端,而是该方向请求的最后一个位置
- 磁臂先到达该方向上最后一个请求处,然后立即反转
9、N步扫描算法(N-step-SCAN)
-
磁头粘着(Arm Stickiness)现象
- SSTF、SCAN及CSCAN等算法中,都可能出现磁头停留在某处不动的情况
- 如:进程反复请求对某一磁道的I/O操作
-
N 步扫描算法
- 将磁盘请求队列分成长度为 N NN 的子队列
- 按FIFO算法依次处理所有子队列
- 扫描算法处理每个队列
-
当正在处理某子队列时,如果又出现新的磁盘I/O请求,便将新请求进程放入其他队列,这样就可避免出现粘着现象。
10、双队列扫描算法(FSCAN)
-
为了更加实用,我们把上方的划分的队列数 N NN 规定为 2 22。
-
FSCAN算法实质上是N步SCAN算法的简化
- FSCAN只将磁盘请求队列分成两个子队列
-
FSCAN算法
- 把磁盘I/O请求分成两个队列,交替使用扫描算法处理一个队列
- 一个是由当前所有请求磁盘I/O的进程形成的队列,由磁盘调度按SCAN算法进行处理
- 在处理某队列期间,将新出现的所有请求磁盘I/O的进程,放入另一个等待处理的请求队列
- 这样,所有的新请求都将被推迟到下一次扫描时处理