大体框架:


文件属性、文件分类只需要略微记忆即可,稍微注意下文件分类中的形式

注意的是,文件名只在open系统调用的时候使用,后续都只使用其标识符


无结构文件别名“流式文件”,有结构文件别名“记录式文件”


文件控制块FCB包含控制文件的一系列信息,类似于PCB进程管理块

FCB的有序集合被称为文件目录,简单来说就是FCB1、FCB2、FCB3、FCB4…这样一串

一个FCB自然就可以被称作是一个文件目录项,通常由FCB组成的文件目录也视作文件

FCB自然有其的一些特有信息,书上有,重视下创建时需要为文件创建其FCB

理解上可以用FCB列表来理解,一个个列下去


FCB是目录文件中的内容,其中包含了文件所在的物理地址等信息

打开文件表中包含的是该文件的表项,也可以说就是FCB,从磁盘调入内存,方便使用

注意区分:

打开文件表存在的目的是方便一个打开文件的写、读、删等操作的实现,不必再去找磁盘

放在内存里,就不必每次都去文件目录里检索这个文件的目录项在哪

目录项和索引结点分离的目的是,减少检索时的开销,加快检索的速度,要区分开

简单来说就是把检索所需要的信息单独列出来,这样检索就可以更加高效

索引结点和一般文件表项结构上:

FCB文件目录—FCB—文件

文件名表项—索引结点—文件

打开文件表:

进程打开文件表—系统打开文件表—FCB—文件


索引结点是FCB的改进版,将文件名和FCB的其他信息相互分离

此时文件目录只包含文件名(此文件名是对应的用户看到的文件名)和索引号

目的是为了减少检索时不必要的调用开支,因为每次检索文件名都需要调入文件目录

采用FCB的目录,明显冗余信息过多,因而单独提取使用的文件名来检索

索引结点内存放其他信息,可以分为磁盘索引结点和内存索引结点,分别包含一些内容

需要注意的是,内存索引结点并非将文件调入,而是将其类FCB的索引调入方便后续查询

内存索引增加的内容基本都是为了进程的存在而服务的


文件操作:

在书上的程度进行一些补充说明,首先其均是操作系统提供的系统调用

创建过程中,一方面是分配外存,另一方面是创建一个FCB//索引结点//文件目录项

删除需要先查,再删除目录项//索引结点//FCB,同时释放内/外存中该文件占用的空间

整个系统只有一张系统打开表,每个进程都有自己的打开文件表用于存储私有信息

当一个文件处于打开的状态,为了减少每次读、写的检索时间,才采用了打开表

这样就能实现保存了该文件的外存地址,不必每次都去外部检索该地址,只需要访该地址

同时只要已经被其他进程使用过即已经检索过,其他进程也可以直接指向系统表即可

不必主动再次检索,系统表内不会新增,只会在进程表内新增

打开的过程:

1.根据文件存放路径找到相应的目录文件,从目录中找到文件名对应的的目录项

