文件系统是操作系统内负责持久保存数据的子系统,提供数据存储和访问功能。
组织、检索、读写访问功能;
大多数计算机系统都有文件系统;
Google也是一个文件系统
文件是具有符号名,由字节序列构成的数据项集合
是文件系统的基本数据单位
文件名时文件的标识符号
文件系统的功能:
分配文件磁盘空间:
管理文件块(位置和顺序)
管理空闲空间(位置)
分配算法(策略)
管理文件集合
定位:文件及其内容
命名:通过名字找到文件
文件系统结构:文件组织方式
数据可靠性和安全
安全:多层次保护数据安全
可靠:持久化保存数据;避免系统崩溃、媒体错误、攻击等
文件头:文件系统元数据中的文件信息
文件属性:名称、类型、位置、大小、保护、创建者、创建时间、最近修改时间等。。。
文件存储位置和顺序
打开文件和文件描述符:
文件访问模式:
进程访问文件数据必须先“打开”文件
内核跟踪进程打开的所有文件:
操作系统为每个进程维护一个打开文件表
文件描述符是打开文件的标识
文件描述符:操作系统在打开文件表中维护的打开文件状态和信息
文件指针:
最近一次读写位置
每个进程分别维护自己的打开文件指针
文件打开计数:
当前打开文件的次数
最后一个进程关闭文件时,将其从打开文件表中移除
文件的磁盘位置:
缓存数据访问信息
访问权限:
每个进程的文件访问模式信息
文件的用户视图和系统视图:
文件的用户视图:
持久的数据结构
系统访问接口:
字节序列的集合
系统不关心存储在磁盘上的数据结构
操作系统的文件视图
数据块的集合
数据块时逻辑存储单元,而扇区时物理存储单元
通常时几个扇区构成一个数据块
用户视图到系统视图的转换
进程读文件:
获取字节所在的数据块
返回数据块内对应部分
进程写文件:
获取数据块
修改数据块中对应部分
写会数据块
文件系统中的基本操作单位时数据块
说明即使读取一个字节,也需要缓存目标数据的一个块大小(4096字节)
访问模式:
操作系统需要了解进程是如何访问文件的
顺序访问:按字节以此读取
大多数的文件访问都是顺序访问
随机访问:从中间读写
不常用,但仍然重要
例如,虚拟内存把内存页存储在swap区上
索引访问:依据数据特征索引
通常操作系统不完整提供索引访问(可认为操作系统的文件系统是一个小型的数据库)
数据库是建立在索引内容的磁盘访问方式
文件内部结构
无结构:
单词、字节序列
简单记录结构:
分列、固定长度、可变长度
复杂机构:
格式化的文档、可执行文件..... 通常由应用程序识别
文件共享和访问控制:
多用户系统中的文件共享时很有必要的
访问控制:
每个用户能够获得哪些文件的哪些访问权限
访问模式:读、写、执行、删除、列表等
文件访问控制列表(ACL)
<文件实体,权限>
Unix模式
<用户|组|所有人,读|写|可执行>
用户标识ID
识别用户,表明每个用户所允许的权限及保护模式
组标识ID
允许用户组成组,并指定组访问权限
语义一致性:
规定多进程如何同时访问共享文件
与同步算法相似 (读写一致)
因磁盘I/O和网络延迟而设计简单
Unix文件系统(UFS)语义
对打开文件的写入内容立即对其他打开同一文件的其他用户可见
共享文件指针允许多用户同时读取和写入文件
需要靠用户进程自己实现读写一致性
会话语义:
写入内容只有当文件关闭时可见
读写锁:
操作系统和文件系统提供该功能
文件系统的实现:
分层结构
虚拟文件系统(VFS)
特定文件系统模块
设备I/O(ext2、fat、ext3、iso9600)、网络I/O(NFS、SMB)
虚拟文件系统(VFS)
目的:提供对所有不同文件系统的抽象
功能:
提供相同的文件和文件系统接口
管理所有文件和文件系统关联的数据结构
高效查询例程,遍历文件系统
与特定文件系统模块的交互
文件系统基本数据结构
文件卷控制块(superblock)
每个文件系统一个
文件系统详细信息
块、块大小、空余块、计数/指针等
文件控制块(iNode,索引节点)
每个文件一个
文件详细信息
访问权限、拥有者、大小、数据块位置等
目录项(detry)
每个目录项一个(目录和文件)
将目录项数据结构及树型布局编码成树型数据结构
指向文件控制块、父目录、子目录等
文件系统的组织视图:
文件卷控制块---->目 录 项
子目录项1 子目录项2 子目录项3
孙目录项1 。。。。 //树状结构
(文件控制块,iNode)
data block.......
文件系统的存储结构:
文件系统数据结构:
卷控制块(每个文件系统一个)
文件控制块(每个文件一个)
目录节点(每个目录项一个)
持久存储在外存中
存储设备的数据块中
当需要时加载进内存
卷控制块模块:当文件系统挂载时进入内存
文件控制块: 当文件被访问时进入每次
目录节点:在遍历一个文件路径时进入内存
文件系统的存储视图:
卷控制块(0)->目录节点(1)(->二级目录项1.5)->文件控制块(2)
Disk ------------------------------------------------------------------------
|0| 1 | 1.5 | 2 | 3 data block |
卷控制块指向目录节点的首位置
目录节点可能指向其子目录项,也有可能指向文件控制块
每个文件控制块指向存放数据的数据块
文件缓存和打开文件:
数据块缓存:
数据块按需读入内存
预读:预先读取后面的数据块
数据块使用后被缓存
假设数据将会被再次用到
写操作可能被缓存和延迟写入
两种数据块缓存方式
数据块缓存
页缓存:同一缓存和数据块和内存页
文件描述符:
每个被打开的文件都有一个文件描述符
文件状态信息
目录项、当前文件指针、文件操作设置等
打开文件表
每个进程一个进程打开的文件表
一个系统级的打开文件表
每一项对应一个目录项和文件控制块
有文件被打开时,文件卷就不能被卸载
不同进程打开文件表会映射到系统的打开文件表
打开文件锁:一些文件系统提供文件锁,用于协调多进程的文件访问
强制锁:
根据锁保持情况和访问需求确定是否拒绝访问
劝告:
进程可以查找锁的状态来决定怎么做
文件分配:如何表示分配给一个文件数据块的位置和顺序
文件大小:大多数文件都很小
需要对小文件提供很好的支持
块空间不能太大
一些文件非常大:
必须支持大文件(64为文件偏移)
大文件访问需要高效
指标:
存储效率:外部碎片
读写性能:访问速度
分配方法:
连续分配:文件头指定起始块和长度。
分配策略:最先匹配,最佳匹配.....
优点:文件读取表现好,高效的顺序和随机访问
缺点:碎片、文件增长问题(预分配?按需分配?)
链式分配:
文件以数据块链表方式存储
文件头包含了到第一块和最后一块的指针
优点:
创建、增大、缩小很容易
没有碎片
缺点:
无法实现真正的随机访问
可靠性差:破坏一个链,后面的数据块就丢了
索引分配:
为每个文件创建一个索引数据块
指向文件数据块的指针列表
文件头包含了索引数据块指针
优点:创建、增大、缩小很容易
没有碎片
支持直接访问
缺点:
当文件很小时,存储索引的开销
如何处理大文件
实际运用上,是把上述几种分配方法组合起来
大文件的索引分配
链式索引块:索引块之间用链式连接
多级索引块:高级索引块存储了低级级索引块的地址,低级索引块存储更低一级索引块,最后一级索引块指向数据块
UFS(Unix file system)多级索引分配
文件控制块的前面10个指针是直接索引,直接指向数据块 10个
第11个指针是指向一级间接索引块,n个数据块
第12个指针是指向二级索引块,该二级间接索引块每一个指针指向一个一级间接索引块 n^2个数据块
第13个指针....... n^3数据块;
效果:
提高了文件大小限制阈值
动态分配数据块,文件扩展很容易
小文件开销小
只为大文件分配间接索引块,大文件在访问数据块时需要大量查询
空闲空间管理和冗余磁盘阵列RAID
空闲空间组织:
位图:
用位图标识空闲数据块列表
使用简单但是可能会是一个大的很大向量表
160G磁盘->40M数据块->5MB位图
频繁查询空闲块,时间复杂度高
链表:
用链表链接空闲块
链式索引:
每个索引指针指向一个空闲块
通常磁盘是通过分区来最大限度减少寻到时间
一个分区可以看做一个圆环,磁头在一个圆环内来回移动