第四章 文件管理
文章目录
本章需要着重区分逻辑结构和物理结构
4.1 文件系统基础
4.1.1 文件的基本概念
文件是以硬盘为载体的,存储在计算机上的信息集合。在系统运行时,计算机以进程为基本单位进行资源的调度和分配,在用户进行输入输出中,是以文件为基本单位的。这时候操作系统就需要一个文件系统来实现文件的管理需求
首先了解文件的结构,通过自底向上的方式来定义:
- 数据项:是文件系统中最低级的数据组织方式,可以分为以下两种类型
- 基本数据项:用于描述一个对象的某种属性的值,是最小的数据单位
- 组合数据项:由多个基本数据项组成
- 记录:是一组相关的数据项集合,用于描述一个对象在某方面的属性
- 文件:是指创建者定义的、具有文件名的一组相关元素的集合。文件分为有结构文件和无结构文件,其中有结构文件由若干个记录组成,而无结构文件则视为一个字符流。
4.1.2 文件控制块和索引节点
1.文件的属性
就平时那些创建者、名称、大小等之类的
2.文件控制块
文件控制块(FCB)是用来存放控制文件需要的各种信息的数据结构。FCB的有序集合称之为文件目录。创建一个新文件,系统将会分配一个FCB并且存放在目录中,称为目录项。
FCB主要包含以下信息:
基本信息:比如文件名、文件物理地址、文件逻辑结构
存取控制信息:包括文件主的存取权限等信息
使用信息:比如文件创建的时间,上次修改时间等
3.索引节点
在检索文件的时候,其实只需要使用到文件名,其余信息不需要用到。因此有的系统比如UNIX会采用文件名和文件描述分开的方法,从而节省系统索引文件开销。称之为i节点,索引节点是一个指针,指向后继的文件信息。
文件名 | 索引节点 |
---|
-
磁盘索引节点
存放在磁盘上的索引节点
-
内存索引节点
存放在内存中的索引节点。当文件被打开之后,将磁盘索引节点复制到内存的索引节点中,便于以后使用
4.1.3 文件的操作
1.文件的基本操作
创建、读写、重定位等
2.文件的打开和关闭
当用户访问文件的时候,总是从检索文件开始,为了避免多次重复检索目录,大多数操作系统会要求文件使用前要用open函数打开文件。
而操作系统会维护一个打开文件表,用于保存最近打开的文件,调用open后,会将open的文件的属性从外存复制到打开文件表中。当用户再次需要访问这个文件的时候,可以直接访问打开文件表,从而避免一个文件多次重复检索目录。每个进程也会维护一个进程级的打开文件表,用于保存当前进程打开的文件的记录。而系统级打开文件表中还有个打开次数的表项,用于记录有多少个进程打开了该文件。
关闭时将进程打开文件表删除表项,然后系统打开文件表-1
读文件:使用read系统操作,需要指明哪个文件和读入多少数据,读入的数据放置在内存的哪个位置
写文件:将更改过的文件数据写回外存,也就是调用write系统调用。需要指明是哪个文件,以及需要写出的数据是多少,放在哪个位置。
每个打开文件都具有如下的关联信息:
- 文件指针:跟踪上次读写位置作为当前文件位置的指针,这种指针对于打开文件来说是唯一的
- 文件打开计数:用于在最后一个进程使用文件完毕后删除打开文件条目。
- 文件磁盘位置
- 访问权限
4.1.4 文件保护
1.访问类型
读、写、执行、添加、删除、列表清单。
2.访问控制
访问控制最常见的方法就是根据用户身份进行控制,现实中给予身份访问最基本的方法为,为每个文件和目录增加 一个访问控制表(ACL),以规定每个用户可以进行的操作。优点是可实现复杂的访问控制,缺点是表大小可能很大。因此可以使用精简的访问控制表来解决这个问题。精简访问控制表有三个表项:
- 拥有者:创建文件的用户
- 组:一组需要共享文件而且有类似访问权的用户
- 其他:系统中的其他用户
另外还有两种访问控制方法:
- 口令保护:使用口令密码保护文件,口令会存储在文件的FCB中,如果要访问则必须提供正确的口令。但是一旦操作系统遭到入侵,FCB中的口令就可能遭到窃取,因此口令保护易于实现,但是安全性不高
- 密码保护:密码会对文件进行加密,文件内容会根据密码进行加密,文件访问的时候需要提供密码进行解密,如果密码错误,则读出的信息是错误的。这种方法安全性高,密码不存储于计算机的任何部分,但是加密和解密需要一定的额外开销。
4.1.5 文件的逻辑结构
文件的逻辑结构指的是文件内部,数据逻辑上是如何组织起来的。
按照逻辑结构,文件可以分为以下两种:
1.无结构文件(流式文件)
最简单的文件组织形式。无结构文件的数据按照顺序结构组织成记录,并且以字节(Byte)为单位。文件没有结构,因此对于记录的访问只能顺序存储。一般对基本信息单位操作不多的文件适合使用无结构方式,比如源程序文件、目标代码文件。
2.有结构文件(记录式文件)
(1)顺序文件
- 链式存储:都无法实现随机存取,每次只能从第一个记录开始查找
- 顺序存储
- 可变长记录:无法实现随机存取,只能从第一个记录开始依次往后查找。因为需要一个记录表来记录可变长的内容,因此无法随机存取出元素。
- 定长记录:
- 可实现随机存取
- 采用串结构则无法快速找到某关键字对应的记录,因为串结构记录之间顺序与关键字无关
- 采用顺序结构可以快速找到某个关键字对应记录(比如折半查找)
(2)索引文件
索引号 | 长度m | 指针ptr |
---|
索引文件本身是一张定长记录的顺序文件,该文件中存储着索引号、对应的变长记录的长度以及该记录的物理地址。
通过索引文件,可以将变长记录的顺序检索转变成对定长记录索引文件的随机检索,因此可以大大提升对变长记录的处理效率。此外,索引文件的记录存储可以是离散的,不要求连续存储,因此也提高了空间利用率。但是索引文件本身是必须连续的。索引文件可以用于对信息处理及时性要求高的场合
(3)索引顺序文件
索引顺序文件是索引文件和顺序文件的结合,传统索引文件在文件项多的情况下会出现非常庞大的索引表,导致空间效能不好;一旦索引的目标文件都是小文件,也会导致索引表比文件本身还大。因此可以将若干个记录划分为一个组,每个组对应一个索引号。在访问记录的时候,先通过索引表定位到所在的组,再在组内进行顺序查找实现精确定位。
文件的物理结构
在外存中,文件的逻辑地址也被分为了一个个文件块,一般块大小和内存的页大小一致。文件的逻辑地址也可以表示为逻辑块号、块内地址
1.连续分配
连续分配方式要求每个文件在磁盘上占有一组连续的块
物理块号=逻辑块号+起始块号
支持顺序访问和随机访问。如果使用的是机械硬盘,因为顺序存储在物理位置上存储也是临近的,所以机械硬盘的磁头不需要移动太远,存取速度快。
缺点在于:
- 如果需要在中间插入数据,会要将后续的数据整体后移,所以最好是使用定长文件,然而有时候文件需要的大小又不确定,灵活性差
- 磁盘中的小块的存储空间会难以利用,使得存储空间利用效率低。
2.链接分配
采用离散分配方式,可为文件分配离散磁盘块。消除了文件的外部碎片,提高了磁盘利用率,可以动态分配磁盘块。由于链式结构的特性,数据的删除和插入都十分方便,可分为隐式链接和显式链接两种
- 隐式链接的目录项中还有文件第一块数据的指针和最后一块数据的指针,每一块数据中都含有指向下一块数据的指针,使用链式结构组织起一个文件。缺点在于只可以顺序访问,随机访问效率很低,而且一旦一个数据块损坏了,就会导致指针丢失而无法继续向下推进。
解决办法一般是将几个块组成簇(Cluster),可以在大范围查找上使用链式结构,而在小范围精确查找的时候使用顺序结构。 - 显式链接是指把用于连接文件各个物理块的指针存储到内存中的一张称为文件分配表FAT的表中,每个表项中储存着该表项的盘块号和下一块的地址指针。假设文件a的FCB中指出其起始地址为块3,而在文件分配表中块3的下一块是8,块8的下一块是块1,块1没有下一块,那么从文件a的FCB中读出起始地址后,按照块3——块8——块1的顺序将文件完全读取。
FAT中使用-1表示当前文件的下一块,-2表示为空块。在分配空间的时候,OS只需要找到FAT中-2的块就可以进行分配。FAT在系统启动的时候就会被读入内存,因此查找记录的操作是在内存中进行的,这就显著提高了检索速度,减少了访问磁盘的次数。
3.索引分配
链接分配也存在一些问题,比如链接分配不能有效支持随机访问(FAT除外),而FAT又会占用比较大的内存空间。事实上在打开某个文件的时候,并不需要将整个FAT调入内存,只需要调入本文件的对应盘的块号就可以了。
索引分配允许文件离散分配在各个磁盘块中,系统为每一个文件创建一张索引表。表中记录了文件的各个逻辑块在哪个物理块上。索引表存放的磁盘块称为索引块,文件数据存放的块称为数据块,索引块中存放着所有文件的索引表。实际使用时,首先在索引块中找到文件对应的索引表,再在索引表中寻找文件具体数据的位置
索引块内部结构
文件名 | … | 索引块 |
---|---|---|
aaa | … | 7 |
索引表内部结构
逻辑块号 | 物理块号 |
---|---|
0 | 2 |
1 | 5 |
2 | 13 |
索引分配优点在于支持直接访问和随机访问,没有外部碎片问题,并且十分容易拓展空间。缺点是索引块的分配增加了系统开销。如果是大文件,索引块的大小会难以接受,有以下几种解决方案:
- 链接方案:将一个大索引表拆分为若干个小索引表,使用链式结构将他们连接起来。如果一个磁盘块大小为1KB,每条记录信息为4B,那么按照一般的索引分配最多只支持有256个物理块的文件。而使用链接方案则没有支持上限。但是链接方案只能顺序读取各个索引块,一旦索引表项多了时间开销会很可观
- 多层索引:将索引块分级,一级索引块指向二级索引,二级索引指向文件块的位置。也可以建立更多级的索引。采用k层索引结构,并且顶级索引表未调入内存,则访问一个数据块需要k+1次读磁盘
- 混合索引:上述两种合二为一
常考题型:
1.给出磁盘块大小、索引表项大小等信息,求文件最大长度
2.给出逻辑块求出应该查找的表项的位置
文件系统的层次结构
4.2 目录
FCB的有序集合称为目录,一个FCB就是一个文件目录项,比如在我的电脑里面的一条条的目录信息。
目录的基本概念
从用户的角度看,目录需要在用户所需的文件名和文件之间建立一种映射关系。实现按名存取,并且需要提高检索速度,在多用户系统中让用户互斥的访问文件
目录结构
1.单级目录结构
访问一个文件的时候,先按照文件名在目录中查找到相应的FCB,经过合法性检查后进行相应操作
在整个文件系统中只有一张目录表,每个文件占用一个目录项。
新建文件的时候,需要遍历表确定没有重名文件,并且删除文件的时候也需要遍历表找到文件才进行删除。单级目录实现了最基本的按名存取,但是效率低下,文件不允许重名,并且不适用多用户
2.两级目录结构
该方法将目录分为主文件目录和用户文件目录两级,主文件目录项纪录用户名以及用户文件目录所在的位置。用户文件目录记录了该用户文件的FCB信息,某用户准备访问文件的时候,只需要搜索该用户对应的用户文件目录。因此提高了检索速度,解决了重名问题,
3.树形目录结构
我们常用的windows操作系统采用的就是树形文件系统。树形结构可以方便对文件进行分类,也可以高效地进行文件的管理和保护。有绝对路径和相对路径两种,采用相对路径可以便于文件移动,并且减少IO次数。但是树形目录结构不便于文件共享
4.无环图目录结构
可以使用不同的文件名指向同一文件,使用不同的路径名到达同一目录,虽然使得共享变得容易,但是系统管理难度上升了。
索引节点
使用文件名搜索文件的时候,不需要关心文件名外的其他信息,因此目录表中只有文件名和索引节点指针,指针指向目录项的其他信息,因此在目录表中存储的信息大大减少了,在一个磁盘块中能放入的目录项也增加了,因此提高了索引速度。
目录的操作
常见的那些删除新增,是人都会
文件共享
文件共享使得多个用户共享同一个文件,系统中只保存该文件的一个副本。如果操作系统没有共享功能,那每个需要该文件的用户都要有副本,会对存储空间造成极大的浪费。
1.基于索引节点的共享方式(硬链接)
将共享文件或者子目录链接到两个或者多个用户的目录中,方便找到该文件。
在这种方式中,诸如文件的物理地址及其文件属性,不再放在目录中,而是放在索引节点中。在文件目录中只设置文件名以及指向相应索引节点的指针。同时,在链接中还有个计数器count,用于表示连接到本文件上的用户目录项数目。如果尚有其它用户在使用该文件,则无法执行诸如删除之类的操作。
2.利用符号链实现文件共享(软链接)
为了B用户可以共享B用户的一个文件F,可以由系统创建一种LINK文件,也取名为F,并且放入B的目录中。当B要访问文件F的时候,操作系统发现是访问LINK文件,则根据文件中的路径名去找A中的F,然后进行读,这种连接方法又称为符号链接。类似于windows中的快捷方式
4.3 文件系统
4.3.1 文件系统结构
用户/应用程序 |
---|
用户接口:向上层用户提供功能接口 |
文件目录系统:根据文件路径找到FCB或索引节点,所有和目录相关的均在这层 |
存取控制模块:用于完成文件保护相关功能 |
逻辑文件系统和文件信息缓冲区:将记录号转化为对应逻辑地址 |
物理文件系统:将文件逻辑地址转化为物理地址 |
辅助分配模块:负责文件存储空间管理|设备管理模块:和设备直接交互 |
4.3.2 文件系统布局
4.3.3 外存空闲空间管理
概念:
- 目录区:存放文件目录信息,用于存储磁盘空间管理信息的区域
- 文件区:存放文件数据
- 文件卷\逻辑卷\逻辑盘:将物理磁盘划分为一个个文件卷,在文件卷中划分目录区和文件区
空闲表法
空闲表结构
第一个空闲盘块号 | 空闲盘块数 |
---|
适用于连续分配方式,在需要将空闲块分配给其他文件时,可以采用之前学的首次适应、最佳适应、最坏适应等算法
回收磁盘块时,需要注意表项合并问题
空闲链表法
空闲盘块链:以盘块为单位组成一条空闲链
空闲盘区链:以盘区为单位组成一条空闲链
如何分配:申请k个盘块则从链头摘下k个盘块分配
如何回收:回收的盘挂在链尾并且改变指针
适合离散分配的方式
为一个文件分配多个盘块时,空闲盘区链分配更快
位视图法
使用0表示空闲,使用1表示不空闲
重点提醒:要通过字号和位号推算出盘块号,或者逆推
成组链接法
空闲表和空闲链表法在大型文件系统中会非常大,访问速度非常慢
在UNIX系统中采用的是成组链表法,这种方法结合了空闲表和空闲链表两种方法。
最后一个盘块指向的空闲块数量-1,因为有一个位置需要存储-1表示后续已经没有空闲盘块表。在分配空闲块时,需要将其信息复制到超级块中,以免信息丢失
空间回收:逐个向后检索直至有空闲的空闲块表,如果遍历到结尾尚未有空闲块表,则在头部新建一个空闲块表