文件系统:一种用于持久性存储的系统抽象
是操作系统中管理持久性数据的子系统,提供数据存储的访问功能
➢ 在存储器上:组织,控制,导航,访问和检索数据
➢ 大多数计算机系统包含文件系统
➢ 个人电脑,服务器,笔记本电脑
➢ ipad,Tivo / 机顶盒,手机 / 掌上电脑
➢ Google可能也是由一个文件系统构成的
文件:文件系统中的一个单元的相关数据在操作系统中的抽象
是具有符号名,由字节序列构成的数据项集合
➢ 文件系统的基本数据单位
➢ 文件名是文件的标志符号
文件系统的功能
分配文件磁盘空间
➢ 管理文件块(哪一块属于哪一个文件)
➢ 管理空闲空间(哪一块是空闲的)
➢ 分配算法(策略)
管理文件集合
➢ 定位文件及其内容
➢ 命名:通过名字找到文件的接口
➢ 最常见:分层文件系统
➢ 文件系统类型(组织文件的不同方式)
提供的便利及特征
➢ 保护:分层来保护数据安全
➢ 可靠性 / 持久性:保持文件的持久即使发生崩溃,媒体错误,攻击等
文件和块
文件属性
➢ 名称,类型,位置,大小,保护,创建者,创建时间,最久修改时间,…
文件头
➢ 在存储元数据中保存了每个文件的信息
➢ 保存文件的属性
➢ 跟踪哪一块存储块属于逻辑上文件结构的哪个偏移
文件使用模式
➢ 使用程序必须在使用前先“打开”文件
f = open(name, flag);
...
... = read(f, ...);
...
close(f);
内核跟踪每个进程打开的文件
➢ 操作系统为每个进程维护一个打开文件表
➢ 一个打开文件描述符是这个表中的索引
需要元数据来管理打开文件:
➢ 文件指针:指向最近的一次读写位置,每个进程分别维护自己的打开文件指针
➢ 文件打开计数:记录文件打开的次数 - 当最后一个进程关闭了文件时,允许将其从打开文件表中移除
➢ 文件磁盘位置:缓存数据访问信息
➢ 访问权限:每个程序访问模式信息
文件的用户视图和系统视图
用户视图
➢ 持久的数据结构
系统访问接口
➢ 字节序列的集合(UNIX)
➢ 系统不会关心你想存储在磁盘上的任何的数据结构
操作系统内部视角
➢ 块的集合(块是逻辑存储单元,而扇区是物理存储单元)
➢ 块大小<> 扇区大小:在UNIX中,块的大小是4KB
用户视图到系统视图的转换
当用户说:给我2-12字节空间时会发生什么?(进程读文件)
➢ 获取字节所在的块
➢ 返回快内对应部分
如果要写2-12字节?(进程写文件)
➢ 获取块
➢ 修改块内对应部分
➢ 写回块
在文件系统中的所有操作都是在整个块空间上进行的(基本操作单位是数据块)
➢ 举个例子,getc()
putc()
: 即使每次只访问1字节的数据,也会缓存目标数据4096字节(一个磁盘块)
访问模式
用户怎么访问文件
➢ 在系统层面需要知道用户的访问模式
顺序访问:按字节依次读取
➢ 几乎所有的访问都是这种方式
随机访问:从中间读写
➢ 不常用,但是仍然重要。例如:虚拟内存支持文件,内存页存储在文件中
➢ 更加快速 - 不希望获取文件中间的内容的时候也必须先获取块内所有字节
基于内容(索引)访问:通过数据特征索引
➢ 通常操作系统不完整提供索引访问
➢ 数据库是建立在索引内容的磁盘访问上(需要高效的随机访问)
索引访问文件示例
文件内部结构
无结构
➢ 单词、比特的队列
简单记录结构
➢ 列
➢ 固定长度
➢ 可变长度
复杂结构
➢ 格式化的文档(如,MS word, PDF)
➢ 可执行文件
➢ …
文件共享和访问控制
多用户系统中的文件共享是很必要的
访问控制
➢ 谁能够获得哪些文件的哪些访问权限
➢ 访问模式:读,写,执行,删除,列举等
文件访问控制列表(ACL)
➢ <文件实体, 权限>
Unix模式
➢ <用户|组|所有人,读|写|可执行>
➢ 用户ID识别用户,表明每个用户所允许的权限及保护模式
➢ 组ID允许用户组成组,并指定了组访问权限
语义一致性
指定多用户 / 客户如何同时访问共享文件:
➢ 和过程同步算法相似
➢ 因磁盘IO和网络延迟而设计简单
UNIX文件系统(UFS)语义
➢ 对打开文件的写入内容立即对其他打开同一文件的其他用户可见
➢ 共享文件指针允许多用户同时读取和写入文件
会话语义
➢ 写入内容只有当文件关闭时可见
锁
➢ 一些操作系统和文件系统提供该功能
分层文件系统
目录操作
典型操作
➢ 搜索文件
➢ 创建文件
➢ 删除文件
➢ 枚举目录
➢ 重命名文件
➢ 在文件系统中遍历一个路径
操作系统应该只允许内核模式修改目录
➢ 确保映射的完整性
➢ 应用程序能够读目录(如 ls)
目录实现
文件名的线性列表,包含了指向数据块的指针
➢ 编程简单
➢ 执行耗时
Hash表 - hash数据结构的线性表
➢ 减少目录搜索时间
➢ 碰撞 - 两个文件名的hash值相同
➢ 固定大小
名字解析(路径遍历)
名字解析:逻辑名字转换成物理资源(如文件)的过程
➢ 在文件系统中:到实际文件的文件名(路径)
➢ 遍历文件目录直到找到目标文件
举例:解析 “/bin/ls”
➢ 读取root的文件头(在磁盘固定位置)
➢ 读取root的数据块:搜索 “bin” 项
➢ 读取bin的文件头
➢ 读取bin的数据块:搜索 “ls” 项
➢ 读取ls的文件头
当前工作目录(PWD)
➢ 每个进程都会指向一个文件目录用于解析文件名
➢ 允许用户指定相对路径来代替绝对路径,如,用 PWD=“/bin” 能够解析 “ls”
文件系统挂载
两个或多个文件名关联同一个文件
硬链接:多个文件项指向一个文件
软链接:以“快捷方式”指向其他文件
➢ 通过存储真实文件的逻辑名称来实现
如果删除一个有别名的文件会如何呢?
➢ 这个别名将成为一个 “悬空指针”
Backpointers方案:
➢ 每个文件有一个包含多个backpointers的列表,所以删除所有的backpointers
➢ Backpointers使用菊花链管理
添加一个间接层:目录项数据结构
➢ 链接 - 已存在文件的另外一个名字(指针)
➢ 链接处理 - 跟随指针来定位文件
文件目录中的循环
如何保证没有循环?
➢ 只允许到文件的链接,不允许在子目录的链接
➢ 增加链接时,用循环检测算法确定是否合理
更多实践
➢ 限制路径可遍历文件目录的数量
磁盘文件系统
➢ 文件存储在数据存储设备上,如磁盘
➢ 例如:FAT,NTFS,ext2/3,ISO9660 等
数据库文件系统
➢ 文件特征是可被寻址(辨识)的
➢ 例如:WinFS
日志文件系统
➢ 记录文件系统的修改/事件
➢ 例如:journaling file system
网络 / 分布式文件系统
➢ 例如:NF,SMB,AFS,GFS
特殊 / 虚拟文件系统
网络 / 分布式文件系统
文件可以通过网络被共享
➢ 文件位于远程服务器
➢ 客户端远程挂载服务器文件系统
➢ 标准系统文件访问被转换成远程访问
➢ 标准文件共享协议:NFS for Unix,CIFS for Windows
分布式文件系统的挑战
➢ 客户端和客户端上的用户辨别起来很复杂
√ 例如,NFS是不安全的
➢ 一致性问题
➢ 错误处理模式
分层结构
➢ 顶层:文件 / 文件系统API
➢ 上层:虚拟(逻辑)文件系统 (将所有设备IO,网络IO全抽象成为文件,使得接口一致)
➢ 底层:特定文件系统模块
目的
➢ 对所有不同文件系统的抽象
功能
➢ 提供相同的文件和文件系统接口
➢ 管理所有文件和文件系统关联的数据结构
➢ 高效查询例程,遍历文件系统
➢ 与特定文件系统模块的交互
基本数据结构
卷控制块(UNIX:“superblock”)
➢ 每个文件系统一个
➢ 文件系统详细信息
➢ 块、块大小、空余块、计数 / 指针等
文件控制块(UNIX:“vnode” or “inode”)
➢ 每个文件一个
➢ 文件详细信息
➢ 许可、拥有者、大小、数据库位置等
目录节点(Linux:“dentry”)
➢ 每个目录项一个(目录和文件)
➢ 将目录项数据结构及树形布局编码成树形数据结构
➢ 指向文件控制块、父节点、项目列表等
文件系统数据结构
➢ 卷控制块(每个文件系统一个)
➢ 文件控制块(每个文件一个)
➢ 目录节点(每个目录项一个)
持续存储在二级存储中
➢ 在分配在存储设备中的数据块中
当需要时加载进内存
➢ 卷控制块:当文件系统挂载时进入内存
➢ 文件控制块:当文件被访问时进入内存
➢ 目录节点:在遍历一个文件路径时进入内存
数据块按需读入内存
➢ 提供read()
操作
➢ 预读:预先读取后面的数据块
数据块使用后被缓存
➢ 假设数据将会再次被使用
➢ 写操作可能被缓存和延迟写入
两种数据块缓存方式
➢ 普通缓冲区缓存
➢ 页缓存:同一缓存数据块和内存页
分页要求
➢ 当需要一个页时才将其载入内存
支持存储
➢ 一个页(在虚拟地址空间中)可以被映射到一个本地文件中(在二级存储中)
文件数据块的页缓存
➢ 在虚拟内存中文件数据块被映射成页
➢ 文件的读/写操作被转换成对内存的访问
➢ 可能导致缺页和/或设置为脏页
➢ 问题:页置换 - 从进程或文件页缓存中?页置换算法需要协调虚拟存储和页缓存间的页面数。
打开文件描述符
➢ 每个被打开的文件一个都有一个文件描述符
➢ 文件状态信息
➢ 目录项,当前文件指针,文件操作设置等
打开文件表
➢ 一个进程一个
➢ 一个系统级的
➢ 每个卷控制块也会保存一个列表
➢ 所以如果有文件被打开将不能被卸载
打开文件锁
一些操作系统和文件系统提供该功能
调节对文件的访问
强制和劝告:
➢ 强制 - 根据锁保持情况和需求拒绝访问
➢ 劝告 - 进程可以查找锁的状态来决定怎么做
文件大小
大多数文件都很小
➢ 需要对小文件提供强力的支持
➢ 块空间不能太小
一些文件非常大
➢ 必须支持大文件(64-bit 文件偏移)
➢ 大文件访问需要相当高效
文件分配
如何为一个文件分配数据块
分配方式
➢ 连续分配
➢ 链式分配
➢ 索引分配
指标
➢ 高效:如存储利用(外部碎片)
➢ 表现:如访问速度
连续分配
文件头指定起始块和长度
位置 / 分配策略
➢ 最先匹配,最佳匹配,…
优势
➢ 文件读取表现好
➢ 高效的顺序和随机访问
劣势
➢ 碎片!
➢ 文件增长问题
√ 预分配?
√ 按需分配?
链式分配
文件以数据块链表方式存储
文件头包含了到第一块和最后一块的指针
优点
➢ 创建,增大,缩小很容易
➢ 没有碎片
缺点
➢ 不可能进行真正的随机访问
➢ 可靠性
√ 破坏一个链然后…
索引分配
为每个文件创建一个名为索引数据块的非数据数据块
➢ 到文件数据块的指针列表
文件头包含了索引数据块
优点
➢ 创建,增大,缩小很容易
➢ 没有碎片
➢ 支持直接访问
缺点
➢ 当文件很小时,存储索引的开销
➢ 如何处理大文件?链式、分层
在早期的Unix系统中就采用了多级索引块方式,UFS多级索引分配:
大文件的问题
文件头包含13个指针
➢ 10个指针指向数据块
➢ 第11个指针指向索引块
➢ 第12个指针指向二级索引块
➢ 第13个指针指向三级索引块
效果
➢ 提高了文件大小限制阈值
➢ 动态分配数据块,文件扩展很容易
➢ 小文件开销小
➢ 只为大文件分配间接数据块,大文件在访问数据块时需要大量查询
这几种办法都有各自的优点和缺点,在实际使用时会把这几种方法组合到一起来用。
位图
用位图代表空闲数据块列表:
➢ 1111111111110011101001010111... 1111111111110011101001010111 ... 1111111111110011101001010111...
➢ 如果 i = 0 i = 0 i=0 表明数据块 i i i 是空闲的,反之则已分配
使用简单但是可能会是一个 b i g v e c t o r big\ vector big vector:
➢ 160 G B d i s k → 40 M b l o c k s → 5 M B w o r t h o f b i t s 160GB\ disk → 40M\ blocks → 5MB\ worth\ of\ bits 160GB disk→40M blocks→5MB worth of bits
➢ 然而,如果空闲空间在磁盘中均匀分布,那么在找到 “ 0 0 0” 之前需要扫描 n / r n / r n/r
√ n = 磁 盘 上 数 据 块 总 数 n=磁盘上数据块总数 n=磁盘上数据块总数
√ r = 空 闲 块 的 数 目 r=空闲块的数目 r=空闲块的数目
保证一致性
需要保护:
➢ 指向空闲列表的指针
➢ 位图
√ 必须保存在磁盘上
√ 在内存和磁盘拷贝可能有所不同
√ 不允许block[i]
在内存中的状态为bit[i] = 1
而在磁盘中bit[i] = 0
解决:
➢ 在磁盘上设置bit[i] = 1
➢ 分配block[i]
➢ 在内存中设置bit[i] = 1
其他空闲空间列表方式
通常磁盘通过分区来最大限度减小寻道时间
➢ 一个分区是一个柱面的集合
➢ 每个分区都是逻辑上独立的磁盘
分区:硬件磁盘的一种适合操作系统指定格式的划分
卷:一个拥有完整文件系统实例的可访问的存储空间
➢ 通常常驻在磁盘的单个分区上
多磁盘管理
使用多个并行磁盘来增加
➢ 吞吐量(通过并行)
➢ 可靠性和可用性(通过冗余)
冗余磁盘阵列 - RAID(Redundant Array of Inexpensive Disks)
➢ 各种磁盘管理技术
➢ RAID levels:不同RAID分类(如,RAID-0,RAID-1,RAID-5)
实现
➢ 软件:在操作系统内核:存储 / 卷管理
➢ 硬件:RAID硬件控制器(I/O)
RAID-0
磁盘条带化
数据块分成多个子块,存储在独立的磁盘中
➢ 和内存交叉相似
通过更大的有效块大小来提供更大的磁盘带宽
存储的时候磁盘分成三个子块,写数据 三个磁盘同时写,读也是同时读三个,速度会提升接近三倍;如果读的数据量比较小 只有第一组,那么速度是没有提升的。
RAID-1
磁盘镜像
可靠性成倍增长
读取性能线性增加
➢ 向两个磁盘写入,从任何一个读取
提高可靠性,但也有一定的代价。
RAID-4
带校验的磁盘条带化(结合RAID-0、RAID-1)
数据块级磁带配有专用奇偶校验磁盘
➢ 允许从任意一个故障磁盘中恢复
➢ 例如:存储8,9,10,11,12,13,14,15,0,1,2,3
既能够提高性能,又能增加可靠性。
如果 D i s k 1 − 4 Disk1-4 Disk1−4 任何一个盘的数据丢失,可以通过另一个特殊的盘 P a r i r y D i s k Pariry\ Disk Pariry Disk 把坏的数据恢复,通过纠错码的方式,假设某一个 D i s k i Disk_i Diski 坏了, P a r i r y D i s k Pariry\ Disk Pariry Disk 中存的数据其实是数字的奇偶校验,可以把坏掉的盘的数据反推出来;
用额外的一个盘来完成容错的功能,出现一个故障可以恢复,但出现多个故障就不行了。
当 D i s k 1 − 4 Disk1-4 Disk1−4 任何一个磁盘进行写操作,此时 P a r i r y D i s k Pariry\ Disk Pariry Disk 也需要进行一次写操作,所以它的读写非常频繁,会出现瓶颈,能否把 P a r i r y D i s k Pariry\ Disk Pariry Disk 的开销均匀的分布在不同的盘中?↓
RAID-5
带分布式校验的磁盘条带化
每个条带块有一个奇偶校验块
➢ 但同样 只允许有一个磁盘错误
基于位和基于块的磁盘条带化
条带化和奇偶校验按byte-by-byte或者bit-by-bit
➢ RAID-0/4/5:block-wise(基于数据块)
➢ RAID-3:bit-wise(基于位)
例如:在RAID- 3系统中存储bit-string 101(粒度太细,不实用)
RAID-6
每个条带块有两个冗余块
➢ 有一种特殊的编码方式
➢ 允许两个磁盘错误
进步一提高可靠性,当然这种可靠性到底提了多少,跟你数据的重要性和对可靠性的要求的不同而不同。
RAID嵌套
此内容我放在了下一章:【操作系统】Operation System-第14章-I/O子系统
整理自 【清华大学】 操作系统