文件的定义:文件就是一组有意义的信息/数据集合。
文件的属性:包括文件名、标识符、类型、位置、大小、保护信息等。
文件内部的数据组织:分为无结构文件(流式文件)和有结构文件(记录式文件)。
文件之间的组织:系统中的各个文件通过一层一层的目录合理有序的组织起来。
操作系统向上提供的功能:create、delete、open、close、read、write系统调用。
文件在外存的存放:外存会分为一个个“块/磁盘块/物理块”,操作系统以“块”为单位为文件分配存储空间。
注:系统运行时,计算机以进程为基本单位进行资源的调度和分配;输入输出时,则以文件为基本单位。
“逻辑结构”就是在用户看来,文件内部的数据应该是如何组织起来的。
“物理结构”指的是在操作系统看来,文件的数据是如何存放在外存中的。
无结构文件是最简单的文件组织形式。
文件内部的数据就是一系列二进制流或字符流组成。又称“流式文件”。如:Windows操作系统中的.txt文件。
文件内部的数据其实就是一系列字符流,没有明显的结构特性。因此也不探讨无结构文件的“逻辑结构”问题。
由一组相似的记录组成,又称“记录式文件”。
每条记录又若干个数据项组成。一般来说,每条记录有一个数据项可作为关键字(作为识别不同记录的ID),如:数据库表文件。
根据各条记录的长度(占用的存储空间)是否相等,又可分为定长记录和可变长记录两种。
文件中的记录一个接一个地顺序排列(逻辑上),记录可以是定长的或可变长的。
顺序文件按记录的排列顺序可以分为串结构和顺序结构。
记录在物理上即可以顺序存储(逻辑上相邻的记录物理上也相邻)也可以链式存储(逻辑上相邻的记录物理上不一定相邻)。
只有定长记录的顺序文件且在物理上采用顺序存储,才可以实现随机存取,如果再采用顺序结构,则可实现快速检索(即根据关键字快速找到对应记录)。
注:试题中所说的“顺序文件”指的是物理上顺序存储的顺序文件。
很多应用场景中必须使用可变长记录,为了快速检索,可以采用索引文件快速找到第i个记录。
索引表本身是定长记录的顺序文件,可以快速找到第i个记录对应的索引项。
可将关键字作为索引号内容,若按关键字顺序排列,则还可以支持按照关键字折半查找。
每当要增加/删除一个记录时,需要对索引表进行修改。由于索引文件有很快的检索速度,因此主要用于对信息处理的及时性要求比较高的场合。
另外,可以用不同的数据项建立多个索引表。如:学生信息表中,可用关键字“学号”建立一张索引表。也可用“姓名”建立一张索引表。这样就可以根据“姓名”快速地检索文件了。
索引顺序文件是索引文件和顺序文件思想的结合。
索引顺序文件中,会为文件建立一张索引表,但并不是每个记录对应一个索引表项,而是一组记录对应一个索引表项。
上图案例中,学生记录按照学生姓名的开头字母进行分组。
每个分组就是一个顺序文件,分组内的记录不需要按关键字排序。
索引顺序文件的索引项也不需要按关键字顺序排列,这样可以极大地方便新表项的插入。
要为N个记录的文件建立K级索引,则最优的分组是每组N1/(K+1)个记录。检索一个记录的平均查找次数是((N1/(K+1) )/2) * (K+1)。
给定记录的键值或通过散列函数转换的键值直接决定记录的物理地址。散列文件具有很高的存取速度,但是会引起冲突,即不同关键字的散列函数值可能相同。
在Windows操作系统下文件目录即“文件夹”。
文件控制块
文件控制块(FCB)是用来存放控制文件需要的各种信息的数据结构,文件与FCB一一对应。
FCB中包含了文件的基本信息(文件名、物理地址、逻辑结构、物理结构等),存取控制信息(是否可读/可写、禁止访问的用户名单等),使用信息(如文件的建立时间、修改时间等)。
最重要,最基本的还是文件名、文件存放的物理地址。FCB实现了文件名和文件之间的映射。使用户(用户程序)可以实现按名存取。
文件目录
FCB的有序集合称为“文件目录”,一个FCB就是一个文件目录项。
目录文件
为了实现对文件目录的管理,通常将文件目录以文件的形式保持在外存,这个文件就叫目录文件。目录文件中的一条记录就是一个FCB。
索引节点(i节点,inode)是对FCB的改进。
在查找各级目录的过程中只需要用到“文件名”这个信息,只有文件名匹配时,才需要读出文件的其他信息。因此可以考虑让目录表“瘦身”来提升效率。
当找到文件名对应的目录项时,才需要将索引结点调入内存,索引结点中记录了文件的各种信息,包括文件在外存中的存放位置,根据“存放位置”即可找到文件。
存放在外存中的索引结点称为“磁盘索引结点”,当索引结点放入内存后称为“内存索引结点”。
磁盘索引节点
是指存放在磁盘上的索引节点。每个文件有一个唯一的磁盘索引节点,主要包括以下内容:
内存索引节点
是指存放在内存中的索引节点。当文件被打开时,要将磁盘索引节点复制到内存的索引节点中,便于以后使用。在内存索引节点中增加了以下内容:
早期操作系统并不支持多级目录,整个系统中只建立一张目录表,每个文件占一个目录项。
单级目录实现了”按名存取“,但不允许文件重名。
在创建一个文件时,需要先检查目录表中有没有重名文件,确定不重名后才能允许建立文件,并将新文件对应的目录项插入目录表中。
单级目录结构不适用于多用户操作系统。
早期的多用户操作系统,采用两级目录结构。分为主文件目录(MFD,Master File Directory)和用户文件目录(UFD,User Flie Directory)。
主文件目录记录用户名及相应用户目录的存放位置。用户文件目录由该用户所有文件的FCB组成。
两级目录结构允许不同用户的文件重名(文件名虽然相同,但是对应的其实是不同的文件),也可以在目录上实现实现访问限制(检查此时登录的用户名是否匹配)。
两级目录结构缺乏灵活性,用户不能对自己的文件进行分类。
又称树形目录结构,不同目录下的文件可以重名。
用户(或用户进程)要访问某个文件时要用文件路径名标识文件,文件路径名是个字符串。各级目录之间用“/”隔开。从根目录出发的路径称为绝对路径。例如:自拍.jpg的绝对路径是/照片/2015-08/自拍.jpg
当层次较多时,每次从根目录查询会浪费时间,于是可为每个进程设置一个当前目录(又称工作目录)。例如,此时已经打开了“照片”的目录文件,也就是说,这张目录表已调入内存,那么可以把它设置为“当前目录”。当用户想要访问某个文件时,可以使用从当前目录出发的相对路径。
在Linux中,“.”表示当前目录,因此如果“照片”是当前目录,则”自拍.jpg”的相对路径为:./2015-08/自拍.jpg
。
树形目录结构可以很方便地对文件进行分类,层次结构清晰,也能够更有效地进行文件的管理和保护;但是增加了磁盘访问次数。
树形结构不便于实现文件的共享。为此,提出了“无环图目录结构”。
在树形目录结构的基础上,增加一些指向同一节点的有向边,整个目录成为一个有向无环图,方便实现多个用户间的文件共享。
可以用不同的文件名指向同一个文件,甚至可以指向同一个目录(共享同一目录下的所有内容)。
需要为每个共享结点设置一个共享计数器,用于记录此时有多少个地方在共享该结点。用户提出删除结点的请求时,只是删除该用户的FCB,并使共享计数器减1,并不会直接删除共享结点。只有共享计数器减为0时,才删除结点。
无环图目录结构方便地实现了文件的共享,但使得系统的管理变得更加复杂。
注:当建立链接关系时,必须将被共享文件的物理地址(盘块号)复制到相应的目录。如果某个用户向该文件添加新数据,且
需要增加新盘块,那么这些新增的盘块只出现在执行操作的目录中,对其他共享用户是不可见的。(此问题的解决采用下文的索引节点)
文件共享使多个用户共享同一个文件,系统中只需保留该文件的一个副本。若系统不能提供共享功能,则每个需要该文件的用户都要有各自的副本,会造成对存储空间的极大浪费。
索引结点中设置一个链接计数变量count,用于表示链接到本索引结点上的用户目录项数。
若count = 2,说明此时有两个用户目录项链接到该索引结点上,或者说是有两个用户在共享此文件。
若某个用户决定“删除”该文件,则只是要把用户目录中与该文件对应的目录项删除,且索引结点的count值减1。
若count>0,说明还有别的用户要使用该文件,暂时不能把文件数据删除,否则会导致指针悬空。
当count = 0时系统负责删除文件。
注:引用计数值为1,说明有一个用户(文件主,即创建文件的用户)正在共享文件,但是不意味着文件被读入内存,只有文件被用户打开时才会读入内存。
当User3访问“ccc”时,操作系统判断文件“ccc”属于Link类型文件,于是会根据其中记录的路径(C:/User1/aaa
)层层查找目录,最终找到User1的目录表中的“aaa”表项,于是就找到了文件1的索引结点。
类似于Windows操作系统的“快捷方式”。
即使软链接指向的共享文件已经被删除,Link型文件依然存在,只是通过Link型文件中的路径去查找共享文件会失败。
由于用软链接的方式访问共享文件时要查询多级目录,会有多次磁盘I/O,因此用软链接访问共享文件的速度要比硬链接更慢。
文件的物理结构/文件分配方式主要探讨对非空闲磁盘块的管理(存放了文件数据的磁盘块)。
类似于内存分页,磁盘中的存储单元会被分为一个个块(磁盘块、物理块),磁盘块大小与内存块、页面的大小相同。
内存与磁盘之间的数据交换(即读/写操作、磁盘I/O)都是以“块”为单位进行的。即每次读入一块,或每次写出一块。
在内存管理中,进程的逻辑地址空间被分为一个一个页面。同样的,在外存管理中,为了方便对文件数据的管理,文件的逻辑地址空间也被分为了一个一个的文件块。文件的逻辑地址也可以表示为(逻辑块号,块内地址)的形式。
操作系统为文件分配存储空间都是以块为单位的,用户通过逻辑地址来操作自己的文件,操作系统负责实现从逻辑地址到物理地址的映射。
连续分配要求每个文件在磁盘上占用一组连续的块。
从(逻辑块号,块内地址)转换到(物理块号,块内地址)。只需转换块号,块内地址保持不变。
文件目录中记录了文件的起始块号和长度,用户给出访问的逻辑块号,操作系统需要找到文件的FCB,物理块号=起始块号+逻辑块号,可直接算出逻辑块号对应的物理块号。即,连续分配支持顺序访问和直接访问(即随机访问)。
优点:支持顺序访问和直接访问(即随机访问);连续分配的文件在顺序访问时速度最快。
缺点:不方便文件拓展;存储空间利用率低,会产生磁盘碎片。
链接分配采取离散分配的方式,可以为文件分配离散的磁盘块,分为隐式链接和显式链接两种。
除了文件的最后一个磁盘块之外,每个磁盘块中都会保存指向下一个盘块的指针,这些指针对用户是透明的。
读入i号逻辑块,总共需要i+1次磁盘I/O。
优点:很方便文件拓展,不会有碎片问题,外存利用率高。
缺点:只支持顺序访问,不支持随机访问,查找效率低,指向下一个盘块的指针也需要耗费少量的存储空间。
注:为了提高查找速度和减小指针所占用的存储空间,可以将几个盘块组成一个簇,按簇而不按块来分配(即一个文件所占用的空间只能是簇的整数倍),可以大幅地减少查找时间,也可以改善许多算法的磁盘访问时间。比如一簇为4块,这样,指针所占的磁盘空间比例也要小得多。这种方法的代价是增加了内部碎片。
把用于链接文件各物理块的指针显式地存放在一张表中。即文件分配表(FAT,File Allocation Table)。
一个磁盘仅设置一张FAT(FAT大小取决于磁盘大小)。开机时,将FAT读入内存,并常驻内存。FAT的各个表项在物理上连续存储,且每一个表项长度相同,因此“物理块号”字段可以是隐含的。
从目录项中找到起始块号,若i>0,则查询内存中的文件分配表FAT,往后找到i号逻辑块对应的物理块号。逻辑块号转换成物理块号的过程不需要读磁盘操作。
优点:很方便文件拓展,不会有碎片问题,外存利用率高,并且支持随机访问。相比于隐式链接来说,地址转换时不需要访问磁盘,因此文件的访问效率更高。
缺点:文件分配表的需要占用一定的存储空间。
注:试题中遇到未指明方式的链接分配,默认式隐式链接的链接分配。
索引分配允许文件离散地分配在各个磁盘块中,系统会为每个文件建立一张索引表(索引表大小取决于文件大小),索引表中记录了文件的各个逻辑块对应的物理块(类似于页表)。索引表存放的磁盘块称为索引块。文件数据存放的磁盘块称为数据块。
从目录项中可知索引表的存放位置,将索引表从外存读入内存,并查找索引表可找到i号逻辑块在外存中的存放位置。
索引分配方式可以支持随机访问。文件拓展也很容易实现(只需要给文件分配一个空闲块,并增加一个索引表项即可),但是索引表需要占用一定的存储空间。
若每个磁盘块1KB,一个索引表项4B,则一个磁盘块只能存放256个索引项。如果一个文件的大小超过了256块,那么一个磁盘块是装不下文件的整张索引表的。可采用链接方案、多层索引、混合索引来解决问题
如果索引表太大,一个索引块装不下,那么可以将多个索引块链接起来存放。
这种设计的缺陷是若想要访问文件的最后一个逻辑块,就必须找到最后一个索引块(第256个索引块),而各个索引块之间是用指针链接起来的,因此必须先顺序地读入前255个索引块。
建立多层索引(原理类似于多级页表)。使第一层索引块指向第二层的索引块。还可根据文件大小的要求再建立第三层、第四层索引块。
采用K层索引结构,且顶级索引表未调入内存,则访问一个数据块只需要K + 1次读磁盘操作。
多种索引分配方式的结合。
例如,一个文件的顶级索引表中,既包含直接地址索引(直接指向数据块),又包含一级间接索引(指向单层索引表)、还包含两级间接索引(指向两层索引表)。
若顶级索引表还没读入内存,访问0~7号逻辑块,两次读磁盘;访问8~263,三次读磁盘;访问264~ 65799,四次读磁盘。
对于小文件,只需较少的读磁盘次数就可以访问目标数据块。
为了提高对小文件的检索速度,在索引节点中可设置10个直接地址项,即用iaddr(0)~iaddr(9)来存放直接地址,即文件数据块的盘块号。假如每个盘块的大小为4KB,当文件不大于40KB时,便可直接从索引节点中读出该文件的全部盘块号。
对于中、大型文件,可利用索引节点中的地址项iaddr(10)来提供一次间接地址,即采用一级索引分配。
一次间接地址中记录了文件的一次间址块号,一次间址块就是索引块,其中记录了文件数据块的盘块号。一次间址块中可以存放1024个盘块号,可以表示1Kx4KB=4MB大小的文件。因此,同时采用直接地址和一次间址,允许的文件最大长度为4MB+40KB。
当文件长度大于4MB+40KB时,还需利用地址项iaddr(11)来提供二次间接地址,即采用两级索引分配。二次间接地址中记录了文件的主索引块号,主索引块中记录了文件的一次间址块号。当地址项iaddr(l1)作为二次间址块时,可以表示1Kx1Kx4KB=4GB大小的文件。
因此,同时采用直接地址、一次间址和二次间址时,允许的文件最大长度为4GB+4MB+40KB。
同理,同时采用直接地址、一次间址、二次间址和三次间址时,允许的文件最大长度为4TB+4GB+4MB+40KB。
注:
文件存储空间管理主要探讨对空闲磁盘块的管理。
适用于“连续分配方式”。
分配:与内存管理中的动态分区分配很类似,为一个文件分配连续的存储空间。同样可采用首次适应、最佳适应、最坏适应等算法来决定要为文件分配哪个区间。
回收:与内存管理中的动态分区分配很类似,当回收某个存储区时需要有四种情况,1)回收区的前后都没有相邻空闲区;2)回收区的前后都是空闲区;3)回收区前面是空闲区;4)回收区后面是空闲区。
适用于离散分配(较难得到连续的空间)的物理结构。为文件分配多个盘块时可能要重复多次操作。
操作系统保存着链头、链尾指针。
分配:若某文件申请K个盘块,则从链头开始依次摘下K个盘块分配,并修改空闲链的链头指针。
回收:回收的盘块依次挂到链尾,并修改空闲链的链尾指针。
离散分配、连续分配都适用。为一个文件分配多个盘块时效率更高。
操作系统保存着链头、链尾指针。
分配:若某文件申请K个盘块,则可以采用首次适应、最佳适应等算法,从链头开始检索,按照算法规则找到一个大小符合要求的空闲盘区,分配给文件。若没有合适的连续空闲块,也可以将不同盘区的盘块同时分配给一个文件,注意分配后可能要修改相应的链指针、盘区大小等数据。
回收:若回收区和某个空闲盘区相邻,则需要将回收区合并到空闲盘区中。若回收区没有和任何空闲区相邻,将回收区作为单独的一个空闲盘区挂到链尾。
连续分配、离散分配都适用。
每个二进制位对应一个盘块。在本例中,“0”代表盘块空闲,“1”代表盘块已分配。位示图一般用连续的“字”来表示,如本例中一个字的字长是16位,字中的每一位对应一个盘块。因此可以用(字号,位号)对应一个盘块号。当然有的题目中也描述为(行号,列号)。
(字号,位号)=(i, j)的二进制位对应的盘块号b = ni + j
b号盘块对应的字号i = b/n,位号j = b%n
注:注意盘块号、字号、位号到底是从0开始还是从1开始。
分配:若文件需要K个块,1)顺序扫描位示图,找到K个相邻或不相邻的“0”;2)根据字号、位号算出对应的盘块号,将相应盘块分配给文件;3)将相应位设置为“1”。
回收:1)根据回收的盘块号计算出对应的字号、位号;2)将相应二进制位设为“0”
空闲表法、空闲链表法不适用于大型文件系统,因为空闲表或空闲链表可能过大。UNIX系统中采用了成组链接法对磁盘空闲块进行管理。
文件卷的目录区中专门用一个磁盘块作为“超级块”,当系统启动时需要将超级块读入内存。并且要保证内存与外存中的“超级块”数据一致。
分配:
需要1个空闲块,1)检查第一个分组的块数是否足够。1<100,因此是足够的。2)分配第一个分组中的1个空闲块,并修改相应数据。
需要100个空闲块,1)检查第一个分组的块数是否足够。100=100,是足够的。2)分配第一个分组中的100个空闲块。但是由于300号块内存放了再下一组的信息,因此300号块的数据需要复制到超级块中。
回收:
假设每个分组最多为100个空闲块,此时第一个分组已有100个块,还要再回收一块。需要将超级块中的数据复制到新回收的块中,并修改超级块的内容,让新回收的块成为第一个分组。
进行Create系统调用时,需要提供的几个主要参数:
所需的外存空间大小(如:一个盘块,即1KB)
文件存放路径(“D:/Demo”)
文件名(这个地方默认为“新建文本文档.txt”)
操作系统在处理Create系统调用时,主要做了两件事:
进行Delete系统调用时,需要提供的几个主要参数:
操作系统在处理Delete系统调用时,主要做了几件事:
使用open系统调用“打开文件”,需要提供的几个主要参数:
操作系统在处理open系统调用时,主要做了几件事:
文件名不必是打开文件表的一部分,因为一旦完成对FCB在磁盘上的定位,系统就不再使用文件名。对于访问打开文件表的索引号,UNIX称之为文件描述符,而Windows称之为文件句柄。因此,只要文件未被关闭,所有文件操作都是通过文件描述符(而不是文件名)来进行。
注:只要完成了文件打开open系统调用,后面再使用read、write、Lseek、close等文件操作的系统调用,就不再使用文件名,而使用文件描述符。
进程使用完文件后,要“关闭文件”。操作系统在处理close系统调用时,主要做了几件事:
注:关闭文件不意味着将文件数据写回磁盘,只是将文件当前的控制信息写回磁盘,写文件操作才会写回磁盘。
进程使用read系统调用完成写操作。需要指明是哪个文件(在支持“打开文件”操作的系统中,只需要提供文件在打开文件表中的索引号即可),还需要指明要读入多少数据(如:读入1KB)、指明读入的数据要放在内存中的什么位置。
主要次序:
操作系统在处理read系统调用时,会从读指针指向的外存中,将用户指定大小的数据读入用户指定的内存区域中。
进程使用write系统调用完成写操作,需要指明是哪个文件(在支持“打开文件”操作的系统中,只需要提供文件在打开文件表中的索引号即可),还需要指明要写出多少数据(如:写出1KB)、写回外存的数据放在内存中的什么位置。
操作系统在处理write系统调用时,会从用户指定的内存区域中,将指定大小的数据写回写指针指向的外存。
为文件设置一个“口令”,用户想要访问文件时需要提供口令,由系统验证口令是否正确。
实现开销小,但”口令”一般存放在FCB或索引结点中(也就是存放在系统中)因此不太安全。
用一个“密码"对文件加密,用户想要访问文件时,需要提供相同的”密码”才能正确的解密。
安全性高,但加密/解密需要耗费一定的时间(Eg:异或加密)。
用一个访问控制表(ACL)记录各个用户(或各组用户)对文件的访问权限。
对文件的访问类型可以分为:读/写/执行/删除/列表清单等。
实现灵活,可以实现复杂的文件保护功能。
注1:如果对某个目录进行了访问权限的控制,那也要对目录下的所有文件进行相同的访问权限控制。
注2:访问权限是文件所持有的,即某文件的访问权限表能够说明所有用户具有的所有权限情况。
用一个例子来辅助记忆文件系统的层次结构:
假设某用户请求删除文件“D:/工作目录/学生信息.xlsx”的最后100条记录。
虚拟文件系统的特点:
注:vnode只存在于主存中,而inode既会被调入主存,也会在外存中存储。
文件系统挂载(mounting),即文件系统安装/装载。
文件系统挂载要做的事:
内存中的挂载表(mount table)包含每个文件系统的相关信息,包括文件系统类型、容量大小等。