文件系统
基本概念
文件系统和文件
文件系统是操作系统中管理持久性数据的子系统,提供数据存储和访问功能。(一种用于持久性存储的系统抽象)
在存储器上组织、检索、读写访问数据
大多数计算机系统都有文件系统
个人电脑、服务器、笔记本电脑
- iPod、机顶盒、手机
- Google也是一个文件系统
文件是具有符号名,由字节序列构成的数据项集合。(文件是文件系统中一个单元的相关数据在操作系统中的抽象)
文件系统的基本数据单位
文件名是文件的标识符号
文件系统的功能
分配文件磁盘空间
管理文件块(位置和顺序)
管理空闲空间(位置)
分配算法 (策略)
管理文件集合
- 定位文件及其内容
命名:通过名字找到文件
最常见:分层文件系统
文件系统结构:文件组织方式
数据可靠和安全
- 安全:分层来保护数据安全
- 可靠性/持久性:保护文件的持久即使发生崩溃、媒体错误、攻击等
文件属性
文件属性
- 名称、类型、位置、大小、保护、创建者、创建时间、最近修改时间(这些元数据保存在文件头中或者文件块中)
文件头
- 在存储元数据中保存了每个文件的信息
- 保存了文件的属性
- 跟踪哪一块存储块属于逻辑上文件结构的哪个偏移
文件描述符
文件访问模式
进程访问文件数据前必须先“打开”文件
f = open(name, flag);
…
read(f, …);
…
close(f);内核跟踪进程打开的所有文件
操作系统为每个进程维护一个打开文件表
一个打开文件描述符(f)是这个表中的索引
需要元数据数据来管理打开的文件(操作系统在打开文件表中维护打开文件状态和信息
)
文件指针:最近一次读写位置,每个进程分别维护自己的打开文件指针
文件打开计数:当前打开文件的次数,最后一个进程关闭文件时,将其从打开文件表中移除
- 文件的磁盘位置:缓存数据访问信息
- 访问权限:每个进程的文件访问模式信息
文件的用户视图和系统视图
文件的用户视图:持久的数据结构
系统访问接口:字节序列的集合(UNIX),系统不关心存储在磁盘上的数据结构
操作系统的文件视图
数据块的集合(数据块是逻辑存储单元,而扇区是物理存储单元)
块大小<>扇区大小:在Unix中,块的大小是4kB
文件系统中的基本操作单位是数据块:例如, getc()和putc()即使每次只访问1字节的数据,也需要缓存目标数据4096字节
用户看到是抽象的一维的线性数据空间,系统看到磁盘块
访问模式
操作系统需要了解进程如何访问文件
顺序访问: 按字节依次读取,大多数的文件访问都是顺序访问
随机访问: 从中间读写,不常用, 但仍然重要,例如, 虚拟内存中把内存页存储在文件
索引访问: 依据数据特征索引,通常操作系统不完整提供索引访问,相反,数据库是建立在索引内容的磁盘访问上(需要高效的随机访问)
文件内部结构(应用系统打开复杂文件,但对操作系统该文件可能很简单,就是字节流 )
- 无结构:单词,字节序列
- 简单记录结构:列,固定/可变长度
- 复杂结构:格式化文档(MS Word, PDF),可执行文件
文件共享和访问控制
- 多用户系统中的文件共享是很必要的
访问控制
每个用户能够获得哪些文件的哪些访问权限
访问模式: 读、写、执行、删除、列表等
- 文件访问控制列表(ACL):<文件实体, 权限>
- Unix模式
<用户|组|所有人, 读|写|可执行>
用户标识ID:识别用户, 表明每个用户所允许的权限及保护模式
组标识ID:允许用户组成组,并指定了组访问权限
语义一致性
- 规定多进程如何同时访问共享文件
与同步算法相似
因磁盘I/O和网络延迟而设计简单
- Unix 文件系统(UFS)语义
对打开文件的写入内容立即对其他打开同一文件的其他用户可见
共享文件指针允许多用户同时读取和写入文件
会话语义:写入内容只有当文件关闭时可见
读写锁:一些操作系统和文件系统提供该功能
目录
分层文件系统
文件以目录的方式组织起来
目录是一类特殊的文件:目录的内容是文件索引表<文件名, 指向文件的指针>
目录和文件的树型结构:
早期的文件系统是扁平的 (只有一层目录)
目录操作
- 典型目录操作:搜索文件,创建文件,删除文件,枚举目录,重命名文件,在文件系统中遍历一个路径
- 操作系统应该只允许内核修改目录:确保映射的完整性,应用程序通过系统调用访问目录(如ls)
目录实现
文件名的线性列表,包涵了指向数据块的指针,编程简单,执行耗时
哈希表– 哈希数据结构的线性表
减少目录搜索时间
碰撞– 两个文件名的哈希值相同
固定大小
名字解析(路径遍历)
- 名字解析: 把逻辑名字转换成物理资源(如文件)
依据路径名,在文件系统中找到实际文件位置
遍历文件目录直到找到目标文件
举例: 解析“/bin/ls”
读取根目录的文件头 (在磁盘固定位置)读取根目录的数据块,搜索“bin”项
读取bin的文件头
读取bin的数据块; 搜索“ls”顶
读取ls的文件头
- 当前工作目录 (PWD)
每个进程都会指向一个文件目录用于解析文件名
允许用户指定相对路径来代替绝对路径,如,用PWD=“/bin” 能够解析 “ls”
文件系统挂载
文件系统需要先挂载才能被访问
一个未挂载的文件系统被挂载在挂载点上
文件别名
- 两个或多个文件名关联同一个文件
硬链接: 多个文件项指向一个文件
软链接: 以“快捷方式”指向其他文件,通过存储真实文件的逻辑名称来实现(文件的内容为另一个文件的路径名)
如果删除了一个有别名的文件会如何呢?
- 如果是软链接,这个别名会成为一个空指针
- 如果是硬链接,可能有一个隐形计数器,删除一次减一,直到为0时才真正删除
backpointers 反向指针 方案
- 每个文件有一个包含多个backpointer的列表,所以删除所有的backpointer
- backpointer使用菊花链管理
添加间接层,即目录项数据结构(先指向间接层,再指向真实的文件)
- 链接:已存在文件的另一个名字(指针)
- 链接处理:根据指针来定位文件
文件目录中的循环
如何保证没有循环?
只允许到文件的链接,不允许在子目录的链接
增加链接时,用循环检测算法确定是否合理
更多实践——限制路径可遍历文件目录的数量
文件系统的类别
磁盘文件系统
- 文件存储在数据存储设备上, 如磁盘
例如:FAT(早起windows), NTFS(现在windows), ext2/3(unix,linux), ISO9660(光盘)等
数据库文件系统
文件特征是可被寻址(辨识)的
例如: WinFS
日志文件系统
记录文件系统的修改/事件(读写操作要么完成,要么不做,避免打断)
例如:journaling file system
网络/分布式文件系统
例如: NFS(网络), SMB(网络), AFS(分布式), GFS(google文件系统,分布式)
特殊/虚拟文件系统:以文件的方式展现读写的接口,来交互访问内核中的数据
网络/分布式文件系统
文件可以通过网络被共享
文件位于远程服务器
客户端远程挂载服务器文件系统
标准系统文件访问被转换成远程访问
标准文件共享协议:NFS for Unix, CIFS for Windows
分布式文件系统的挑战
客户端和客户端上的用户辨别起来很复杂,例如, NFS是不安全的
一致性问题
错误处理模式
虚拟文件系统
分层结构
- 上层:虚拟(逻辑)文件系统
- 底层:特定文件系统模块
目的:对所有不同文件系统的抽象
功能:
提供相同的文件和文件系统接口 ,管理所有文件和文件系统关联的数据结构, 高效查询例程,遍历文件系统 ,与特定文件系统模块的交互虚拟文件系统存在于内存中,当OS创建后会创建虚拟文件子系统
基本数据结构:
- 卷控制块(Unix:superblock ):每个文件系统一个,包括文件系统详细信息,包括块,块大小,空余块,计数/指针等
- 文件控制块(Unix:VNODE/INODE): 每个文件一个,文件的详细信息 ,包括许可、拥有者、大小、数据库位置等
- 目录节点(linux:dentry):每个目录项一个;将目录项数据结构及树性布局编码成树型数据结构;指向文件控制块、父节点、项目列表等
卷控制块(每个文件系统一个)、文件控制块(每个文件一个)、目录节点(每个目录项一个)都会映射到磁盘系统的一个或者对个扇区
持续存储在二级存储中:分配在存储设备的数据块中
当需要时加载进内存
- 卷控制块:当文件系统挂载时进入内存
- 文件控制块:当文件被访问时进入内存
- 目录节点: 在遍历一个文件路径时进入内存
数据缓存
虽然内存容量比内存大,但是硬盘数据访问太慢,为了能够提高速度,在内存仿一块缓存,把经常用到的或者当前正在访问的数据从硬盘读到内存中,接下来访问在内存中进行,提高效率
数据块缓存
数据块按需读入内存
提供read()操作
预读: 预先读取后面的数据块
数据块使用后被缓存
假设数据将会再次用到
写操作可能被缓存和延迟写入
两种数据块缓存方式(怎么样组织)
普通数据块缓存
页缓存: 统一缓存数据块和内存页
页缓存
- 分页要求:当需要一个页才将其载入内存
- 虚拟页式存储:在虚拟地址空间中虚拟页面可映射到本地文件中(在二级存储中)
文件数据块的页缓存
在虚拟内存中文件数据块被映射成页
文件的读/写操作被转换成对内存的访问
可能导致缺页和/或设置为脏页
问题: 页置换算法需要协调虚拟存储和页缓存间的页面数
打开文件的数据结构
打开文件首先要在硬盘中找到文件的位置,所谓开开就是将硬盘中关于文件控制块的内容读到内存中,将相关信息放到打开文件表中,文件表中设置一个项,将项的索引fd返回给应用程序(fd=fopen()),应用程序就可以基于索引进行读写。
文件描述符
每个被打开的文件都有一个文件描述符
文件状态信息:目录项、当前文件指针、文件操作设置等
打开文件表
- 一个进程一个
- 一个系统级的的打开文件表
- 如果有文件被打开的将不能卸载
打开文件锁:一些文件系统提供文件锁,用于协调多进程的文件访问
强制–根据锁保持情况和访问需求确定是否拒绝访问
劝告 – 进程可以查找锁的状态来决定怎么做
文件分配
打开文件之后可能要进行写操作,就要设计到对文件空间的管理。
文件大小
大多数文件都很小
需要对小文件提供很好的支持
块空间不能太大
一些文件非常大
必须支持大文件 (64位文件偏移)
大文件访问需要高效
文件分配
如何给一个文件分配数据块
分配方式
- 连续分配
- 链式分配
- 索引分配
指标
存储效率:外部碎片等
读写性能:访问速度
连续分配
- 文件头指定起始块和长度
分配策略:最先匹配, 最佳匹配, ...(内存分配的方法)
优点:文件读取表现好,高效的顺序和随机访问
缺点:碎片,文件增长问题(预分配?,按需分配?)
链式分配
文件以数据块链表方式存储
文件头包含了到第一块和最后一块的指针
优点:创建、增大和缩小容易,没有碎片
缺点:不可能真正地随机访问,可靠性(破坏一个链然后整个文件都丢失了)
索引分配
- 为每个文件创建一个名为索引数据块的非数据数据块——指向文件数据块的指针列表
文件头包含了索引数据块指针
优点:创建,增大缩小都很容易,没有碎片,支持直接访问
缺点:当文件很小时,存储索引的开销,如何处理大文件?大文件的索引块要很大,如何组织索引?
大文件的索引分配
早期的Unix是多级索引方式(UFS多级索引分配)
文件头包含13个指针:10 个指针指向数据块,第11个指针指向索引,第12个指针指向二级索引块,第13个指针指向三级索引块
效果:提高了文件大小限制阀值,动态分配数据块,文件扩展很容易,小文件开销小,只为大文件分配间接数据块,大文件在访问数据块时需要大量查询。
空闲空间列表
跟踪记录文件卷中未分配的数据块,空闲空间列表存储在哪里?采用什么数据结构表示空闲空间列表?
空闲空间组织: 位图
用位图代表空闲数据块列表
111111111111111001110101011101111...
i = 0 表明数据块i是空闲, 否则,表示已分配
使用简单但是可能会是一个大的很大向量表
160GB磁盘-> 40M数据块-> 5MB位图
假定空闲空间在磁盘中均匀分布,则找到“0”之前要扫描n/r ,n = 磁盘上数据块的总数,r = 空闲块的数目
其他空闲空间组织方式
多磁盘管理——RAID
磁盘分区
通常磁盘通过分区来最大限度减小寻道时间
一个分区是一组柱面的集合
每个分区都可视为逻辑上独立的磁盘
磁头的前后寻道(最慢),磁盘在旋转。
分区:硬件磁盘的一种适合操作系统指定格式的规划
卷:一个拥有一个文件系统实例的可访问的存储空间,通常常驻在磁盘的单个分区上。(一个文件系统由分区组成,一个磁盘可以有多个分区,一个卷拥有多个分区,可以把文件系统扩展到多个磁盘上)
多磁盘管理
使用多磁盘可改善:吞吐量(通过并行),可靠性和可用性 (通过冗余)
冗余磁盘阵列(RAID, Redundant Array of InexpensiveDisks)
多种磁盘管理技术
RAID分类,如,RAID-0, RAID-1, RAID-5
实现:
软件:操作系统内核的文件卷管理(在文件系统之下,在磁盘驱动之上有个RAID层,完成磁盘阵列管理)
硬件:RAID硬件控制器(I/O)(RAID硬件控制器映射出一个虚拟硬盘)
RAID-0:磁盘条带化
把数据块分成多个子块,存储在独立的磁盘中,通过独立磁盘上并行数据块访问提供更大的磁盘带宽
RAID-1: 磁盘镜像
向两个磁盘写入,从任何一个读取,可靠性成倍增长,读取性能线性增加
RAID-4: 带校验的磁盘条带化
数据块级的磁盘条带化加专用奇偶校验磁盘,允许从任意一个故障磁盘中恢复
出现一个故障可以恢复,校验磁盘是其他盘的奇偶校验。忘其他盘写数据时,也要往校验磁盘写,开销很大。
RAID-5: 带分布式校验的磁盘条带化
带化和奇偶校验按“字节”或者“位”
RAID-0/4/5: 基于数据块
RAID-3: 基于位
RAID-5: 每组条带块有一个奇偶校验块,允许一个磁盘错误
RAID-6: 每组条带块有两个冗余块,允许两个磁盘错误