并检查该用户是否有指定的操作权限(操作权限存放在文件目录项中

2.将目录项复制到内存中的“打开文件表”中,并将对应表目的编号返回给用户

之后用户使用打开文件表的编号来指明要操作的文件

对于关闭文件,设置了一个计数器,只有当计数器完全为零是才允许删除,存于系统表中

读、写细节补充,课后习题里面也有:


文件保护补充:

口令密码需要和日常区分,没有对访问类型控制进行任何的限制,只限制了访问

口令就是字母意思,密码是因为只需要解码、译码所以比较费时间

精简的访问控制“精简”在了对用户的处理,以“组”进行分配,将庞大用户群分为组

按组分配访问控制权限


文件逻辑结构、物理结构以及混淆点总结:

无结构文件也被称为“流式文件”,关于定长、不定长的补充说明

三种文件模式


索引号一般都是可以默认不需要显示存储的,因为索引表一般是顺序连续存储的

例如变长顺序索引文件中的文件表,其索引号就是可以不需要显式的

显示链接分配中的FAT文件分配表中的盘块号也是可以隐含的,因为通过数组来实现的

一般通过数组来实现的,其就可以以A[0]直接取即可,其顺序号可以直接隐含

自然,顺序文件的读取也是可以直接隐含读取的,因为本就是数组结构


顺序文件:

然后这边看到边上的字,这边说的是,考试中说的顺序存储指代的是物理上顺序

需要注意的是,这边的链式存储、顺序存储均是逻辑意义上的存储方式

例如,不论数据结构到底是链表还是顺序表,他们都采用分配在一块连续逻辑区域数组上

顺序表各个数据项可以通过数组下标访问其下一项、支持随机访问、次序访问

但链表只能是单向走的,为了得到下一项,必须要遍历前N项,因而说是不可随机访问

数组在逻辑上也是连续的,所以这一切都只是逻辑上的分配、处理

因而他们和物理上的存储手段几乎是无关的,物理上要处理的是数组怎么存储的事

这一节这部分内容所说的“地址”也均是逻辑地址,而非物理地址,有困扰

单纯在顺序文件中,可变长记录只能一个个访问,但后续的两种策略,其就可以随机访问

定长均是可以随机访问的,无非在随机访问的基础上,如果实现了顺序结构那么可快查

顺序文件顺序存储顺序结构,妙啊,顺序文件也有其适用范围,书上有写


索引文件,就是为了满足顺序文件变长记录的随机访问的,为其增加一个索引表

附:索引表可以不唯一

为每一项变长记录增加一个索引表,相当于用一个新数组,A[0]代表第0个记录的地址

把变长变成了定长,但需要明白的是,这个索引表也是占内存的,这些和表目录暂时无关

单纯是逻辑上对文件的实现,这些都属于逻辑结构,是属于数据结构,和底层无关


索引顺序文件:

将“变长记录顺序文件”中的所有记录分为多个组,一组记录对应一个索引表项目

例如And/Anaconda/Anti/Auto分为一个组,But/Because/Become/Bit分为一个组

组内可以无序排序,但一个组内的分组依据必须要有,类似于以某个字母开头

或者也可以采用以某个字母结尾,在实际数据库应用里面还可以采用以学号等作分组依据

组间也可以随意排序,反正是顺序查找,组之间的排序依靠一个“代表”实现区分

代表即为这个组的首元,例如A组就是And,B组就是But


索引表可以按关键词排序,也可以不排序,如果按关键词排序那么就是顺序文件顺序结构

可以折半查找


相对前两者而言,检索次数总体减少了很多,这个要会计算

同时多级索引文件也需要会计算其查找次数,其平均查找次数课后综合题有写,建议观摩


哈希,这边的地址说的是逻辑地址,不是物理地址,可支持随机查找


连续分配、链接分配、索引分配(单级、多级、混合)均是对上述“数组”的存储方式

也就是上层采用的无论是什么模式,说白都离不开一块抽象连续的存储区域“数组”实现

下层需要实现的就是对数组具体在内存中怎么存储的实现,这里就需要联系到表目录

不同的实现方式,表目录是不同的,因为涉及到实际的内存分配


磁盘一般采用“块”的方式分配,和主存的块一般大小一致,一般都会有内部碎片存在

簇的思想是把多个块当作一个块来处理


连续分配就是单纯的,分配一块物理上连续的区域

这时候每个文件的表目录项只有文件名、起始位和最终结束位,一般默认采用了索引结点

毕竟FCB本就是落后淘汰的选择

这时候说的访问地址,一般就是直接在说物理地址了,可以随机访问、可以顺序访问

至于优缺点,这里只对缺点稍微谈一下:

连续内存空间的要求比较高,有时候甚至都不能完成,即使磁盘空间足够的情况下

例如空闲有1、1、1,需要3个连续空间,即使磁盘空间够,也无法分配,分配了就有外碎

在文件修改的时候,是需要物理上移动的,例如拓展如果空间不够就需要物理向后移动


链接分配,分为隐/显两种分配方式,两种均对应离散型磁盘空间分配法,有空间就可装


隐式的目录组成是文件名、开始项指针、结束项指针,其实就是单链表的物理存储逻辑

内部每个磁盘项都会有向后一块的指针,用户必然不可见,因为是底层的

用户可见的是其自己设置的链式存储方式中,这个链表指针是可见的,和这个不同

缺点、优点见书,对簇进行一些解释

簇实际上相当于扩大了块的大小,仅此而已,把多个块当作一个块

因而分配磁盘的时候,一个文件也是分配一个簇,而不是以块,那么浪费就会增加

其访问是需要多次磁盘I/O的,因为总体是一个链表,只能顺序去访问

一个1KB的文件,占用4个4KB的块组成的簇,内部碎片大量增加自然能理解


显式的特征就是Fat文件分配表,整个磁盘里面只会有一个,并且加载后一直驻留内存

此时的文件目录只有文件名、起始块号,而Fat表内存储相应该块号的下一块是哪块

由于这个表是驻留在内存里面的,所以未来的查询只需要访问这个表,不需要访问磁盘

这个算是比较重要的点吧,毕竟磁盘的访问变成了访存的次数,两个都可以作考点

相对而言访问磁盘的数量肯定大幅度减少了

其支持顺序访问、随机访问,物理层面上的,内存中的随机访问


相对而言上述两种方式都是物理上有一定联系在,例如链式物理间用链连起来

但索引就真正没关系了,物理存储可以随意,具体的索引顺序,即数据顺序可以任意改变

因为只需要在内存中的索引表中修改即可,完全不会涉及物理上

例如插入、删除,隐式链式是比较麻烦的,需要去反复找到其前驱并且读、写磁盘

而索引表就比较方便,只需要改变一下索引表的结构,稍微移动添加/减少就行

显示链式也比较方便,只需要改变内存中Fat就行,不需要回到原先的物理结构去修改


索引式:

在上述Fat思想下的进一步优化,只调入Fat表内的该文件对应的盘号而别的不需要调入

Fat是整个磁盘只有一个,因而代表其含有所有磁盘的对应下一块记录

因而占用内存也会较大并且还需要长时间驻留内存,但实际上用到的并没那么多

索引式将文件和其相应的索引块打包,当需要调用文件的时候,再把索引块给扔到内存

也就是一个文件对应一个索引表,这一点要注意,但索引块具体多少个,需要看情况

平时磁盘是需要存储这个索引块的

有时候也会把这个索引块称作是一个索引表,问题不大


当单级无法满足整个文件的索引时,就需要想办法拓展,而采用链肯定是最不明智的

因为链导致了各个索引块之间变成了链式,只能顺序访问,必然导致效率下降

所以就考虑使用多级索引,而多级索引对小文件不友好,因而产生了混合索引法


逻辑地址随机访问:定长顺序、变长索引、散列哈希

物理地址随机访问:连续分配、索引分配、显示链接分配

仅连续分配是固定连续区域分配,其余均是离散型分配


下面的内容总体总结不深,后续会进一步完善


关于计算:

多层索引的访磁盘次数,计算访问表项

需要注意的是,每次的查询表项都需要先把这个表给读入内存,再去查找

尤其要注意“顶级索引表”是否调入内存,统考常考,磁盘I/O也会因此有些变化

随机访问的逻辑号计算:

可以参考页表那边的计算,比较类似,16i/块大小=块号,16i%块大小=块内地址


混合索引计算,只需要明确直接、多级之间的关系即可

需要注意的是,其到底声明的是查询第XX块,还是查询前XX块,两者访磁盘次数不一样

因为文件是分离存储的,所以一旦要访问前XX块,那么就要加上前面的访问

例如本例中访问整个文件就需要10次磁盘I/O,因为每一节都需要考虑在内去访问


对多级索引的补充:

顶级表是否调入内存,影响的是其下一级表是否可以直接获得

如果顶级表没有调入内存,那么需要调入顶级表,然后才能根据表项得到下级表位置

从而从磁盘该位置把下级表引入内存

对于该步骤的一些补充和说明,以课后P289为例子

这里比较特殊,说的是“根目录第一块常驻内存”,而usr表项在第二块内…

所以需要先读入root表第二个块,读到对应的usr表项,一次I/O

再根据表项读入usr表,一次I/O

读入usr表项第一个块,这里面没有所需表项,但只能一个个查询去读,一次I/O

读入usr表项第二个块中所需表项you表,一次I/O

根据you表项读入dir表,一次I/O

这时候就已经能够从dir表中读到A的物理地址起始了,就算已经找到A了

其实个人觉得是比较容易混淆或者少数几次的,确实有点麻烦

理解上面可以从间接寻址理解,先读入地址,再根据地址读入这个表,再读表内地址

根据磁盘对应地址读入表,再根据表项的磁盘对应地址读入表,一个循环


另外就是,顶级表–>一级表—>二级表—>三级表—>数据块,容易混淆,有笔记在

P291