文件的逻辑结构
大体上分为有结构文件和无结构文件。
无结构文件: 如windows下的.txt文件就是无结构的, 文件内部的数据就是一系列的二进制流或者字符流组成, 又称为流式文件。
有结构文件: 数据按记录的形式有组织的排列。 类似于数据库的存储方式。 有结构文件又分为顺序文件、 索引文件、 索引顺序文件三种。
顺序文件:
文件中的记录一个接一个的按逻辑上顺序排列, 记录可以是定长的, 也可以是不定长的。 物理上可以由顺序存储或者链式存储来实现。
PS:
链式存储:不能实现随机访问, 只能从第一个记录依次往后找。
顺序存储:若为可变长记录, 则不能实现随机存储。
若为定长记录, 则可以实现随机存储。 并且如果顺序存储采用的是串结构, 则无法根据关键字来找到某个特定的记录的位置, 而若使用的是顺序结构, 则可以快速找到某个特定的关键字的位置。
顺序文件分为串结构和顺序结构。 其中串结构中 记录之间的顺序与关键字无关, 一般按照存入记录的时间来存储。而在顺序结构中, 记录按照关键字的顺序排列,(比如按学号排列)。
顺序结构中删除/增加一个记录比较麻烦, 串结构比较简单。
索引文件
通过建立索引表来实现索引文件存储, 索引表的结构如下
索引号 | 长度 | 指针 |
指针指向记录,记录可以实现离散存储。
由于索引表是定长记录的顺序文件, 所以可以实现对记录的随机访问。
缺点: 每个记录对应一个索引表项, 导致存储空间利用率低。
索引顺序文件
是对索引文件和顺序文件的结合, 建立索引表,每个索引表项对应一组记录。 其中这组记录中是按照顺序文件的方式存储的。
完成了对索引文件的瘦身, 使索引表的变小。
若一个顺序文件由10000个记录,根据关键字进行检索, 平均需要5000次。
若为索引顺序文件, 把10000个记录分为100组, 每组100个记录, 则平均需要50 + 50 = 100次。
若此时检索次数 依旧很大, 则可以多次分组。
文件目录
文件目录是FCB的有序集合。FCB就是文件控制块。
文件目录是有结构文件,数据按记录存放。
FCB的结构如下:
文件名 | 类型 | 存取权限 | …… | 物理位置 |
usr | 目录 | 读/写 | …… | 外存298号块 |
…… | …… | …… | …… | …… |
每一个目录下都有一个文件目录, FCB实现了文件名和文件的一一映射, 实现了让用户按名存取
打开一个文件时, 先找到当前文件目录中对应的表项的物理位置, 然后把它读入内存就可以知道当前文件下存放的数据。
目录操作
搜索:当用户使用一个文件时, 需要根据文件名搜索目录, 找到文件对应的目录项。
创建文件:当用户创建一个文件时, 在文件目录中添加对应的FCB。
删除文件: 当用户删除一个文件时, 删除对应的FCB。
显示目录: 略。
修改目录:略。
目录结构
分为单级目录, 两级目录, 多级目录(树形目录), 无环图目录, 索引节点。
单级目录
整个系统只有一张目录表, 每个文件占一个目录项,可以实现按名存取, 但不允许重名。
两级目录
第一层文件目录中存放不同用户的主文件目录(MFD), 第二层文件目录中存放用户的用户文件目录(UFD), 不同用户之间可以重名, 但用户不可以对自己的文件再分类。
多级目录 又称为树形目录
Windows就是采用这种目录, 打开一个文件夹, 文件中还可以有文件夹。
当用户需要访问某个文件是需要指明路径。
路径分为两种:绝对路径和相对路径。
绝对路径:从根目录出发的路径。
相对路径:从某当前目录出发的路径。
绝对路径每次都要从根目录一级一级的往下找,I/O操作次数多,效率低。
相对路径可以从当前目录出发, 减少I/O操作。
不便于实现目录共享。
无环图目录
在树形目录的基础上增加了一些指向同一个节点的有向边, 成为了一个有向无环图。
不同目录下的文件可以指向不同的文件。
在这种情况下,当进行文件删除操作时,不能直接删除, 在这种结构下, 每个文件都有一个共享计数器, 每一个指向该文件的目录项都会使该计数器加一, 当删除时,该目录项删除,并且共享计数器减一, 若为0, 则删除该文件。
索引节点
索引节点是对FCB的改进,在实际中查找各级目录是的过程中,只需要文件名,只有当文件名匹配时,才需要读入其它信息,所以在没有匹配时除了文件名的信息就是冗余的。
索引节点的结构如下
文件名 | 索引节点指针 |
指针指向除了文件名之外的信息, 这样实现了对目录表的瘦身。
因为FCB小了, 每个磁盘块可以存储FCB的数量就提高了, 所以I/O操作的次数减少了,因此提高了文件的检索速度。
文件的物理结构
对空闲磁盘块的管理和对非空闲磁盘块的管理
用户通过 (逻辑块号, 块内地址) 来操作自己的文件, 操作系统需要负责逻辑块号到物理块号的转换。
对非空闲的磁盘块的管理
连续分配
每个文件在磁盘上占一组连续的磁盘块。
在文件目录中存放记录的起始块号和长度,
当用户给出要访问的逻辑块号时, 操作系统找到对应的FCB, 然后利用公式: 物理块号 = 逻辑块号 + 起始块号求出物理块号。 所以支持顺序访问和随机访问。
优点:
因为存储文件的磁盘块时相邻的, 所以读写速度最快。
缺点:
文件拓展性差: 若某个文件占有三个磁盘块,且相邻的磁盘块都是非空闲的, 此时若文件需要增加一个磁盘块, 则需要把整个文件都迁移到新的空闲区, 而迁移操作很浪费时间。
会产生磁盘碎片:比如一个文件需要4个磁盘块,
若有四个离散的空闲块, 则无法为此文件分配磁盘空间。
可以用紧凑技术来处理碎片, 但很浪费时间。
链接分配
可采用离散的分配方式。 分为隐式链接和显式链接。
隐式链接
在FCB中记录起始块号和结束块号,并且存储了指向下一个块号的指针(最后一块没有指针)。
当用户要访问逻辑块号i, 操作系统找到该文件对应的FCB, 在FCB中查找到该起始块号, 将0号逻辑块读入内存, 之后就可以依次读取。(一共需要i+1次i/o操作)。
不支持随机存取, 查找效率低,由于需要存储指向下一个盘块的指针, 存储效率不高。拓展文件很方便。
显式链接
把链接各个物理块的指针放在FAT中,即文件分配表, 表中记录了当前物理块号和下一块的数据。
此时FCB中只需要记录起始块号, 不需要记录结束块号。
一个磁盘仅设置一张FAT, 在开机时读入并常驻内存。并且FAT中记录时定长的, 所以物理块号可以是隐含的, 即可以不存储物理块号。
实现逻辑块号到物理块号中不需要I/O操作, 因为FAT在内存中。
索引分配
每个文件对应一个索引表, 索引表中记录了逻辑块号到物理块号的转换。
索引表存在磁盘块中, 存入索引块的磁盘块叫索引块, 存放文件数据的磁盘块叫做数据块。
文件的FCB中记录了对应的索引块, 当要查找逻辑块号为i, 先去对应的FCB中找到它的索引块, 然后到索引表中找到逻辑块号为i对应的物理块号。
这种方式类似 页表。 索引分配方式可以支持随机访问, 文件拓展很方便, 增加一个索引表项并分配一个磁盘块就可以了。
假设每个磁盘块为1KB, 一个索引表项为4B, 则一个磁盘块只能存256个索引项, 若某个文件超过了256, 则可以采用以下三种方案:
1.链接分配
2.多层索引
3.混合索引
链接分配
为该文件分配多个索引块, 并建立索引块记录之下下一索引块的指针。
但这种方式查找速度很慢, 因为只能顺序的查找。
多层索引
类似 多级页表。
若为两级索引, 则第一层索引的每一个索引项又指向一个索引表, 该索引表的每一项对应逻辑块号到物理块号的映射。
若要访问逻辑块号为513, 则 513 / 256 = 2, 513 % 256 = 1, 先访问一级索引表中的第二项, 然后再去二级索引表中找到第一项。 需要三次I/O操作。
若文件很小, 还是需要三次I/O操作。
混合索引
建立一个顶级索引表, 表中有一些直接地址, 直接映射到物理块号, 还有一些一级间接索引, 指向一个索引表, 还有一些二级间接索引, 每个表项又指向一个索引表。
对空闲的存储空间的管理
磁盘中分为文件区和目录区, 目录区存放FCB、管理磁盘的信息, 文件区存放普通数据。
空闲表法
在空闲盘块表中记录空闲信息,
第一个空闲盘块号 | 空闲盘块数 |
2 | 5 |
…… | …… |
采用这种方式分配磁盘块时, 可以采用首次适配, 最佳适配, 最坏适配算法来决定为文件分配哪个区间。
回收磁盘块时, 要分四种情况:1. 回收区前后都没有相邻空闲区。2.~前面有空闲区, 后面没有。3. ~后有前没有。4. ~前后都有。 要注意表项的合并问题。
空闲链表法
分为空闲盘块链和空闲盘区链。
一个以盘块为单位, 一个以盘区为单位。
空闲盘块中存放着下一个空闲盘块的指针。
操作系统保存链头和链尾指针。
当要为文件分配磁盘块时, 从链头依次取出需要的磁盘块数, 并修改链头指针。
回收时, 把回收的盘块依次放在链尾, 并修改链尾指针。
位视图法
设立一张表, 1代表非空闲块, 0 代表空闲块。
位视图用(字号, 位号)来标记每个盘块。
例: (2, 5) , 若一个字的字长为16位, 则(2,5)对应的磁盘号是 2 * 16 + 5 = 37.
成组链接法
文件卷的目录区有一个叫做超级块的磁盘块, 当操作系统启动时, 便将超级快读入内存, 要时刻保持内存和外存的超级块信息的一致。
超级块中记录了下一组空闲磁盘块数和空闲块号, 而下一个空闲块号又对应了一组空闲区。
将倒数第二个盘块设为一个特殊值, 表示没有下一个空闲块了。
分配时, 检查一个分组的块数能否满足, 若某个盘块正好够, 则把这个盘块从超级块中删除, 并把指向下一分组的磁盘块的信息先与超级块设置一个指针, 这样可以保证下一个分组的链接信息保存在超级块中。