第四章 文件管理
6.文件实现
连续分配方式:逻辑上相邻的块在物理上也必须相邻,也必须是占有一组连续的块并且依然需要保持这些块之间的相对顺序。
在连续分配方式下为了实现逻辑块号到物理块号之间的映射关系,在文件的目录表中必须记录两个文件的属性,第一是文件存放的起始块号,第二是这个文件的长度,也就是它总共占用了多少个块。
采用连续分配方式,只要用户给出了自己想要访问的逻辑块号,操作系统就可以直接根据逻辑块号算出对应的物理块号是多少,因此连续分配方式是支持顺序访问和直接访问(即随机访问)【顺序访问是指如果要访问逻辑块号2的话,那么必须先顺序的访问逻辑块号0和逻辑块号1,之后才能找到逻辑块号2;直接访问(随机访问)是指如果要访问逻辑块号2的话,并不需要访问其他的块,可以直接找到逻辑块号2存放的位置。】 支持直接访问(随机访问)是连续分配方式一个最大的优点。
连续分配方式的缺点:此时文件A想要拓展,即想要再增加一个磁盘块的话,由于连续分配方式要求为一个文件分配的是连续的块,而文件A后面的相邻的块此时已经被别的文件所占有了,因此如果要拓展文件A,则不得不把文件A的数据整体的迁移到另外的连续的更大的区域中,而进行数据迁移的过程其实是需要耗费很大的开销的。所以得出结论,由于连续分配方式要求文件在磁盘上占有的必须是一组连续的块,因此对文件的拓展很不方便。
链接分配方式:采用离散分配的方式,可以为文件分配离散的磁盘块,然后用指针链接的方式,把这些磁盘块串联起来。链接分配又分为隐式链接和显式链接。
隐式链接:
如果采用隐式链接方式,则在目录项即FCB中记录文件的起始块号和结束块号,另外各个块之间都会用一定的存储空间存储指向下一块的链接指针,当然了最后一个块是没有指向下一个块的链接指针的,这些指针对用户来说是透明的。
隐式链接方式下实现逻辑块号到物理块号的转变:用户给出要访问的逻辑块号i,操作系统根据文件名找到对应的目录项(FCB),找到文件的起始块号,于是操作系统可以先读入文件的起始块,而这个块就是逻辑上的0号块,只有把这个块读入内存后才可以知道这个块指向下一块的指针数据是多少,只有这样才可以调入下一个块,于是下一个块在调入内存之后也可以再继续找到再下一个块存放的位置,于是再读入再下一个块,以此类推……。因此如果想要访问逻辑块号i,那么操作系统首先需要依次读入0号到i-1号逻辑块才可以找到i号块存放的位置,因此读入i号逻辑块总共需要i+1次磁盘I/O操作(每次磁盘I/O只能读入一个块)。由此可以得到结论:采用链接分配(隐式链接)方式,只支持顺序访问,不支持随机访问(也就是说想要访问i号逻辑块,那么只能顺序的访问0到i-1号块才可以),查找效率低。另外,指向下一个盘块的指针也需要耗费少量的存储空间。
显式链接:
磁盘中各个块的先后顺序都是统一记录在文件分配表FAT中的,所以一个磁盘有多少个块,在文件分配表中就相应的会有多少个表项。需要在文件aaa的目录项即FCB中需要记录它存放的起始块号,在文件分配表中会显式的记录文件aaa这几个磁盘块的链接关系,最后一个块的“下一块”这个位置可以设置为一个特殊的值,比如说“-1”用来表示这里就是这个文件的结尾。
一个磁盘仅用设置一张FAT,每个文件的盘块的先后顺序都是统一的存放在这张表中的。并且在开机的时候,FAT会被读入内存,并常驻内存。FAT的各个表项在物理上连续存储,且每一个表项长度相同,因此“物理块号”字段可以是隐含的。
显式链接方式下实现逻辑块号到物理块号的转变:如现在要访问文件aaa的2号逻辑块,那么操作系统首先找到aaa这个文件的0号逻辑块存放的物理块号是2,接下来操作系统会查询内存中的文件分配表FAT,找到0号逻辑块的下一个块即1号逻辑块是存放在5号物理块中的,接下来再接着找1号逻辑块的下一个块即2号逻辑块存放在0号物理块中,于是查询到这个位置便可知这个文件的2号逻辑块存放的物理块号了。 由于FAT常驻内存,因此查询FAT的过程并不需要读磁盘操作。 由此得到结论:采用显式链接方式的文件既支持顺序访问,也支持随机访问【访问i号逻辑块并不需要顺序的依次访问0到i-1号逻辑块,可以直接通过FAT表查到i号逻辑块存放的地址】。由于查询的过程并不需要访问磁盘,所以相比隐式链接,采用显式链接访问速度会快很多。
再次强调一个磁盘只会对应一张文件分配表FAT,并且在开机时文件分配表会读入内存并且常驻内存,因此查询文件分配表的过程并不需要读磁盘操作,所以造就了显式链接方式支持随机访问并且在地址转换过程中访问效率更高。
索引分配:
假设某个新创建的文件“aaa”的数据依次存放在2->5->13->9这几个磁盘块中,采用索引分配方式的话,系统会为aaa这个文件建立一张索引表,这个索引表记录了aaa的逻辑块和物理块之间一一对应的映射关系,如果aaa的索引表是存放在7号磁盘块中的,那么7号磁盘块就是aaa的索引块,而2,5,13,9是aaa的数据块。采用索引分配方式的文件需要在自己的目录项(FCB)中记录自己的索引块是哪一块,索引块中存放的是索引表,因此操作系统可以根据索引块的块号找到它的索引表,接下来就可以找到各个逻辑块对应的物理块号了。
索引分配方式下实现逻辑块号到物理块号的转变:用户要访问逻辑块号i,操作系统首先需要找到文件对应的目录项(FCB)从中找到这个文件的索引块的块号,再从这个索引块中读出这个文件的索引表的内容,接下来就只需要通过逻辑块号来查询索引表就可以找到某一个逻辑块号对应的物理块号了,这个查表的方式与内存管理中介绍的通过逻辑页号查询页表项的方式是类似的。所以从这个过程可以知道如果想要访问i号逻辑块,并不需要顺序的访问前面0到i-1号逻辑块,可以直接找到i号块存放的物理块号,因此索引分配这种方式是支持随机访问的。另外索引分配这种方式也很容易实现文件的拓展,比如aaa这个文件想要拓展(想要再分配一个磁盘块)则只需要随便找一个磁盘块将其分配个aaa,然后在aaa对应的索引表中增加相应的表项即可。缺点:索引分配方式的索引表需要占用一定的存储空间。
为了解决一个磁盘块装不下文件的整张索引表这个问题,有三种方案①链接方案②多层索引③混合索引。
①链接方案:如果一个文件的索引表太大,一个索引块装不下,那么可以为这个索引表分配多个索引块并且用链接的方式将它们链接起来。假设磁盘块大小为1KB,一个索引表项占4B,则一个磁盘块只能存放256个索引项,如果一个文件的大小超过了256块,则说明这个文件的索引表的索引项肯定也超过了256个。因此可以把这个索引表拆分,为这个文件分配多个索引块,每一个索引块当中存放256个索引项,并且在每个索引块当中用一定的空间存储指向下一个索引块的指针,这样就可以把很长的索引表拆分成不同的部分,并且用这种链接的方式把他们连起来了。如果采用链接方案的话文件的目录项(FCB)中只需要记录它的第一个索引块的块号,像文件aaa就只需要记录它的第一个索引块也就是7号块,假设现在要访问aaa的逻辑块号为256的逻辑块,为了查到256的逻辑块对应的物理块号,就肯定需要找到aaa的第二个索引块,而由于各个索引块之间是用链接的方式连起来的,所以为了找到第二个索引块的块号,操作系统首先需要把第一个索引块读入内存,然后才能根据这个索引块当中的指针找到第二个索引块的块号并且把第二个索引块读入内存,只有这样才能找到256这个逻辑块号对应的物理块号,因此可以看到如果用户要访问的逻辑块号对应的索引项是在索引表中的第二个部分,那么操作系统必须首先顺序的读取索引表的第一部分,之后才能找到索引表的第二部分。这个特点就造成了一个问题:假设一个文件的大小256*256KB=64MB,每个磁盘块的大小为1KB,这个文件需要占用256*256个磁盘块,那么需要对应同等数量的256*256个索引项,又每一个磁盘块中只能存放256个索引项,因此这么多的索引项需要用256个索引块存储,并且如果采用的是链接方案则这些索引块之间是用链接的方式把他们连起来的。所以如果一个用户想要访问这个文件的最后一个逻辑块,那么必须找到这个文件最后一个索引块(第256个索引块),通过前面文件aaa的例子可以知道如果要找到最后一个索引块的话,那么必须先依次读入前面的所有的索引块,所以可以看到如果采用链接方案的话那么为了找到文件某一个逻辑块对应的物理块号,光地址转换的过程就可能需要两百多次读磁盘操作,这显然是很低效的。这也是链接方案存在的最大的问题。
②多层索引:建立多层索引(原理类似于多级页表)。使第一层索引块指向第二层索引块。还可以根据文件大小的要求再建立第三层、第四层索引块。 接着刚才的例子分析:磁盘块大小为1KB,一个索引表项占4B,则一个磁盘块只能存放256个索引项,文件的大小是256*256个块,那么这个文件可以为他建立两层索引,第一层的索引块总共有256个索引项,第二层的索引块每一块也有256个索引项,然后每一个索引项再指向某一个数据块。在文件的目录项(FCB)中只需要记录它的顶级索引表存放的索引块号即可。
如果采用多层索引,各层索引表的大小不能超过一个磁盘块。根据这一规定在该例子中第一层的索引表最多只能有256个索引项,分别指向256个第二层的索引表;而第二层的索引表大小仍然不能超过一个磁盘块,所以第二层的索引表最多也只能有256个索引项,每个索引项又会分别指向一个数据块,每一个数据块的大小是1KB,因此如果采用两层索引那么文件的最大长度是64MB。【类似的计算文件的最大长度是常考题型。】在多层索引下实现逻辑块号到物理块号的转变:假设用户此时要访问的是1026号逻辑块,1026/256=4,说明1026号逻辑块对应的索引项在4号第二级索引表中存放的,而4号二级索引表存放的位置操作系统只需要从一级索引表中找到4号索引项对应的物理块就可以找到了,在读入了4号二级索引表之后,接下来应该查询4号二级索引表中的哪个表项呢?1026%256=2,说明此时需要查询的是4号二级索引表中的2号表项,找到2号表项之后就可以找到1026号逻辑块对应的物理块了。所以如果采用两层索引的方式,那么操作系统首先需要根据目录项(FCB)中的记录的顶级索引块的块号找到顶级索引表,把顶级索引表读入内存,查询相应的表项之后找到相应的二级索引表存放的物理块号,接下来再把二级索引表读入内存,最后再查询二级索引表就可以找到最终想要访问的逻辑块存放在哪个物理块中了,所以这整个过程访问目标数据块共进行了三次读磁盘操作,第一次是读入了一级索引表,第二次是读入了对应的二级索引表,第三次读入最终要访问的数据块。类似的如果采用三层索引,则文件的最大长度为256*256*256*1KB=16GB,同时,访问目标数据块,需要4次读磁盘操作。
结论:采用K层索引结构,并且顶级索引表的数据还没有调入内存,则根据一个逻辑块号访问一个数据块总共需要K+1次读磁盘操作才可以完成。
多层索引方式也存在一个问题:假如一个文件本来很小,它的数据块只有1KB这么大,但是这个文件如果在物理上采用两层索引结构,那么读入这个文件1KB的内容依然需要3次读磁盘操作(磁盘I/O),可不可以用某种方式解决这个问题呢?为此又提出了混合索引方案。
③混合索引:多种索引分配方式的结合。例如,一个文件的顶级索引表中,既包含直接地址索引(直接指向数据块),又包含一级简介索引(指向单层索引表)、还包含两级间接索引(指向两层索引表)。
直接地址索引是指这些索引项是直接指向了一个数据块,图中8个直接地址索引会对应8个数据块;一级间接索引会指向一个单层索引表,这个单层索引表的各个表项又会分别指向一个数据块,每个数据块1KB;二级间接索引会指向一个两层索引表,这个两层索引表的各个表项依次指向了一个下一级的索引表,而第二层索引表的各个表项又分别指向了一个数据块。所以如果有8个直接地址索引会分别指向8个数据块,一个一级间接索引会指向一个单层索引表,而每个索引表最多会有256个表项,因此这个单层索引表最多会对应256个数据块;相应的二级间接索引最多会指向256*256=65536个数据块,因此如果一个文件的混合索引的顶级索引表是这样的结构的话,那么这个文件的最大长度就是把这些所有数据块相加。【根据顶级索引表的结构计算文件的最大长度这种题型常考。】
下面分析访问某一个逻辑块需要几次读磁盘操作(磁盘I/O),假设此时顶级索引表还没有读入内存,如果要访问0~7号逻辑块,则需要两次读磁盘操作,第一次是读入顶级索引表,第二次是根据顶级索引表中某一个直接地址找到目标数据块存放的位置,再把目标数据块读入内存,所以总共需要两次读磁盘操作。如果要访问8~263号逻辑块那么总共需要三次读磁盘操作,第一次将顶级索引表读入内存,第二次是根据一级间接索引找到单层索引表,把单层索引表读入内存,第三次是根据单层索引表找到目标数据块存放的位置,然后把目标数据块读入内存,所以总共要3次读磁盘操作。要访问264~65799号逻辑块,总共需要4次读磁盘操作。 所以混合索引的一个好处就是,对于小文件来说,只需要占用前面的几个块就可以了,那么小文件的读取只需要访问较少的读磁盘次数【一般来说计算机中小文件是比较多的】。如此便解决了多层索引的那个小问题,在多层索引结构下,如两层索引,即便文件比较小,则依然需要三次读磁盘操作(在顶级索引表未调入内存的情况下),而混合索引便可以解决这个问题。