是将其管理的程序和数据组织为一系列文件。
文件:是指具有文件名的若干相关元素的集合。
元素通常是记录,而记录又是一组有意义的数据项的集合。
数据项:最低级的数据组织形式。
基本数据项(原子数据 数据元素字段):用于描述一个对象的某种属性的字符集,是数据组织中可以命名的最小逻辑数据单位,它的命名往往与其属性一致。基本数据项除了数据名外,还应有数据类型,因为基本项仅是描述某个对象的属性,根据属性的不同,需要用不同的数据类型来描述。
组合数据项:由若干个基本数据项组成的,简称组项。
记录:一组相关数据项的集合,用于描述一个对象在某方面的属性,一个记录应包含哪些数据项,取决于需要描述对象的哪个方面。为了能惟一地标识一个记录,必须在一个记录的各个数据项中,确定出一个或几个数据项,把它们的集合称为关键字(key)。
文件:是指由创建者所定义的、具有文件名的一组相关元素的集合。文件在文件系统中是一个最大的数据单位,它描述了一个对象集。一个文件必须要有一个文件名,它通常是由一串 ASCII 码或(和)汉字构成的,名字的长度因系统不同而异。用户利用文件名来访问文件。在许多 OS 中都把文件类型作为扩展名缀在文件名的后面,在文件名和扩展名之间用“.” 号隔开。
有结构文件:文件由若干个相关记录组成;
无结构文件:被看成是一个字符流。
文件的物理位置:指示文件在哪一个设备上及在该设备的哪个位置的指针。
文件的建立时间:这是指文件最后一次的修改时间
文件类型
按用途分类:
系统文件:这是指由系统软件构成的文件。大多数的系统文件只允许用户调用,不允许用户去读,更不允许修改;有的系统文件不直接对用户开放。
用户文件:指由用户的源代码、目标文件、可执行文件或数据等所构成的文件。用户将这些文件委托给系统保管。
库文件:这是由标准子例程及常用的例程等所构成的文件。这类文件允许用户调用,但不允许修改。
按文件中数据的形式分类
源文件:这是指由源程序和数据构成的文件。通常由终端或输入设备输入的源程序和数据所形成的文件都属于源文件。它通常是由 ASCII 码或汉字所组成的。
目标文件:这是指把源程序经过相应语言的编译程序编译过,但尚未经过链接程序链接的目标代码所构成的文件。它属于二进制文件。通常使用的后缀名是“.obj”
可执行文件:这是指把编译后所产生的目标代码再经过链接程序链接后所形成的文件
按存取控制属性分类
只执行文件:该类文件只允许被核准的用户调用执行,既不允许读,更不允许写。
只读文件:该类文件只允许文件主及被核准的用户去读,但不允许写
读写文件:这是指允许文件主和被核准的用户去读或写的文件
按组织形式和处理方式分类
普通文件:由 ASCII 码或二进制码组成的字符文件。一般用户建立的源程序文件、 数据文件、目标代码文件及操作系统自身代码文件、库文件、实用程序文件等都是普通文件。它们通常存储在外存储设备上。
目录文件:由文件目录组成的,用来管理和实现文件系统功能的系统文件。通过目录文件可以对其它文件的信息进行检索。由于目录文件也是由字符序列构成,因此对其可进行与普通文件一样的种种文件操作
特殊文件:特指系统中的各类 I/O 设备。为了便于统一管理,系统将所有的输入/输出设备都视为文件,按文件方式提供给用户使用。如目录的检索、权限的验证等都与普通文件相似,只是对这些文件的操作是和设备驱动程序紧密相连的系统将这些操作转为对具体设备的操作。根据设备数据交换单位的不同,又可将特殊文件分为块设备文件和字符设备文件。
文件系统模型
文件管理系统管理的对象:文件(文件管理的直接对象)。
目录:为了方便用户对文件的存取和检索,在文件系统中必须配置目录,每个目录项中,必须含有文件名及该文件所在的物理地址(或指针)。对目录的组织和管理是方便用户和提高对文件存取速度的关键。
磁盘(磁带)存储空间:文件和目录必定占用存储空间,对这部分空间的有效管理,能提高外存的利用率和对文件的存取速度。
对对象操纵和管理的软件集合(文件管理系统的核心部分):对文件读和写的管理,对文件的共享与保护等功能 。
文件系统的接口
命令接口:这是指作为用户与文件系统交互的接口。 用户可通过键盘终端键入命令,取得文件系统的服务。
程序接口:这是指作为用户程序与文件系统的接口。用户程序可通过系统调用来取得文件系统的服务。
最基本的文件操作
创建文件:在创建一个新文件时,系统首先要为新文件分配必要的外存空间,并在文件系统的目录中,为之建立一个目录项。目录项中应记录新文件的文件名及其在外存的地址等属性。
删除文件:当已不再需要某文件时,可将它从文件系统中删除。在删除时,系统应先从目录中找到要删除文件的目录项,使之成为空项,然后回收该文件所占用的存储空间。
读文件:在读一个文件时,须在相应系统调用中给出文件名和应读入的内存目标地址。此时,系统要查找目录,找到指定的目录项,从中得到被读文件在外存中的位置。 在目录项中,还有一个指针用于对文件的读/写。
写文件:在写一个文件时,须在相应系统调用中给出该文件名及该文件在内存中的 (源)地址。须先查找目录,找到指定文件的目录项,再利用目录中的写指针进行写操作。
截断文件:如果一个文件的内容已经陈旧而需要全部更新时,一种方法是将此文件删除,再重新创建一个新文件。但如果文件名及其属性均无改变时,则可采取另一种所谓的截断文件的方法即将原有文件的长度设置为 0,或者说是放弃原有的文件内容。
设置文件的读/写位置:前述的文件读/写操作都只提供了对文件顺序存取的手段, 即每次都是从文件的始端读或写。设置文件读/写位置的操作,用于设置文件读/写指针的位置,以便每次读/写文件时不是从其始端而是从所设置的位置开始操作,能改顺序存取为随机存取。
文件的“打开”和“关闭”操作
为了避免多次重复地检索目录,在大多数 OS 中都引入了“打开”(open)这一文件系统调用,当用户第一次请求对某文件进行操作时,先利用 open 系统调用将该文件打开。 “打开”,是指系统将指名文件的属性(包括该文件在外存上的物理位置)从外存拷贝到内存打开文件表的一个表目中,并将该表目的编号(或称为索引)返回给用户。当用户再要求对该文件进行相应的操作时,便可利用系统所返回的索引号向系统提出操作请求。系统这时便可直接利用该索引号到打开文件表中去查找,从而避免了对该文件的再次检索。如果用户已不再需要对该文件实施相应的操作时,可利用“关闭”(close)系统调用来关闭此文件
OS 将会把该文件从打开文件表中的表目上删除掉。
其它文件操作
对文件属性进行操作,即允许用户直接设置和获得文件的属性
有关目录,如创建一个目录,删除一个目录,改变当前目录和工作目录等;
用于实现文件共享的系统调用和用于对文件系统进行操作的系统调用等。
从用户观点出发所观察到的文件组织形式,是用户可以直接处理的数据及其结构,它独立于文件的物理特性,又称为文件组织(File Organization)
文件的物理结构(文件的存储结构),指文件在外存上的存储组织形式。这不仅与存储介质的存储性能有关,而且与所采用的外存分配方式有关。
对文件逻辑结构的基本要求
能提高检索速度:在将大批记录组成文件时,应有利于提高检索记录的速度和效率。
便于修改:便于在文件中增加、删除和修改一个或多个记录。
降低文件的存储费用:即减少文件占用的存储空间,不要求大片的连续存储空间。
有结构文件:由一个以上的记录构成的文件,故又把它称为记录式文件; 每个记录都用于描述实体集中的一个实体,各记录有着相同或不同数目的数据项。
记录的长度可分为定长和不定长两类。
定长记录:指文件中所有记录的长度都是相同的,所有记录中的各数据项都处在记录中相同的位置,具有相同的顺序和长度。文件的长度用记录数目表示。对定长记录的处理方便、开销小,是较常用的一种记录格式,广泛用于数据处理中。
变长记录:这是指文件中各记录的长度不相同。产生变长记录的原因,可能是由于一个记录中所包含的数据项数目并不相同,也可能是数据项本身的长度不定,不论是哪一种,在处理前,每个记录的长度是可知的。
根据用户和系统管理上的需要,可采用多种方式来组织这些记录
顺序文件:这是由一系列记录按某种顺序排列所形成的文件。其中的记录通常是定长记录,能用较快的速度查找文件中的记录。
索引文件:当记录为可变长度时,通常为之建立一张索引表,并为每个记录设置一个表项,以加快对记录检索的速度。
索引顺序文件:它为文件建立一张索引表,为每一组记录中的第一个记录设置一个表项。
无结构文件(流式文件):由字符流构成的文件,其长度以字节为单位。 对流式文件的访问,则是采用读/写指针来指出下一个要访问的字符。可以把流式文件看做是记录式文件的一个特例。在 UNIX系统中,所有的文件都被看做是流式文件。
串结构:各记录之间的顺序与关键字无关。通常是由时间来决定,即按存入时间的先后排列,最先存入的记录作为第一个记录,依此类推。
顺序结构:指文件中的所有记录按关键字(词)排列。有更高的检索效率,可利用某种有效的查找算法来提高检索效率。
对顺序文件的读/写操作
顺序文件中的记录可以是定长的,也可以是变长的。
对于定长记录的顺序文件,如果已知当前记录的逻辑地址,便很容易确定下一个记录的逻辑地址。在读一个文件时, 可设置一个读指针 Rptr,令它指向下一个记录的首地址,每当读完一个记录时,便执行Rptr:=Rptr + L 操作,使之指向下一个记录的首地址,其中的 L 为记录长度。类似地,在写一个文件时,也应设置一个写指针Wptr,使之指向要写的记录的首地址。在每写完一个记录时,又须执行以下操作: Wptr:=Wptr + L 。
对于变长记录的顺序文件,在顺序读或写时的情况相似,但应分别为它们设置读或写指针,在每次读或写完一个记录后,须将读或写指针加上 Li。Li 是刚读或刚写完的记录的长度。
顺序文件的优缺点
顺序文件的最佳应用场合是在对诸记录进行批量存取时,即每次要读或写一大批记录时。此时顺序文件的存取效率是所有逻辑文件中最高的;此外,也只有顺序文件才能存储在磁带上,并能有效地工作。
在交互应用的场合,如果用户(程序)要求查找或修改单个记录,为此系统便要去逐个地查找诸记录。这时,顺序文件所表现出来的性能就可能很差,尤其是当文件较大时,情况更为严重。如果想增加或删除一个记录都比较困难。为了解决这一问题,可以为顺序文件配置一个运行记录文件(Log File),或称为事务文件(Transaction File)把试图增加删除或修改的信息记录于其中,规定每隔一定时间,将运行记录文件与主文件合并,产生一个按关键字排序的新文件。
对于变长记录较难实现直接存取,为了解决这一问题,可为变长记录文件建立一张索引表。对主文件中的每个记录,在索引表中设有一个相应的表项,用于记录该记录的长度L 及指向该记录的指针(指向该记录在逻辑地址空间的首址)。由于索引表是按记录键排序的,因此,索引表本身是一个定长记录的顺序文件,从而也就可以方便地实现直接存取。
在对索引文件进行检索时,首先是根据用户(程序)提供的关键字,并利用折半查找法去 检索索引表,从中找到相应的表项;再利用该表项中给出的指向记录的指针值,去访问所需的记录。当要向索引文件中增加一个新记录时,便须对索引表进行修改。由于索引文件可有较快的检索速度,故它主要用于对信息处理的及时性要求较高的场合,提高了存储费用。
具有多个索引表的索引文件
使用按关键字建立索引表的索引文件与顺序文件一样,都只能按该关键字进行检索,而实际应用情况往往是:不同的用户,为了不同的目的,希望能按不同的属性(或不同的关键字)来检索一条记录。为实现此要求,需要为顺序文件建立多个索引表,即为每一种可能成为检索条件的域(属性或关键字)都配置一张索引表。在每一个索引表中,都按相应的一种属性或关键字进行排序
要求为每一个文件分配一组相邻接的盘块。 一组盘块的地址定义了磁盘上的一段线性地址。通常,它们都位于一条磁道上,在进行读/写时,不必移动磁头,仅当访问到一条磁道的最后一个盘块后,才需要移到下一条磁道,连续地读/写多个盘块。
在采用连续分配方式时,可把逻辑文件中的记录顺序地存储到邻接的各物理盘块中,这样所形成的文件结构称为顺序文件结构,此时的物理文件称为顺序文件。 这种分配方式保证了逻辑文件中的记录顺序与存储器中文件占用盘块的顺序的一致性。
为使系统能找到文件存放的地址,应在目录项的“文件物理地址”字段中,记录该文件第一个记录所在的盘块号和文件长度。
如同内存的动态分区分配一样,随着文件建立时空间的分配和文件删除时空间的回收,将使磁盘空间被分割成许多小块,这些较小的连续区已难于用来存储文件,此即外存的碎片。同样,我们也可以利用紧凑的方法,将盘上所有的文件紧靠在一起,把所有的碎片拼接成一大片连续的存储空间。
可以运行一个再装配例程(repack routine) 由它将磁盘 A 上的大量文件拷贝到一张软盘 B 或几张软盘上,并释放原来的 A 盘,使之成为一个空闲盘。 然后再将软盘上的文件拷回 A 盘上。 这种方法能将含有多个文件的盘上的所有空闲盘块都集中在一起,从而消除了外部碎片。但为了将外存上的空闲空间进行一次紧凑,所花费的时间远比将内存紧凑一次所花费的时间多得多。
连续分配的主要优缺点
顺序访问容易:系统可从目录中找到该顺序文件所在的第一个盘块号,从此开始顺序地、逐个盘块地往下读/写。连续分配也支持直接存取。
顺序访问速度快:由连续分配所装入的文件,其所占用的盘块可能是位于一条或几条相邻的磁道上,磁头的移动距离最少。
要求有连续的存储空间:要为每一个文件分配一段连续的存储空间,便会产生出许多外部碎片,严重地降低了外存空间的利用率。如果是定期地利用紧凑方法来消除碎片,则又需花费大量的机器时间。
必须事先知道文件的长度:根据其大小,在存储空间中找出一块其大小足够的存储区,将文件装入。 对于那些动态增长的文件,由于开始时文件很小。在此情况下,即使事先知道文件的最终大小,在采用预分配存储空间的方法时,也将是很低效的,它使大量的存储空间长期地空闲着。
FAT 和 NTFS 技术所采用的文件分配方式基本上都是显式链接方法。
卷:可以支持将一个物理磁盘分成四个逻辑磁盘,每个逻辑磁盘就是一个卷(也称为分区),也就是说每个卷都是一个能够被单独格式化和使用的逻辑单元,供文件系统分配空间时使用。 一个卷中包含了文件系统信息、一组文件以及空闲空间。每个卷都专门划出一个单独区域来存放自己的目录和 FAT 表,以及自己的逻辑驱动器字母。因此,通常对于仅有一个硬盘的计算机,最多可将其硬盘分为四个卷(逻辑磁盘)。 需要指出的是,在现代 OS中,一个物理磁盘可以划分为多个卷,一个卷也可以由多个物理磁盘组成。
FAT12
以盘块为基本分配单位,在每个分区中都配有两张文件分配表 FAT1 和 FAT2,在 FAT的每个表项中存放下一个盘块号,它实际上是用于盘块之间的链接的指针,通过它可以将一个文件的所有的盘块链接起来,而将文件的第一个盘块号放在自己的FCB,对于 1.2 MB 的软盘,每个盘块的大小为 512 B,在每个FAT 中共含有 2.4 K 个表项,由于每个 FAT 表项占12 位,故 FAT 表占用 3.6 KB 的存储空间,在 FAT 表中最多允许有4096 个表项,如果采用以盘块作为基本分配单位, 每个盘块(也称扇区)的大小一般是 512 字节,那么,每个磁盘分区的容量为 2 MB (4096×512 B)。 同时,一个物理磁盘支持 4 个逻辑磁盘分区,所以相应的磁盘最大容量仅为 8 MB。
簇的基本概念
为了适应磁盘容量不断增大的需要,在进行盘块分配时,不再以盘块而是以簇(cluster) 为基本单位。簇是一组连续的扇区,在 FAT 中它是作为一个虚拟扇区,簇的大小一般是 2n (n 为整数)个盘块,一个簇应包含扇区的数量与磁盘容量的大小直接有关,以簇作为基本的分配单位所带来的最主要的好处是,能适应磁盘容量不断增大的情况。
值得注意的是,使用簇作为基本的分配单位虽可减少 FAT 表中的项数(在相同的磁盘容量下,FAT 表的项数是与簇的大小成反比的),这一方面会使 FAT表占用更少的存储空间,并减少访问 FAT 表的存取开销,提高文件系统的效率,但这也会造成更大的簇内零头。
FAT12 存在的问题
对所允许的磁盘容量存在着严重的限制,通常只能是数十兆字节,虽然可以用继续增加簇的大小来提高所允许的最大磁盘容量,但随着支持的硬盘容量的增加,相应的簇内碎片也将随之成倍地增加,此外,它只能支持 8+3 格式的文件名。
FAT16
增加 FAT 表的宽度,如果我们将 FAT 表的宽度增至 16 位,最大表项数增至 65536个,此时便能将一个磁盘分区分为 65 536(216)个簇。 在 FAT16 的每个簇中可以有的盘块数为 4、8、16、32 直到 64。由此得出 FAT16 可以管理的最大分区空间为 216 × 64 × 512 = 2048 MB。 例如,当要求磁盘分区的大小为 8GB 时,则每个簇的大小达到128KB,这意味着内部零头最大可达到 128 KB。 一般而言,对于 1 ~ 4 GB的硬盘来说,大约会浪费10%~20%的空间。 为了解决这一问题,微软推出了FAT32。 另外,由于 FAT12 和 FAT16都不支持长文件名,文件名受到了8个字符文件名和3个字符文件扩展名的长度限制,为了满足用户通过文件名更好地描述文件内容的需求,在Windows 95以后的系统中,对FAT16进行了扩展,通过一个长文件名占用多个目录项的方法,使得文件名的长度可以长达 255 个字符,这种扩展的 FAT16 也称为 VFAT。
FAT32
如同存储器管理中的分页管理,所选择的页面越大,可能造成的页内零头也会越大。 为减少页内零头就应该选择适当大小的页面。FAT32是FAT 系列文件系统的最后一个产品。 每一簇在FAT 表中的表项占据4字节(232), FAT 表可以表示 4294967296 项,即FAT32 允许管理比 FAT16 更多的簇。 这样就允许在 FAT32 中采用较小的簇,FAT32 的每个簇都固定为 4KB,即每簇用 8 个盘块代替 FAT16 的 64 个盘块,每个盘块仍为 512 字节,FAT32分区格式可以管理的单个最大磁盘空间大到 4 KB×232 = 2 TB。 FAT32 比 FAT16支持更小的簇和更大的磁盘容量,这就大大减少了磁盘空间的浪费,使得 FAT32 分区的空间分配更有效率。FAT32 主要应用于Windows 98 及后续 Windows 系统,它可以增强磁盘性能,并增加可用磁盘空间,同时也支持长文件名;它不存在最小存储空间问题,能够有效地节省硬盘空间。由于文件分配表的扩大,运行速度比FAT16 格式要慢; 其次,FAT32 有最小管理空间的限制,FAT32 卷必须至少有 65537 个簇,所以 FAT32不支持容量小于 512 MB 的分区。因此对于小分区,则仍然需要使用 FAT16 或 FAT12; 再之,FAT32的单个文件的长度也不能大于4 GB; 最后,FAT32 最大的限制在于兼容性方面,FAT32 不能保持向下兼容。
NTFS(New Technology File System)
NTFS是一个专门为 Windows NT 开发的、全新的文件系统,并适用于 Windows 2000/XP/2003。它使用了 64 位磁盘地址,理论上可以支持 2 的 64 次方字节的磁盘分区;其次,在 NTFS 中可以很好地支持长文件名,单个文件名限制在 255 个字符以内,全路径名为 32 767 个字符;第三,具有系统容错功能,即在系统出现故障或差错时,仍能保证系统正常运行。第四,提供了数据的一致性,还提供了文件加密、文件压缩等功能。
磁盘组织
NTFS 也是以簇作为磁盘空间分配和回收的基本单位。一个文件占用若干个簇,一个簇只属于一个文件。通过簇来间接管理磁盘,可以不需要知道盘块(扇区)的大小,使 NTFS 具有了与磁盘物理扇区大小无关的独立性。很容易支持扇区大小不是 512 字节的非标准磁盘,从而可以根据不同的磁盘选择匹配的簇大小。
在 NTFS 文件系统中,把卷上簇的大小称为“卷因子”,卷因子是在磁盘格式化时确定的,其大小同 FAT 一样,也是物理磁盘扇区的整数倍,即一个簇包含2n(n 为整数)个盘块,簇的大小可由格式化命令或格式化程序按磁盘容量和应用需求来确定,可以为 512 B、 1KB、 2 KB……,最大可达 64 KB。事实上,为了在传输效率和簇内碎片之间进行折中,NTFS 在大多数情况下都是使用 4 KB。 对于簇的定位,NTFS 是采用逻辑簇号 LCN(Logical Cluster Number)和虚拟簇号 VCN(Virtual Cluster Number)进行的。LCN 是以卷为单位,将整个卷中所有的簇按顺序进行简单的编号,NTFS 在进行地址映射时,可以通过卷因子与 LCN 的乘积便可算出卷上的物理字节偏移量,从而得到文件数据所在的物理磁盘地址。为了方便文件中数据的引用, NTFS 还可以使用 VCN,以文件为单位,将属于某个文件的簇按顺序进行编号。只要知道了文件开始的簇地址,便可将 VCN 映射到 LCN。
文件的组织
在 NTFS 中,以卷为单位,将一个卷中的所有文件信息、目录信息以及可用的未分配空间信息都以文件记录的方式记录在一张主控文件表 MFT(Master File Table)中。该表是 NTFS 卷结构的中心,卷中的每个文件作为一条记录,在MFT表中占有一行,其中还包括 MFT 自己的这一行。每行大小固定为 1 KB,每行称为该行所对应文件的元数据(metadata),也称为文件控制字。
在 MFT 表中,每个元数据将其所对应文件的所有信息,包括文件的内容等,都被组织在所对应文件的一组属性中。由于文件大小相差悬殊,其属性所需空间大小也相差很大。在 MFT 表中,对于元数据的 1 KB 空间,可能记录不下文件的全部信息。所以当文件较小时,其属性值所占空间也较小,可以将文件的所有属性直接记录在元数据中。而当文件较大时,元数据仅记录该文件的一部分属性,其余属性,可以记录到卷中的其它可用簇中,并将这些簇按其所记录文件的属性进行分类,分别链接成多个队列,将指向这些队列的指针保存在元数据中。
实际上,文件在存储过程中,数据往往连续存放在若干个相邻的簇中,仅用一个指针记录这几个相邻的簇即可,而不是每个簇需要一个指针,从而可以节省指针所耗费的空间。 一般地,只需十几个字节就可以含有 FAT32 所需几百个 KB 才拥有的信息量。 NTFS 的不足之处在于,它只能被 Windows NT 所识别。NTFS 文件系统可以存取 FAT 等文件系统的文件,但 NTFS 文件却不能FAT 等文件系统所存取,缺乏兼容性。
单级索引分配
链接分配方式虽然解决了连续分配方式所存在的问题,但不能支持高效的直接存取。要对一个较大的文件进行直接存取,须首先在 FAT 中顺序地查找许多盘块号。FAT 需占用较大的内存空间。由于一个文件所占用盘块的盘块号是随机地分布在FAT中的。只有将整个FAT调入内存,才能保证在FAT 中找到一个文件的所有盘块号。当磁盘容量较大时,FAT 可能要占用数兆字节以上的内存空间。事实上,在打开某个文件时,只需把该文件占用的盘块的编号调入内存即可,完全没有必要将整个FAT 调入内存。为此,应将每个文件所对应的盘块号集中地放在一起。
索引分配方法就是基于这种想法所形成的一种分配方法。它为每个文件分配一个索引块(表),再把分配给该文件的所有盘块号都记录在该索引块中。因而该索引块就是一个含有许多盘块号的数组。在建立一个文件时,只需在为之建立的目录项中填上指向该索引块的指针。
索引分配方式支持直接访问。当要读文件的第 i 个盘块时,可以方便地直接从索引块中找到第 i 个盘块的盘块号;此外,索引分配方式也不会产生外部碎片。当文件较大时,索引分配方式无疑要优于链接分配方式。
可能要花费较多的外存空间:每当建立一个文件时,便须为之分配一个索引块,将分配给该文件的所有盘块号记录于其中。但在一般情况下,总是中、小型文件居多,甚至有不少文件只需 1~2 个盘块,如果采用链接分配方式,只需设置 1~2 个指针。如果采用索引分配方式,则同样仍须为之分配一索引块。通常是采用一个专门的盘块作为索引块,其中可存放成百个、甚至上千个盘块号。可见,对于小文件采用索引分配方式时,其索引块的利用率将是极低的。
多级索引分配
当 OS 为一个大文件分配磁盘空间时,如果所分配出去的盘块的盘块号已经装满一个索引块时,OS 便为该文件分配另一个索引块,用于将以后继续为之分配的盘块号记录于其中。 依此类推,再通过链指针将各索引块按序链接起来。当文件太大,其索引块太多时, 这种方法是低效的。此时,应为这些索引块再建立一级索引,称为第一级索引,即系统再分配一个索引块,作为第一级索引的索引块将第一块、第二块……等索引块的盘块号填入到此索引表中,这样便形成了两级索引分配方式。如果每个盘块的大小为 1 KB,每个盘块号占 4 个字节,则在一个索引块中可存放 256 个盘块号。这样,在两级索引时, 最多可包含的存放文件的盘块的盘块号总数 N = 256 × 256 = 64 K 个盘块号。由此可得出结论: 采用两级索引时,所允许的文件最大长度为 64 MB。倘若盘块的大小为 4 KB, 在采用单级索引时所允许的最大文件长度为 4 MB;而在采用两级索引时所允许的最大文件长度可达 4 GB。
混合索引分配方式
所谓混合索引分配方式,是指将多种索引分配方式相结合而形成的一种分配方式。例如,系统既采用了直接地址,又采用了一级索引分配方式,或两级索引分配方式,甚至还采用了三级索引分配方式。这种混合索引分配方式已在 UNIX 系统中采用。在 UNIX System Ⅴ的索引结点中, 共设置了13个地址项, 即iaddr(0)~iaddr(12),在 BSD UNIX 的索引结点中,共设置了 13 个地址项,它们都把所有的地址项分成两类,即直接地址和间接地址。
直接地址
为了提高对文件的检索速度,在索引结点中可设置 10 个直接地址项,即用 iaddr(0)~ iaddr(9)来存放直接地址。换言之,在这里的每项中所存放的是该文件数据所在盘块的盘块号。假如每个盘块的大小为 4 KB,当文件不大于 40 KB 时,便可直接从索引结点中读出该文件的全部盘块号。
一次间接地址
对于大、中型文件,只采用直接地址是不现实的。为此,可再利用索引结点中的地址项 iaddr(10)来提供一次间接地址。这种方式的实质就是一级索引分配方式。一次间址块也就是索引块,系统将分配给文件的多个盘块号记入其中。在一次间址块中可存放 1 K 个盘块号,因而允许文件长达 4 MB。
多次间接地址
当文件长度大于4 MB + 40 KB 时(一次间址与 10 个直接地址项),系统还须采用二次间址分配方式。这时,用地址项 iaddr(11)提供二次间接地址。该方式的实质是两级索引分配方式。系统此时是在二次间址块中记入所有一次间址块的盘号。在采用二次间址方式时, 文件最大长度可达 4 GB。同理,地址项 iaddr(12)作为三次间接地址,其所允许的文件最大长度可达 4 TB。
实现“按名存取”
即用户只须向系统提供所需访问文件的名字,便能快速准确地找到指定文件在外存上的存储位置。这是目录管理中最基本的功能,也是文件系统向用户提供的最基本的服务。
提高对目录的检索速度
通过合理地组织目录结构的方法,可加快对目录的检索速度,从而提高对文件的存取速度。这是在设计一个大、中型文件系统时所追求的主要目标。
文件共享
在多用户系统中,应允许多个用户共享一个文件。这样就须在外存中只保留一份该文件的副本,供不同用户使用,以节省大量的存储空间,并方便用户和提高文件利用率。
允许文件重名
系统应允许不同用户对不同文件采用相同的名字,以便于用户按照自己的习惯给文件命名和使用文件。
为了能对一个文件进行正确的存取,必须为文件设置用于描述和控制文件的数据结构,称之为“文件控制块(FCB)”。文件管理程序可借助于文件控制块中的信息,对文件施以各种操作。文件与文件控制块一一对应,而人们把文件控制块的有序集合称为文件目录,即一个文件控制块就是一个文件目录项。通常,一个文件目录也被看做是一个文件,称为目录文件。
文件控制块FCB(File Control Block)
基本信息类
文件名
指用于标识一个文件的符号名。在每个系统中,每一个文件都必须有惟一的名字,用户利用该名字进行存取。
文件物理位置
指文件在外存上的存储位置。它包括存放文件的设备名、文件在外存上的起始盘块号、指示文件所占用的盘块数或字节数的文件长度。
文件逻辑结构
指示文件是流式文件还是记录式文件、记录数;文件是定长记录还是变长记录等。
文件的物理结构
指示文件是顺序文件,还是链接式文件或索引文件
存取控制信息类
文件主的存取权限、核准用户的存取权限以及一般用户的存取权限
使用信息类
文件的建立日期和时间、文件上一次修改的日期和时间及当前使用信息。
索引结点
文件目录通常是存放在磁盘上的,当文件很多时,文件目录可能要占用大量的盘块。 在查找目录的过程中,先将存放目录文件的第一个盘块中的目录调入内存,然后把用户所给定的文件名与目录项中的文件名逐一比较。若未找到指定文件,便再将下一个盘块中的目录项调入内存。
在检索目录文件的过程中,只用到了文件名,仅当找到一个目录项(即其中的文件名与指定要查找的文件名相匹配)时,才需从该目录项中读出该文件的物理地址。而其它一些对该文件进行描述的信息,在检索目录时一概不用。显然,这些信息在检索目录时不需调入内存。为此,在有的系统中,便采用了把文件名与文件描述信息分开的办法。即使文件描述信息单独形成一个称为索引结点的数据结构, 简称为 i 结点。在文件目录中的每个目录项仅由文件名和指向该文件所对应的 i 结点的指针所构成。在 UNIX 系统中一个目录仅占 16 个字节,其中 14 个字节是文件名,2 个字节为 i 结点指针。在 1 KB 的盘块中可做 64 个目录项,这样,为找到一个文件,可使平均启动磁盘次数减少到原来的 1/4,大大节省了系统开销。
磁盘索引结点
这是存放在磁盘上的索引结点。每个文件有惟一的一个磁盘索引结点
文件主标识符,即拥有该文件的个人或小组的标识符。
文件类型,包括正规文件、目录文件或特别文件。
文件存取权限,指各类用户对该文件的存取权限。
文件物理地址,每一个索引结点中含有 13 个地址项,即 iaddr(0)~iaddr(12),以直接或间接方式给出数据文件所在盘块的编号。
文件长度,指以字节为单位的文件长度。
文件连接计数,表明在本文件系统中所有指向该(文件的)文件名的指针计数。
文件存取时间,指本文件最近被进程存取的时间、最近被修改的时间及索引结点最近被修改的时间。
内存索引结点
这是存放在内存中的索引结点。当文件被打开时,要将磁盘索引结点拷贝到内存的索引结点中,便于以后使用。
索引结点编号,用于标识内存索引结点。
状态,指示 i 结点是否上锁或被修改。
访问计数,每当有一进程要访问此 i 结点时,将该访问计数加 1,访问完再减 1。
文件所属文件系统的逻辑设备号。
链接指针,设置有分别指向空闲链表和散列队列的指针。
单级目录结构
这是最简单的目录结构。在整个文件系统中只建立一张目录表,每个文件占一个目录项
目录项中含文件名、文件扩展名、文件长度、文件类型、文件物理地址以及其它文件属性。此外,为表明每个目录项是否空闲,又设置了一个状态位。
每当要建立一个新文件时,必须先检索所有的目录项,以保证新文件名在目录中是惟一的。然后再从目录表中找出一个空白目录项,填入新文件的文件名及其它说明信息,并置状态位为 1。删除文件时,先从目录中找到该文件的目录项,回收该文件所占用的存储空间,然后再清除该目录项。简单且能实现目录管理的基本功能——按名存取
查找速度慢:对于稍具规模的文件系统,会拥有数目可观的目录项,致使为找到一个指定的目录项要花费较多的时间。对于一个具有 N 个目录项的单级目录,为检索出一个目录项,平均需查找 N/2 个目录项。
不允许重名:在一个目录表中的所有文件,都不能与另一个文件有相同的名字。
不便于实现文件共享:通常,每个用户都有自己的名字空间或命名习惯。因此,应当允许不同用户使用不同的文件名来访问同一个文件。单级目录要求所有用户都用同一个名字来访问同一文件。
简言之,单级目录只能满足对目录管理的四点要求中的第一点, 因而,它只能适用于单用户环境。
两级目录
为了克服单级目录所存在的缺点,可以为每一个用户建立一个单独的用户文件目录 UFD(User File Directory)。这些文件目录具有相似的结构,它由用户所有文件的文件控制块组成。此外,在系统中再建立一个主文件目录 MFD(Master File Directory);在主文件目录中, 每个用户目录文件都占有一个目录项,其目录项中包括用户名和指向该用户目录文件的指针。
在两级目录结构中,如果用户希望有自己的用户文件目录 UFD,可以请求系统为自己建立一个用户文件目录;如果自己不再需要 UFD,也可以请求系统管理员将它撤消。在有了UFD 后,用户可以根据自己的需要创建新文件。OS 只需检查该用户的 UFD,判定在该 UFD 中是否已有同名的另一个文件。若有,用户必须为新文件重新命名;若无,便在 UFD 中建立一个新目录项,将新文件名及其有关属性填入目录项中,并置其状态位为 “1”。当用户要删除一个文件时,OS 也只需查找该用户的 UFD,从中找出指定文件的目录项,在回收该文件所占用的存储空间后,将该目录项删除。
提高了检索目录的速度:如果在主目录中有 n 个子目录,每个用户目录最多为 m 个目录项,则为查找一指定的目录项,最多只需检索 n + m 个目录项。但如果是采用单级目录结构,则最多需检索 n × m 个目录项。假定 n = m,两级目录可使检索效率提高 n/2 倍。
在不同的用户目录中,可以使用相同的文件名。不同用户还可使用不同的文件名来访问系统中的同一个共享文件。该结构虽然能有效地将多个用户隔开,在各用户之间完全无关时,这种隔离是一个优点;但当多个用户之间要相互合作去完成一个大任务,且一用户又需去访问其他用户的文件时,这种隔离便成为一个缺点,因为这种隔离会使诸用户之间不便于共享文件。
多级目录结构(树型目录结构)
主目录在这里被称为根目录,把数据文件称为树叶,其它的目录均作为树的结点。为了提高文件系统的灵活性,应允许在一个目录文件中的目录项既是作为目录文件的 FCB, 又是数据文件的 FCB,这一信息可用目录项中的一位来指示。
路径名
在树形目录结构中,从根目录到任何数据文件,都只有一条惟一的通路。在该路径上从树的根(即主目录)开始,把全部目录文件名与数据文件名依次地用“/”连接起来,即构成该数据文件的路径名(path name)。系统中的每一个文件都有惟一的路径名。
当前目录(Current Directory)
当一个文件系统含有许多级时,每访问一个文件,都要使用从树根开始直到树叶为止的、包括各中间节点名的全路径名。同时由于一个进程运行时所访问的文件大多仅局限于某个范围,可为每个进程设置一个“当前目录”,又称为“工作目录”。进程对各文件的访问都相对于“当前目录”而进行。此时各文件所使用的路径名,只需从当前目录开始,逐级经过中间的目录文件,最后到达要访问的数据文件。把这一路径上的全部目录文件名与数据文件名用“/”连接形成路径名。
把从当前目录开始直到数据文件为止所构成的路径名,称为相对路径名(relative path name);而把从树根开始的路径名称为绝对路径名(absolute path name)。 查询速度更快,同时层次结构更加清晰,能够更加有效地进行文件的管理和保护。
在多级目录中,不同性质、不同用户的文件可以构成不同的目录子树,不同层次、不同用户的文件分别呈现在系统目录树中的不同层次或不同子树中,可以容易地赋予不同的存取权限。 但是在多级目录中查找一个文件,需要按路径名逐级访问中间节点,这就增加了磁盘访问次数,影响查询速度。
增加和删除目录
在树型目录结构中,用户可为自己建立 UFD,并可再创建子目录。在用户要创建一个新文件时,只需查看在自己的 UFD 及其子目录中有无与新建文件相同的文件名。若无,便可在 UFD 或其某个子目录中增加一个新目录项。 在树型目录中,对于一个已不再需要的目录,应如何删除其目录项,须视情况而定。
如果所要删除的目录是空的,可简单地将该目录项删除,使它在其上一级目录中对应的目录项为空;
如果要删除的目录不空,则可采用下述两种方法处理:
不删除非空目录:为了删除一个非空目录, 必须先删除目录中的所有文件,使之先成为空目录,然后再予以删除。如果目录中还包含有子目录,还必须采取递归调用方式来将其删除。
可删除非空目录:当要删除一个目录时,如果在该目录中还包含有文件,则目录中的所有文件和子目录也同时被删除。
当用户要访问一个已存在文件时,系统首先利用用户提供的文件名对目录进行查询, 找出该文件的文件控制块或对应索引结点;然后,根据 FCB 或索引结点中所记录的文件物理地址(盘块号),换算出文件在磁盘上的物理位置;再通过磁盘驱动程序,将所需文件读入内存。
线性检索法(顺序检索法)
在单级目录中,利用用户提供的文件名,用顺序查找法直接从文件目录中找到指名文件的目录项。在树型目录中,用户提供的文件名是由多个文件分量名组成的路径名,此时须对多级目录进行查找。如果在顺序查找过程中发现有一个文件分量名未能找到,则应停止查找,并返回“文件未找到”信息。
Hash 方法
系统利用用户提供的文件名并将它变换为文件目录的索引值,再利用该索引值到目录中去查找,这将显著地提高检索速度。 对于使用了通配符的文件名,系统此时便无法利用Hash 方法检索目录,因此,这时系统还是需要利用线性查找法查找目录。 在进行文件名的转换时,有可能把 n 个不同的文件名转换为相同的 Hash 值,即出现了所谓的“冲突”。一种处理此“冲突”的有效规则是: 在利用 Hash 法索引查找目录时,如果目录表中相应的目录项是空的,则表示系统中并无指定文件。 如果目录项中的文件名与指定文件名相匹配,则表示该目录项正是所要寻找的文件所对应的目录项,故而可从中找到该文件所在的物理地址。 如果在目录表的相应目录项中的文件名与指定文件名并不匹配,则表示发生了“冲突”此时须将其 Hash 值再加上一个常数(该常数应与目录的长度值互质),形成新的索引值,再返回到第一步重新开始查找。
空闲表法
空闲表
空闲表法属于连续分配方式,系统为外存上的所有空闲区建立一张空闲表,每个空闲区对应于一个空闲表项,其中包括表项序号、该空闲区的第一个盘块号、该区的空闲盘块数等信息。 再将所有空闲区按其起始盘块号递增的次序排列。
存储空间的分配与回收
空闲盘区的分配与内存的动态分配类似,在内存分配上,虽然很少采用连续分配方式,然而在外存的管理中,这种分配方式具有较高的分配速度,可减少访问磁盘的 I/O 频率。
空闲链表法
空闲链表法是将所有空闲盘区拉成一条空闲链。根据构成链所用基本元素的不同,可把链表分成两种形式:空闲盘块链和空闲盘区链。
空闲盘块链:
这是将磁盘上的所有空闲空间,以盘块为单位拉成一条链。当用户因创建文件而请求分配存储空间时,系统从链首开始,依次摘下适当数目的空闲盘块分配给用户。当用户因删除文件而释放存储空间时,系统将回收的盘块依次插入空闲盘块链的末尾。用于分配和回收一个盘块的过程非常简单,但在为一个文件分配盘块时,可能要重复操作多次。
空闲盘区链:
这是将磁盘上的所有空闲盘区(每个盘区可包含若干个盘块)拉成一条链。在每个盘区上除含有用于指示下一个空闲盘区的指针外,还应有能指明本盘区大小(盘块数)的信息。分配盘区通常采用首次适应算法。在回收盘区时,同样也要将回收区与相邻接的空闲盘区相合并。在采用首次适应算法时,为了提高对空闲盘区的检索速度,可以采用显式链接方法,亦即,在内存中为空闲盘区建立一张链表。
位示图
位示图是利用二进制的一位来表示磁盘中一个盘块的使用情况。磁盘上的所有盘块都有一个二进制位与之对应,这样,由所有盘块所对应的位构成一个集合,称为位示图。 通常可用 m × n个位数来构成位示图,并使 m × n 等于磁盘的总块数,位示图也可描述为一个二维数组。
盘块的分配
顺序扫描位示图,从中找出一个或一组其值为“0”的二进制位(“0”表示空闲时)。将所找到的一个或一组二进制位转换成与之相应的盘块号。 假定找到的其值为“0” 的二进制位位于位示图的第i行、第j列,则其相应的盘块号应按下式计算: b = n(i - 1) + j 式中,n 代表每行的位数。 修改位示图,令 map[i,j]=1。
盘块的回收
将回收盘块的盘块号转换成位示图中的行号和列号。 转换公式为: i = (b - 1)DIV n + 1,j = (b -1)MOD n + 1 修改位示图:令 map[i,j] =0
从位示图中很容易找到一个或一组相邻接的空闲盘块。 此外,由于位示图很小,占用空间少,因而可将它保存在内存中,使在每次进行盘区分配时,无需把盘区分配表读入内存,从而节省了许多磁盘的启动操作。
空闲表法和空闲链表法都不适用于大型文件系统,因为这会使空闲表或空闲链表太长。
空闲盘块的组织
空闲盘块号栈用来存放当前可用的一组空闲盘块的盘块号(最多含100个号),以及栈中尚有的空闲盘块号数 N。顺便指出,N 还兼作栈顶指针用。由于栈是临界资源,每次只允许一个进程去访问,故系统为栈设置了一把锁。
文件区中的所有空闲盘块被分成若干个组 ,将每一组含有的盘块总数 N 和该组所有的盘块号记入其前一组的第一个盘块的 S.free(0)~S.free(99)中。这样,由各组的第一个盘块可链成一条链。 将第一组的盘块总数和所有的盘块号记入空闲盘块号栈中,作为当前可供分配的空闲盘块号。
最末一组只有 99 个盘块,其盘块号分别记入其前一组的 S.free(1) ~S.free(99)中,而在 S.free(0)中则存放“0”,作为空闲盘块链的结束标志。最后一组的盘块数应为99, 不应是 100,因为这是指可供使用的空闲盘块,其编号应为(1~99),0 号中放空闲盘块链的结尾标志。
空闲盘块的分配与回收
当系统要为用户分配文件所需的盘块时,须调用盘块分配过程来完成。该过程首先检查空闲盘块号栈是否上锁,如未上锁,便从栈顶取出一空闲盘块号,将与之对应的盘块分配给用户,然后将栈顶指针下移一格。若该盘块号已是栈底,即 S.free(0),这是当前栈中最后一个可分配的盘块号。由于在该盘块号所对应的盘块中记有下一组可用的盘块号,因此, **须调用磁盘读过程,将栈底盘块号所对应盘块的内容读入栈中,作为新的盘块号栈的内容, 并把原栈底对应的盘块分配出去(其中的有用数据已读入栈中)。**然后,再分配一相应的缓冲区(作为该盘块的缓冲区)。最后,把栈中的空闲盘块数减 1 并返回。
在系统回收空闲盘块时,须调用盘块回收过程进行回收。它是将回收盘块的盘块号记入空闲盘块号栈的顶部,并执行空闲盘块数加 1 操作。当栈中空闲盘块号数目已达 100 时,表示栈已满,便将现有栈中的 100 个盘块号记入新回收的盘块中,再将其盘块号作为新栈底。
在树型结构的目录中,当有两个用户要共享一个子目录或文件时,必须将共享文件或子目录链接到两个用户的目录中,才能方便地找到该文件。 此时该文件系统的目录结构已不再是树型结构,而是个有向非循环图 DAG(Directed Acyclic Graph)。
如果在文件目录中包含了文件的物理地址, 即文件所在盘块的盘块号,则在链接时,必须将文件的物理地址拷贝到 B 目录中去。但如果以后 B 或 C 还要继续向该文件中添加新内容,也必然要相应地再增加新的盘块,这须由附加操作 Append 来完成。而这些新增加的盘块,也只会出现在执行了操作的目录中。可见,这种变化对其他用户而言是不可见的,因而新增加的这部分内容已不能被共享。
可以引用索引结点,即诸如文件的物理地址及其它的文件属性等信息,不再是放在目录项中,而是放在索引结点中。在文件目录中只设置文件名及指向相应索引结点的指针。 此时,由任何用户对文件进行 Append操作或修改,所引起的相应结点内容的改变是其他用户可见的,也就能提供给其他用户来共享。
在索引结点中,还应有一个链接计数 count,用于表示链接到本索引结点(亦即文件)上的用户目录项的数目。当用户 C 创建一个新文件时,他便是该文件的所有者,此时将 count 置 1。当有用户 B 要共享此文件时,在用户 B 的目录中增加一目录项,并设置一指针指向该文件的索引结点,此时,文件主仍然是 C,count=2,如果用户 C 不再需要此文件若删除了该文件,也必然删除了该文件的索引结点,这样便会使 B 的指针悬空,而 B 则可能正在此文件上执行写操作,此时将因此半途而废。但如果 C 不删 除此文件而等待 B 继续使用,这样,由于文件主是 C,如果系统要记账收费,则 C 必须为 B 使用此共享文件而付账,直至 B 不再需要。
为使 B 能共享 C 的一个文件 F,可以由系统创建一个 LINK 类型的新文件,也取名为 F 并将 F 写入 B 的目录中,以实现 B的目录与文件 F 的链接。 在新文件中只包含被链接文件 F 的路径名。这样的链接方法被称为符号链接(Symbolic Linking)。
新文件中的路径名则只被看作是符号链(Symbolic Link),当 B 要访问被链接的文件F且正要读 LINK 类新文件时,此要求将被 OS 截获,OS 根据新文件中的路径名去读该文件,于是就实现了用户 B 对文件 F 的共享。
在利用符号链方式实现文件共享时,只是文件主才拥有指向其索引结点的指针;而共享该文件的其他用户则只有该文件的路径名,并不拥有指向其索引结点的指针。这样,也就不会发生在文件主删除一共享文件后留下一悬空指针的情况。当文件的拥有者把文件删除后,其他用户试图通过符号链去访问一个已被删除的共享文件时,会因系统找不到该文件而使访问失败,于是再将符号链删除,此时不会产生任何影响。
当其他用户去读共享文件时,系统是根据给定的文件路径名,逐个分量(名)地去查找目录,直至找到该文件的索引结点。因此,在每次 访问共享文件时,都可能要多次地读盘。这使每次访问文件的开销甚大,且增加了启动磁盘的频率。此外,要为每个共享用户建立一条符号链,而由于该链实际上是一个文件,尽管该文件非常简单,却仍要为它配置一个索引结点,这也要耗费一定的磁盘空间。
能够用于链接世界上任何地方的计算机中的文件,此时只需提供该文件所在机器的网络地址以及该机器中的文件路径即可。
影响文件安全性的主要因素:
人为因素
即由于人们有意或无意的行为,而使文件系统中的数据遭到破坏或丢失。
系统因素
即由于系统的某部分出现异常情况,而造成对数据的破坏或丢失。特别是作为数据存储介质的磁盘,在出现故障或损坏时,会对文件系统的安全性造成影响;
自然因素
即存放在磁盘上的数据,随着时间的推移将可能发生溢出或逐渐消失。
为了确保文件系统的安全性,可采取以下措施:
通过存取控制机制来防止由人为因素所造成的文件不安全性。
通过磁盘容错技术来防止由磁盘部分的故障所造成的文件不安全性。
通过“后备系统”来防止由自然因素所造成的不安全性。
容错技术是通过在系统中设置冗余部件的办法,来提高系统可靠性的一种技术。磁盘容错技术则是通过增加冗余的磁盘驱动器、磁盘控制器等方法,来提高磁盘系统可靠性的一种技术,即当磁盘系统的某部分出现缺陷或故障时,磁盘仍能正常工作,且不致造成数据的丢失或错误。
第一级容错技术 SFT-Ⅰ
最基本的一种磁盘容错技术,主要用于防止因磁盘表面缺陷所造成的数据丢失。它包含双份目录、双份文件分配表及写后读校验等措施。
双份目录和双份文件分配表
在磁盘上存放的文件目录和文件分配表 FAT,是文件管理所用的重要数据结构。为了防止这些表格被破坏,可在不同的磁盘上或在磁盘的不同区域中,分别建立(双份)目录表和 FAT。其中一份为主目录及主 FAT;另一份为备份目录及备份 FAT。一旦由于磁盘表面缺陷而造成主文件目录或主 FAT 的损坏时,系统便自动启用备份文件目录及备份 FAT从而可以保证磁盘上的数据仍是可访问的。
由于磁盘价格昂贵,当磁盘表面有少量缺陷时,可采取某种补救措施。
热修复重定向:
系统将磁盘容量的一部分(例如 2%~3%)作为热修复重定向区,用于**存放当发现磁盘有缺陷时的待写数据,并对写入该区的所有数据进行登记,以便于以后对数据进行访问。 **
写后读校验方式
在每次从内存缓冲区向磁盘中写入一个数据块后,又立即从磁盘上读出该数据块,并送至另一缓冲区中,再将该缓冲区内容与内存缓冲区中在写后仍保留的数据进行比较。若两者一致,便认为此次写入成功,可继续写下一个盘块;否则,再重写。若重写后两者仍不一致,则认为该盘块有缺陷,此时,便将应写入该盘块的数据,写入到热修复重定向区中。
第二级容错技术 SFT-Ⅱ
主要用于防止由磁盘驱动器和磁盘控制器故障所导致的系统不能正常工作,它具体又可分为磁盘镜像和磁盘双工。
磁盘镜像(Disk Mirroring)
须在同一磁盘控制器下再增设一个完全相同的磁盘驱动器。
在每次向主磁盘写入数据后,都需要将数据再写到备份磁盘上,使两个磁盘上具有完全相同的位像图,当主磁盘驱动器发生故障时,由于有备份磁盘的存在,在进行切换后,使主机仍能正常工作。磁盘镜像虽然实现了容错功能,但未能使服务器的磁盘 I/O 速度得到提高,却使磁盘的利用率降至仅为 50%。
磁盘双工(Disk Duplexing)
如果控制这两台磁盘驱动器的磁盘控制器发生故障,或主机到磁盘控制器之间的通道发生了故障,磁盘镜像功能便起不到数据保护的作用。磁盘双工功能,即将两台磁盘驱动器分别接到两个磁盘控制器上,同样使这两台磁盘机镜像成对。 在磁盘双工时,文件服务器同时将数据写到两个处于不同控制器下的磁盘上,使两者有完全相同的位像图。如果某个通道或控制器发生故障时,另一通道上的磁盘仍能正常工作,不会造成数据的丢失。在磁盘双工时,由于每一个磁盘都有自己的独立通道,故可同时(并行)地将数据写入磁盘,或读出数据。
基于集群技术的容错功能
所谓集群,是指由一组互连的自主计算机组成统一的计算机系统,给人们的感觉是,它们是一台机器。利用集群系统可提高系统的并行处理能力,还可用于提高系统的可用性,是当前使用最广泛的一类具有容错功能的集群系统
主要工作模式
双机热备份模式
在这种模式的系统中,备有两台服务器,两者的处理能力通常是完全相同的,一台作为主服务器,另一台作为备份服务器。平时主服务器运行,备份服务器则时刻监视着主服务器的运行一旦主服务器出现故障,备份服务器便立即接替主服务器的工作而成为系统中的主服务器,修复后的服务器再作为备份服务器。
为使在这两台服务器间能保持镜像关系,应在这两台服务器上各装入一块网卡,并通过一条镜像服务器链路 MSL(Mirrored Server Link)将两台服务器连接起来。两台服务器之间保持一定的距离,其所允许的距离取决于所配置的网卡和传输介质。**此外,还必须在系统中设置某种机制,来检测主服务器中数据的改变。一旦该机制检测到主服务器中有数据变化,便立即通过通信系统将修改后的数据传送到备份服务器的相应数据文件中。**为了保证在两台服务器之间通信的高速性和安全性,通常都选用高速通信信道,并有备份线路。
在这种模式下,一旦主服务器发生故障,系统能自动地将主要业务用户切换到备份服务器上。为保证切换时间足够快(通常为数分钟),要求在系统中配置有切换硬件的开关设备,在备份服务器上事先建立好通信配置,并能迅速处理客户机的重新登录等事宜。
提高了系统的可用性,易于实现, 而且主、备份服务器完全独立,可支持远程热备份,从而能消除由于火灾、爆炸等非计算机因素所造成的隐患,从服务器处于被动等待状态,整个系统的使用 效率只有 50%。
双机互为备份模式
**平时,两台服务器均为在线服务器,它们各自完成自己的任务。**为了实现两者互为备份,在两台服务器之间,应通过某种专线连接起来。再通过路由器将两台服务器互连起来,作为备份通信线路。在互为备份的模式中,最好在每台服务器内都配置两台硬盘,一个用于装载系统程序和应用程序,另一个用于接收由另一台服务器发来的备份数据,作为该服务器的镜像盘。
在正常运行时,镜像盘对本地用户是锁死的,这样就较易于保证在镜像盘中数据的正确性。 如果仅有一个硬盘,则可用建立虚拟盘的方式或分区方式来分别存放系统程序和应用程序, 以及另一台服务器的备份数据。 如果通过专线链接检查到某台服务器发生了故障,此时,再通过路由器去验证这台服务器是否真的发生了故障。如果故障被证实,则由正常服务器向故障服务器的客户机发出广播信息,表明要进行切换。连接到故障服务器上的客户机在切换过程中会感觉到网络服务器的短暂停顿。在切换成功后,客户机无需重新登录便可继续使用网络提供的服务和访问服务器上的数据。而对于接在非故障服务器上的客户机,则只会感觉到网络服务稍有减慢而已,不会有任何影响。当故障服务器修复并重新连到网上后,已被迁移到无故障服务器上的服务功能将被返回,恢复正常工作。
两台服务器都可用于处理任务,因而系统效率较高,系统中所有的机器都可用于处理任务,当其中一台发生故障时,系统可指定另一台机器来接替它的工作。
公用磁盘模式
为了减少信息复制的开销,可以将多台计算机连接到一台公共的磁盘系统上去。该公共磁盘被划分为若干个卷。每台计算机使用一个卷。如果某台计算机发生故障,此时系统将重新进行配置,根据某种调度策略来选择另一台替代机器,后者对发生故障的机器的卷拥有所有权,从而来接替故障计算机所承担的任务。消除了信息的复制时间,因而减少了网络和服务器的开销。
访问权
为了对系统中的对象加以保护,应由系统来控制进程对对象的访问。对象可以是硬件对象,也可以是软件对象,我们把一个进程能对某对象执行操作的权力,称为访问权(Access right)。
保护域(Protection Domain)
保护域简称为“域”,“域”是进程对一组对象访问权的集合,进程只能在指定域内执行操作,这样,“域”也就规定了进程所能访问的对象和能执行的操作。
进程和域间的静态联系
在进程和域之间可以一一对应,即一个进程只联系着一个域,这意味着,在进程的整个生命期中,其可用资源是固定的,我们把这种域称为“静态域”,在这种情况下,进程运行的全过程都是受限于同一个域,这将会使赋予进程的访问权超过了实际需要。
进程和域间的动态联系方式
在进程和域之间,也可以是一对多的关系,即一个进程可以联系着多个域,在此情况下,可将进程的运行分为若干个阶段,其每个阶段联系着一个域,这样便可根据运行的实际需要来规定在进程运行的每个阶段中所能访问的对象。
访问矩阵(Access Matrix)
基本的访问矩阵
我们可以利用一个矩阵来描述系统的访问控制,并把该矩阵称为访问矩阵。访问矩阵中的行代表域,列代表对象,矩阵中的每一项是由一组访问权组成的,因为对象已由列显式地定义,故可以只写出访问权而不必写出是对哪个对象的访问权,每一项访问权access(i, j)定义了在域Di中执行的进程能对对象Qj所施加的操作集。
具有域切换权的访问矩阵
为了实现在进程和域之间的动态联系,应能够将进程从一个保护域切换到另一个保护域,为了能对进程进行控制,同样应将切换作为一种权力,仅当进程有切换权时,才能进行这种切换。为此,在访问矩阵中又增加了几个对象,分别把它们作为访问矩阵中的几个域,当且仅当switch∈access(i, j)时,才允许进程从域i切换到域j。
访问矩阵的修改
拷贝权(Copy Right)
我们可利用拷贝权将在某个域中所拥有的访问权(access(i, j))扩展到同一列的其它域中,亦即,为进程在其它的域中也赋予对同一对象的访问权(access(k, j)) 。
所有权(Owner Right)
人们不仅要求能将已有的访问权进行有控制的扩散,而且同样需要能增加某种访问权,或者能删除某种访问权,此时,可利用所有权(O)来实现这些操作。
控制权(Control Right)
拷贝权和所有权都是用于改变矩阵内同一列的各项访问权的,或者说,是用于改变在不同域中运行的进程对同一对象的访问权的。控制权则可用于改变矩阵内同一行中(域中)的各项访问权,亦即,用于改变在某个域中运行的进程对不同对象的访问权的。如果在access(i,j)中包含了控制权,则在域Di中运行的进程可以删除在域Dj中运行的进程对各对象的任何访问权。
访问矩阵的实现
访问控制表(Access Control List)
这是指对访问矩阵按列(对象)划分,为每一列建立一张访问控制表ACL。在该表中,已把矩阵中属于该列的所有空项删除,此时的访问控制表是由一有序对(域,权集)所组成的。由于在大多数情况下,矩阵中的空项远多于非空项,因而使用访问控制表可以显著地减少所占用的存储空间,并能提高查找速度。
访问权限表(Capabilities)
如果把访问矩阵按行(即域)划分,便可由每一行构成一张访问权限表,换言之,这是由一个域对每一个对象可以执行的一组操作所构成的表,表中的每一项即为该域对某对象的访问权限,当域为用户(进程)、对象为文件时,访问权限表便可用来描述一个用户(进程)对每一个文件所能执行的一组操作
事务是用于访问和修改各种数据项的一个程序单位。也可以被看做是一系列相关读和写操作。被访问的数据可以分散地存放在同一文件的不同记录中,也可放在多个文件中。只有对分布在不同位置的同一数据所进行的读和写(含修改)操作全部完成时,才能再以托付操作(Commit Operation)来终止事务。
只要有一个读、写或修改操作失败,便须执行夭折操作(Abort Operation)。读或写操作的失败可能是由于逻辑错误,也可能是系统故障所导致的。
一个夭折的事务,通常已执行了一些操作,因而可能已对某些数据做了修改。为使夭折的事务不会引起数据的不一致性,须将该事务内刚被修改的数据项恢复成原来的情况,使系统中各数据项与该事务未执行时的数据项内容完全相同。此时,可以说该事务“已被退回”(rolled back)。
不难看出,一个事务在对一批数据执行修改操作时,要么全部完成,并用修改后的数据去代替原来的数据,要么一个也不修改。
事务记录(Transaction Record)
为了实现上述的原子修改,通常须借助于称为事务记录的数据结构来实现。这些数据结构被放在稳定存储器中,用来记录在事务运行时数据项修改的全部信息,故又称为运行记录(Log)。
事务名:用于标识该事务的惟一名字;
数据项名:指被修改数据项的惟一名字;
旧值:修改前数据项的值;
新值:修改后数据项将具有的值。
在事务记录表中的每一记录,描述了在事务运行中的重要事务操作。在一个事务 Ti开始执行时, 〈Ti开始〉记录被写入事务记录表中; Ti 执行期间,在 Ti 的任何写(修改)操作之前,便写一适当的新记录到事务记录表中;当 Ti进行托付时,把一个〈Ti托付〉记录写入事务记录表中。
恢复算法
利用事务记录表,系统能处理任何故障而不致使故障造成非易失性存储器中信息的丢失。undo〈Ti〉:该过程把所有被事务 Ti修改过的数据恢复为修改前的值。redo〈Ti〉:该过程把所有被事务 Ti修改过的数据设置为新值。
如果系统发生故障,系统应对以前所发生的事务进行清理。通过查找事务记录表,可以把尚未清理的事务分成两类。一类是其所包含的各类操作都已完成的事务。在事务记录表中,既包含了〈Ti开始〉记录,又包含了〈Ti 托付〉记录。 此时系统利用 redo〈Ti〉过程,把所有已被修改的数据设置成新值。另一类是其所包含的各个操作并未全部完成的事务。在 Log 表中只有〈Ti 开始〉记录而无 〈Ti托付〉记录,此时,系统便利用 undo〈Ti〉过程,将所有已被修改的数据,恢复为修改前的值。
检查点(Check Points)的作用
由于在系统中可能存在着许多并发执行的事务,因而在事务记录表中就会有许多事务执行操作的记录。随着时间的推移,记录的数据也会愈来愈多。因此,一旦系统发生故障,在事务记录表中的记录清理起来就非常费时。 引入检查点的主要目的,是使对事务记录表中事务记录的清理工作经常化。
首先是将驻留在易失性存储器(内存)中的当前事务记录表中的所有记录输出到稳定存储器中;其次是将驻留在易失性存储器中的所有已修改数据输出到稳定存储器中;然后是将事务记录表中的〈检查点〉记录输出到稳定存储器中;最后是每当出现一个〈检查点〉记录时,系统便执行恢复操作,利用 redo 和 undo 过程实现恢复功能。
如果一个事务 Ti 在检查点前就做了托付,则在事务记录表中便会出现一个在检查点记录前的〈Ti 托付〉记录。在这种情况下,所有被 Ti 修改过的数据,或者是在检查点前已写入稳定存储器,或者是作为检查点记录自身的一部分写入稳定存储器中。因此,以后在系统出现故障时,就不必再执行 redo 操作了。
新的恢复算法
在引入检查点后,可以大大减少恢复处理的开销。因为在发生故障后,并不需要对事务记录表中的所有事务记录进行处理,而只需对最后一个检查点之后的事务记录进行处理。因此,恢复例程首先查找事务记录表,确定在最近检查点以前开始执行的最后的事务 Ti。在找到这样的事务后,再返回去搜索事务记录表,便可找到第一个检查点记录。
恢复例程便从该检查点开始,返回搜索各个事务的记录,并利用 redo 和 undo 过程对它们进行处理。 如果把所有在事务 Ti 以后开始执行的事务表示为事务集 T,则新的恢复操作要求是: 对所有在 T 中的事务 TK,如果在事务记录表中出现了〈TK 托付〉记录,则执行 redo〈TK〉 操作;反之,如果在事务记录表中并未出现〈TK托付〉记录,则执行 undo〈TK〉操作。
在多用户系统和计算机网络环境下,可能有多个用户在同时执行事务。由于事务具有原子性,这使各个事务的执行必然是按某种次序依次执行的,只有在一个事务执行完后,才允许另一事务执行,即各事务对数据项的修改是互斥的。人们把这种特性称为顺序性(Serializability)。把用于实现事务顺序性的技术称为并发控制(Concurrent Control)。
利用互斥锁实现“顺序性”
在利用互斥锁实现顺序性时,应为每一个共享对象设置一把互斥锁。 当一事务 Ti要去访问某对象时,应先获得该对象的互斥锁。 若成功,便用该锁将该对象锁住, 于是事务 Ti便可对该对象执行读或写操作;而其它事务由于未能获得该锁而不能访问该对象,如果 Ti需要对一批对象进行访问,为了保证事务操作的原子性,Ti 应先获得这一批对象的互斥锁,以将这些对象全部锁住。如果成功,便可对这一批对象执行读或写操作; 操作完成后又将所有这些锁释放。但如果在这一批对象中的某一个对象已被其它事物锁住,则此时 Ti应对此前已被Ti锁住的其它对象进行开锁,宣布此次事务运行失败,但不致引起数据的变化。
利用互斥锁和共享锁实现顺序性
利用互斥锁实现顺序性的方法简单易行。但这却存在着效率不高的问题。**因为一个共享文件虽然只允许一个事务去写,但却允许多个事务同时去读;而在利用互斥锁来锁住文件后,则只允许一个事务去读。**为了提高运行效率而又引入了共享锁(Shared Lock)。
共享锁与互斥锁的区别在于:
互斥锁仅允许一个事务对相应对象执行读或写操作,共享锁则允许多个事务对相应对象执行读操作,不允许其中任何一个事务对对象执行写操作。 在为一个对象设置了互斥锁和共享锁的情况下,如果事务 Ti 要对 Q 执行读操作,则只需去获得对象 Q 的共享锁。如果对象 Q 已被互斥锁锁住,则 Ti 必须等待;否则,便可获得共享锁而对 Q 执行读操作。如果 Ti 要对 Q 执行写操作,则 Ti还须去获得 Q 的互斥锁。若失败,须等待;否则,可获得互斥锁而对 Q 执行写操作。
为了保证数据的安全性,最常用的做法是把关键文件或数据结构复制多份,分别存储在不同的地方,当主文件(数据结构)失效时,还有备份文件(数据结构)可以使用,不会造成数据丢失,也不会影响系统工作。显然,主文件(数据结构)中的数据应与各备份文件中的对应数据相一致。此外,还有些数据结构(如空闲盘块表)在系统运行过程中,总是不断地对它进行修改,因此,同样应保证不同处的同一数据结构中数据的一致性。
重复文件的一致性
对于通常的 UNIX 文件目录,其每个目录项中含有一个 ASCII 码的文件名和一个索引结点号,后者指向一个索引结点。当有重复文件时,一个目录项可由一个文件名和若干个索引结点号组成,每个索引结点号都是指向各自的索引结点。
在有重复文件时,**如果一个文件拷贝被修改,则必须也同时修改其它几个文件拷贝, 以保证各相应文件中数据的一致性。**当一个文件被修改后,可查找文件目录,以得到其它几个拷贝的索引结点号,再从这些索引结点中找到各拷贝的物理位置,然后对这些拷贝做同样的修改;为新修改的文件建立几个拷贝,并用新拷贝去取代原来的文件拷贝。
盘块号一致性的检查
为了描述盘块的使用情况,通常利用空闲盘块表(链)来记录所有尚未使用的空闲盘块的编号。文件分配表 FAT 则是用于记录已分配盘块的使用情况。由于 OS 经常访问这些数据结构,也对它们进行修改,而如果正在修改时,机器突然发生故障,此时也会使盘块数据结构中的数据产生不一致性现象。因此,在每次启动机器时,都应该检查相应的多个数据结构,看它们之间是否保持了数据的一致性。
为了保证盘块数据结构(中数据)的一致性,可利用软件方法构成一个**计数器表,每个盘块号占一个表项,每一个表项中包含两个计数器,分别用作空闲盘块号计数器和数据盘块号计数器。计数器表中的表项数目等于盘块数 N。在对盘块的数据结构进行检查时,应该先将计数器表中的所有表项初始化为 0,用 N 个空闲盘块号计数器所组成的第一组计数器来对从空闲盘块表(链)中读出的盘块号进行计数;再用 N 个数据盘块号计数器所组成的第二组计数器去对从文件分配表中读出的、已分配给文件使用的盘块号进行计数。**如果情况正常,则上述两组计数器中对应的一对(计数器中的) 数据应该互补,但如果情况并非如此,则说明发生了某种错误。
链接数一致性检查
在 UNIX 类型的文件目录中,其每个目录项内都含有一个索引结点号,用于指向该文件的索引结点。对于一个共享文件,其索引结点号会在目录中出现多次,另一方面,在该共享文件的索引结点中有一个链接计数 count,用来指出共享本文件的用户(进程)数。在正常情况下这两个数据应该一致,否则就会出现数据不一致性差错。
为了检查这种数据不一致性差错,同样要配置一张计数器表,此时应是为每个文件而不是为每个盘块建立一个表项,其中含有该索引结点号的计数值。在进行检查时,从根目录开始查找,每当在目录中遇到该索引结点号时,便在该计数器表中相应文件的表项上加 1。 当把所有目录都检查完后,便可将该计数器表中每个表项中的索引结点号计数值与该文件索引结点中的链接计数 count 值加以比较,如果两者一致,表示是正确的;否则,便是产生了链接数据不一致的错误。
如果索引结点中的链接计数 count 值大于计数器表中相应索引结点号的计数值,则即使在所有共享此文件的用户都不再使用此文件时,其 count 值仍不为 0,因而该文件不会被删除。这种错误的后果是使一些已无用户需要的文件仍驻留在磁盘上,浪费了存储空间。反之,如果出现 count 值小于计数器表中索引结点号计数值的情况时,就有潜在的危险。 假如有两个用户共享一个文件,但是 count 值仍为 1,这样,只要其中有一个用户不再需要此文件时,count 值就会减为 0,从而使系统将此文件删除,并释放其索引结点及文件所占用的盘块,导致另一共享此文件的用户所对应的目录项指向了一个空索引结点,最终是使该用户再无法访问此文件。如果该索引结点很快又被分配给其它文件,则又会带来潜在的危险。解决的方法是将count 值置为正确值。