操作系统原理之文件管理
转载整理自这篇博客
操作系统原理之基础知识
操作系统原理之死锁
操作系统原理之处理机调度
操作系统原理之信号量
操作系统原理之进程和线程管理
操作系统原理之内存管理
文件的概念和定义
文件(File)是操作系统中的一个重要概念。
在系统运行时,计算机以进程为基本单位进行资源的调度和分配;在用户进行的输入、输出中,则以文件为基本单位。
大多数应用程序的输入都是通过文件来实现的,其输出也都保存在文件中,以便信息的长期存及将来的访问。当用户将文件用于应用程序的输入、输出时,还希望可以访问文件、修改文件和保存文件等,实现对文件的维护管理,这就需要系统提供一个文件管理系统,操作系统中的文件系统(File System)就是用于实现用户的这些管理要求。
从用户的角度看,文件系统是操作系统的重要部分之一。用户关心的是如何命名、分类和查找文件,如何保证文件数据的安全性以及对文件可以进行哪些操作等。而对其中的细节,如文件如何存储在辅存上、如何管理文件辅存区域等关心甚少。
文件系统提供了与二级存储相关的资源的抽象,让用户能在不了解文件的各种属性、文件存储介质的特征以及文件在存储介质上的具体位置等情况下,方便快捷地使用文件。
用户通过文件系统建立文件,提供应用程序的输入、输出,对资源进行管理。
首先了解文件的结构,我们通过自底向上的方式来定义:
数据项
数据项是文件系统中最低级的数据组织形式,可分为以下两种类型:
1、基本数据项:用于描述一个对象的某种属性的一个值,如姓名、日期或证件号等,是数据中可命名的最小逻辑数据单位,即原子数据。
2、组合数据项:由多个基本数据项组成。
记录
记录是一组相关的数据项的集合,用于描述一个对象在某方面的属性,如一个考生报名记录包括考生姓名、出生日期、报考学校代号、身份证号等一系列域。
文件
文件是指由创建者所定义的一组相关信息的集合,逻辑上可分为有结构文件和无结构文件两种:
1、在有结构文件中,文件由一组相似记录组成,如报考某学校的所有考生的报考信息记录,又称记录式文件;
2、无结构文件则被看成是一个字符流,比如一个二进制文件或字符文件,又称流式文件。
虽然上面给出了结构化的表述,但实际上关于文件并无严格的定义。通常在操作系统中将程序和数据组织成文件。文件可以是数字、字母或二进制代码,基本访问单元可以是字节、 行或记录。文件可以长期存储于硬盘或其他二级存储器中,允许可控制的进程间共享访问,能够被组织成复杂的结构。Linux中一切皆文件。
文件的属性
文件有一定的属性,这根据系统的不同而有所不同,但是通常都包括如下属性:
①名称:文件名称唯一,以容易读取的形式保存。
②标识符:标识文件系统内文件的唯一标签,通常为数字,它是对人不可读的一种内部名称。
③类型:被支持不同类型的文件系统所使用。
④位置:指向设备和设备上文件的指针。
⑤大小:文件当前大小(用字节、字或块表示),也可包含文件允许的最大值。
⑥保护:对文件进行保护的访问控制信息,比如读写执行权限等。
⑦时间、日期和用户标识:文件创建、上次修改和上次访问的相关信息,用于保护、 安全和跟踪文件的使用。
所有文件的信息都保存在目录结构中,而目录结构也保存在外存上。文件信息当需要时再调入内存。
通常,目录条目包括文件名称和文件的唯一标识符,唯一标识符则定位该文件其他属性信息。
文件的基本橾作
文件属于抽象数据类型。为了恰当地定义文件,就需要考虑有关文件的操作。操作系统提供系统调用,它对文件进行创建、写、读、定位和截断。
①创建文件:创建文件有两个必要步骤,一是在文件系统中为文件找到空间;二是在目录中为新文件创建条目,该条目记录文件名称、在文件系统中的位置及其他可能信息。
②写文件:为了写文件,执行一个系统调用,指明文件名称和要写入文件的内容。对于给定文件名称,系统搜索目录以查找文件位置。系统必须为该文件维护一个写位置的指针。每当发生写操作,便更新写指针。
③读文件:为了读文件,执行一个系统调用,指明文件名称和要读入文件块的内存位置。同样,需要搜索目录以找到相关目录项,系统维护一个读位置的指针。每当发生读操作时,更新读指针。一个进程通常只对一个文件读或写,所以当前操作位置可作为每个进程当前文件位置指针。由于读和写操作都使用同一指针,节省了空间也降低了系统复杂度。
④文件重定位(文件寻址):按某条件搜索目录,将当前文件位置设为给定值,并且不会读、写文件。
⑤删除文件:先从目录中找到要删除文件的目录项,使之成为空项,然后回收该文件所占用的存储空间(一般只是删除目录项,不会再覆写文件内容)。
⑥截断文件:允许文件所有属性不变,并删除文件内容,即将其长度设为0并释放其空间。
这6个基本操作可以组合执行其他文件操作。
例如,一个文件的复制:先创建新文件,再从旧文件读出内容并写入到新文件。
文件的打开与关闭
因为许多文件操作都涉及为给定文件搜索相关目录条目,许多系统要求在首次使用文件时,使用系统调用open,将指明文件的属性(包括该文件在外存上的物理位置)从外存拷贝到内存打开文件目录表的一个表目中,并将该表目的编号(或称为索引)返回给用户。操作系统维护一个包含所有打开的文件信息表(打开文件表,open-file table)。当用户需要一个文件操作时,可通过该表的一个索引指定文件,就省略了搜索环节。当文件不再使用时,进程可以关闭它,操作系统从打开文件表中删除这一条目。
大部分操作系统要求在文件使用之前就被显式地打开。操作open会根据文件名搜索目录,并将目录条目复制到打开文件表。如果调用open的请求(创建、只读、读写、添加等)得到允许,进程就可以打开文件,而open通常返回一个指向打开文件表中的一个条目的指针。通过使用该指计(而非文件名)进行所有I/O操作,以简化步骤并节省资源。
整个系统表包含进程相关信息,如文件在磁盘的位置、访问日期和大小。一个进程打开一个文件,系统打开文件表就会为打开的文件增加相应的条目。当另一个进程执行open时,只不过是在其进程打开表中增加一个条目,并指向整个系统表的相应条目。通常,系统打开文件表的每个文件时,还用一个文件打开计数器(Open Count),以记录多少进程打开了该文件。每个关闭操作close则使count递减,当打开计数器为0时,表示该文件不再被使用,系统将回收分配给该文件的内存空间等资源。若文件被修改过,则将文件写回外存,并将系统打开文件表中相应条目删除,最后释放文件的文件控制块(File Control Block, FCB)。
每个打开的文件都有如下关联信息:
1、文件指针:系统跟踪上次读写位置作为当前文件位置指针,这种指针对打开文件的某个进程来说是唯一的,因此必须与磁盘文件属性分开保存。
2、文件打开计数:文件关闭时,操作系统必须重用其打开文件表条目,否则表内空间会不够用。因为多个进程可能打开同一个文件,所以系统在删除打开文件条目之前,必须等待最后一个进程关闭文件。该计数器跟踪打开和关闭的数量,当该计数为0 时,系统关闭文件,删除该条目。
3、文件磁盘位置:绝大多数文件操作都要求系统修改文件数据。该信息保存在内存中以免为每个操作都从磁盘中读取。
4、访问权限:每个进程打开文件都需要有一个访问模式(创建、只读、读写、添加等)。该信息保存在进程的打开文件表中以便操作系统能允许或拒绝之后的I/O请求。
文件的逻辑结构
文件的逻辑结构是从用户观点出发看到的文件的组织形式。
文件的物理结构是从实现观点出发,又称为文件的存储结构,是指文件在外存上的存储形式。
文件的逻辑结构与存储介质特性无关,但文件的物理结构与存储介质的特性有很大关系。
按逻辑结构,文件有无结构文件和有结构文件两种类型:无结构文件和有结构文件。
无结构文件(流式文件)
无结构文件是最简单的文件组织形式。无结构文件将数据按顺序组织成记录并积累保存,它是有序相关信息项的集合,以字节(Byte)为单位。由于无结构文件没有结构,因而对记录的访问只能通过穷举搜索的方式,故这种文件形式对大多数应用不适用。
但字符流的无结构文件管理简单,用户可以方便地对其进行操作。所以,那些对基本信息单位操作不多的文件较适于釆用字符流的无结构方式,如源程序文件、目标代码文件等。
有结构文件(记录式文件)
有结构文件按记录的组织形式可以分为:顺序文件、索引文件、索引顺序文件、直接文件(散列文件)。
顺序文件
文件中的记录一个接一个地顺序排列,记录可以是定长的或变长的,可以顺序存储或以链表形式存储,在访问时需要顺序搜索文件。
顺序文件有以下两种结构:
第一种是串结构,记录之间的顺序与关键字无关。通常的办法是由时间决定,即按存入时间的先后排列,最先存入的记录作为第1个记录,其次存入的为第2个记录,依此类推。
第二种是顺序结构,指文件中的所有记录按关键字顺序排列。
在对记录进行批量操作时,即每次要读或写一大批记录,对顺序文件的效率是所有逻辑文件中最高的;
此外,也只有顺序文件才能存储在磁带上,并能有效地工作。
但顺序文件对查找、修改、增加或删除单个记录的操作比较困难。
索引文件。
对于定长记录文件,如果要查找第i个记录,可直接根据公式计算来迅速获得第i个记录相对于第一个记录的地址:
对于可变长记录的文件,要查找第i个记录时,必须顺序地查找前i-1个记录,从而获得相应记录的长度L,然后才能按下面的公式计算出第i个记录的首址:
注意:假定每个记录前用一个字节指明该记录的长度。
变长记录文件只能顺序查找,系统开销较大。为此可以建立一张索引表以加快检索速度,索引表本身是定长记录的顺序文件。在记录很多或是访问要求高的文件中,需要引入索引以提供有效的访问。实际中,通过索引可以成百上千倍地提高访问速度。
索引文件示意图:
索引顺序文件。
索引顺序文件是顺序和索引两种组织形式的结合。索引顺序文件将顺序文件中的所有记录分为若干个组,为顺序文件建立一张索引表,在索引表中为每组中的第一个记录建立一个索引项,其中含有该记录的关键字值和指向该记录的指针。
索引顺序文件示意图:
如上图所示,主文件名包含姓名和其他数据项。姓名为关键字,索引表中为每组的第一个记录(不是每个记录)的关键字值,用指计指向主文件中该记录的起始位置。索引表只包含关键字和指计两个数据项,所有姓名关键字递增排列。主文件中记录分组排列,同一个组中关键字可以无序,但组与组之间关键字必须有序。查找一个记录时,通过索引表找到其所在的组,然后在该组中使用顺序查找就能很快地找到记录。
对于含有N个记录的顺序文件,查找某关键字值的记录时平均需要查找N/2次。在索引顺序文件中,假设N个记录分为N1/2组,索引表中有N1/2个表项,每组有N1/2个记录,在查找某关键字值的记录时,先顺序查找索引表,需要查找N1/2/2次,然后再在主文件中对应的组中顺序查找,也需要查找N1/2/2次,这样总共查找N1/2/2+N1/2/2=N1/2次。显然,索引顺序文件提高了查找效率,如果记录数很多,可以釆用两级或多级索引。
索引文件和索引顺序文件都提高了存取的速度,但因为配置索引表而增加了存储空间。
直接文件或散列文件(Hash File)
给定记录的键值或通过Hash函数转换的键值直接决定记录的物理地址。
这种映射结构不同于顺序文件或索引文件,没有顺序的特性。
散列文件有很高的存取速度,但是会引起冲突,即不同关键字的散列函数值相同情况。
文件的目录结构
与文件管理系统和文件集合相关联的是文件目录,它包含有关文件的信息,包括属性、 位置和所有权等,这些信息主要是由操作系统进行管理。
首先我们来看目录管理的基本要求:
从用户的角度看,目录在用户(应用程序)所需要的文件名和文件之间提供一种映射,所以目录管理要实现“按名存取”;目录存取的效率直接影响到系统的性能,所以要提高对目录的检索速度;在共享系统中,目录还需要提供用于控制访问文件的信息。此外,文件允许重名也是用户的合理和必然要求,目录管理通过树形结构来解决和实现。
文件控制块和索引结点
同进程管理一样,为实现目录管理,操作系统中引入了文件控制块的数据结构。
1) 文件控制块。
文件控制块(FCB)是用来存放控制文件需要的各种信息的数据结构,以实现“按名存取”。FCB的有序集合称为文件目录,一个FCB就是一个文件目录项。
为了创建一个新文件,系统将分配一个FCB并存放在文件目录中,成为目录项。
FCB主要包含以下信息:
1、基本信息,如文件名、文件的物理位置、文件的逻辑结构、文件的物理结构等。
2、存取控制信息,如文件存取权限等。
3、使用信息,如文件建立时间、修改时间等。
2) 索引结点。
在检索目录文件的过程中,只用到了文件名,仅当找到一个目录项(查找文件名与目录项中文件名匹配)时,才需要从该目录项中读出该文件的物理地址。
也就是说,在检索目录时,文件的其他描述信息不会用到,也不需调入内存。因此,有的系统(如UNIX,见下表)釆用了文件名和文件描述信息分开的方法,文件描述信息单独形成一个称为索引结点的数据结构,简称为i-Node。在文件目录中的每个目录项仅由文件名和指向该文件所对应的i结点的指针构成。
UNIX的文件目录结构:
一个FCB的大小是64字节,盘块大小是1KB,则在每个盘块中可以存放16个FCB(注意,FCB必须连续存放)。而在UNIX系统中一个目录项仅占16字节,其中14字节是文件名,2字节是i结点指针。在1KB的盘块中可存放64个目录项。这样,可使查找文件时平均启动磁盘次数减少到原来的1/4,大大节省了系统开销。
存放在磁盘上的索引结点称为磁盘索引结点,UNIX中的每个文件都有一个唯一的磁盘索引结点,主要包括以下几个方面:
1、文件主标识符,拥有该文件的个人或小组的标识符。
2、文件类型,包括普通文件、目录文件或特别文件。
3、文件存取权限,各类用户对该文件的存取权限。
4、文件物理地址,每个索引结点中含有13个地址项,即 iaddr(0) ~ iaddr(12),它们以直接或间接方式给出数据文件所在盘块的编号。
5、文件长度,以字节为单位。
6、文件链接计数,在本文件系统中所有指向该文件的文件名的指针计数。
7、文件存取时间,本文件最近被进程存取的时间、最近被修改的时间以及索引结点最近被修改的时间。
8、文件被打开时,磁盘索引结点复制到内存的索引结点中,以便于使用。在内存索引结点中又增加了以下内容:
9、索引结点编号,用于标识内存索引结点。
10、状态,指示i结点是否上锁或被修改。
11、访问计数,每当有一进程要访问此i结点时,计数加1,访问结束减1。
12、逻辑设备号,文件所属文件系统的逻辑设备号。
13、链接指针,设置分别指向空闲链表和散列队列的指针。
目录结构
在理解一个文件系统的需求前,我们首先来考虑在目录这个层次上所需要执行的操作,这有助于后面文件系统的整体理解。
1、搜索:当用户使用一个文件时,需要搜索目录,以找到该文件的对应目录项。
2、创建文件:当创建一个新文件时,需要在目录中增加一个目录项。
3、删除文件:当删除一个文件时,需要在目录中删除相应的目录项。
4、显示目录:用户可以请求显示目录的内容,如显示该用户目录中的所有文件及属性。
5、修改目录:某些文件属性保存在目录中,因而这些属性的变化需要改变相应的目录项。
操作时,考虑以下几种目录结构:
单级目录结构
在整个文件系统中只建立一张目录表,每个文件占一个目录项,如下图所示。
单级目录结构:
当访问一个文件时,先按文件名在该目录中查找到相应的FCB,经合法性检查后执行相应的操作。当建立一个新文件时,必须先检索所有目录项以确保没有“重名”的情况,然后在该目录中增设一项,把FCB的全部信息保存在该项中。当删除一个文件时,先从该目录中找到该文件的目录项,回收该文件所占用的存储空间,然后再清除该目录项。
单级目录结构实现了 “按名存取”,但是存在查找速度慢、文件不允许重名、不便于文件共享等缺点,而且对于多用户的操作系统显然是不适用的。
两级目录结构
单级目录很容易造成文件名称的混淆,可以考虑釆用两级方案,将文件目录分成主文件目录(Master File Directory, MFD)和用户文件目录(User File Directory, UFD)两级,如下图所示。
两级目录结构:
主文件目录项记录用户名及相应用户文件目录所在的存储位置。用户文件目录项记录该用户文件的FCB信息。当某用户欲对其文件进行访问时,只需搜索该用户对应的UFD,这既解决了不同用户文件的“重名”问题,也在一定程度上保证了文件的安全。
两级目录结构可以解决多用户之间的文件重名问题,文件系统可以在目录上实现访问限制。但是两级目录结构缺乏灵活性,不能对文件分类。
多级目录结构(树形目录结构)。
将两级目录结构的层次关系加以推广,就形成了多级目录结构,即树形目录结构,如下图所示。
树形目录结枸:
用户要访问某个文件时用文件的路径名标识文件,文件路径名是个字符串,由从根目录出发到所找文件的通路上的所有目录名与数据文件名用分隔符链接起来而成。
从根目录出发的路径称绝对路径。当层次较多时,每次从根目录查询浪费时间,于是加入了当前目录,进程对各文件的访问都是相对于当前目录进行的,叫相对路径。当用户要访问某个文件时,使用相对路径标识文件,相对路径由从当前目录出发到所找文件通路上所有目录名与数据文件名用分隔符“/”链接而成。
上图是Linux操作系统的目录结构,“/dev/hda”就是一个绝对路径。若当前目录为 “/bin”,则“./ls”就是一个相对路径,其中符号“.”表示当前工作目录。
通常,每个用户都有各自的“当前目录”,登录后自动进入该用户的“当前目录”。操作系统提供一条专门的系统调用,供用户随时改变“当前目录”。
例如,UNIX系统中, “/etc/passwd”文件就包含有用户登录时默认的“当前目录”,可用cd命令改变“当前目录”。
树形目录结构可以很方便地对文件进行分类,层次结构清晰,也能够更有效地进行文件的管理和保护。但是,在树形目录中查找一个文件,需要按路径名逐级访问中间结点,这就增加了磁盘访问次数,无疑将影响查询速度。
无环图目录结构。
树形目录结构可便于实现文件分类,但不便于实现文件共享,为此在树形目录结构的基础上增加了一些指向同一结点的有向边,使整个目录成为一个有向无环图。
引入无环图目录结构是为了实现文件共享,如下图所示。
图形目录结构:
当某用户要求删除一个共享结点时,若系统只是简单地将它删除,当另一共享用户需要访问时,却无法找到这个文件而发生错误。为此可以为每个共享结点设置一个共享计数器,每当图中增加对该结点的共享链时,计数器加1;每当某用户提出删除该结点时,计数器减1。仅当共享计数器为0时,才真正删除该结点,否则仅删除请求用户的共享链。
在linux中存在类似的实现:ln创建链接,下面会详细介绍。
共享文件(或目录)不同于文件拷贝(副本)。如果有两个文件拷贝,每个程序员看到的是拷贝而不是原件;但如果一个文件被修改,那么另一个程序员的拷贝不会有改变。
对于共享文件,只存在一个真正文件,任何改变都会为其他用户所见。
无环图目录结构方便实现了文件的共享,但使得系统的管理变得更加复杂。
共享文件
文件共享使多个用户(进程)共享同一份文件,系统中只需保留该文件的一份副本。如果系统不能提供共享功能,那么每个需要该文件的用户都要有各自的副本,会造成对存储空间的极大浪费。随着计算机技术的发展,文件共享的范围已由单机系统发展到多机系统,进而通过网络扩展到全球。这些文件的分享是通过分布式文件系统、远程文件系统、分布式信息系统实现的。这些系统允许多个客户通过C/S模型共享网络中的服务器文件。
现代常用的两种文件共享方法有:
基于索引结点的共享方式(硬链接)
在树形结构的目录中,当有两个或多个用户要共享一个子目录或文件时,必须将共享文件或子目录链接到两个或多个用户的目录中,才能方便地找到该文件,如下图所示。
基于索引结点的共享方式:
在这种共享方式中引用索引结点,即诸如文件的物理地址及其他的文件属性等信息,不再是放在目录项中,而是放在索引结点中。在文件目录中只设置文件名及指向相应索引结点的指针。在索引结点中还应有一个链接计数count,用于表示链接到本索引结点(亦即文件)上的用户目录项的数目。当count=2时,表示有两个用户目录项链接到本文件上,或者说是有两个用户共享此文件。
当用户A创建一个新文件时,它便是该文件的所有者,此时将count置为1。当有用户B要共享此文件时,在用户B的目录中增加一个目录项,并设置一指针指向该文件的索引结点。此时,文件主仍然是用户A,count=2。如果用户A不再需要此文件,不能将文件直接删除。因为,若删除了该文件,也必然删除了该文件的索引结点,这样便会便用户B的指针悬空,而用户B则可能正在此文件上执行写操作,此时用户B会无法访问到文件。因此用户A不能删除此文件,只是将该文件的count减1,然后删除自己目录中的相应目录项。用户B仍可以使用该文件。当count=0时,表示没有用户使用该文件,系统将负责删除该文件。
如下图给出了用户B链接到文件上的前、后情况。
文件共享中的链接计数
利用符号链实现文件共享(软链接)
为使用户B能共享用户A的一个文件F,可以由系统创建一个LINK类型的新文件,也取名为F,并将文件F写入用户B的目录中,以实现用户B的目录与文件F的链接。
在新文件中只存储被链接文件F的路径名。这样的链接方法被称为符号链接。
新文件中的路径名则只被看做是符号链,当用户B要访问被链接的文件F且正要读LINK类新文件时,操作系统根据新文件中的路径名去读该文件,从而实现了用户B对文件F的共享。
在利用符号链方式实现文件共享时,只有文件的拥有者才拥有指向其索引结点的指针。而共享该文件的其他用户则只有该文件的路径名,并不拥有指向其索引结点的指针。这样,也就不会发生在文件主删除一共享文件后留下一悬空指针的情况。当文件的拥有者把一个共享文件删除后,其他用户通过符号链去访问它时,会出现访问失败,于是将符号链删除,此时不会产生任何影响。当然,利用符号链实现文件共享仍然存在问题,例如:一个文件釆用符号链方式共享,当文件拥有者将其删除,而在共享的其他用户使用其符号链接访问该文件之前,又有人在同一路径下创建了另一个具有同样名称的文件,则该符号链将仍然有效,但访问的文件已经改变,从而导致问题。
在符号链的共享方式中,当其他用户读共享文件时,需要根据文件路径名逐个地查找目录,直至找到该文件的索引结点。因此,每次访问时,都可能要多次地读盘,使得访问文件的开销变大并增加了启动磁盘的频率。此外,符号链的索引结点也要耗费一定的磁盘空间。符号链方式有一个很大的优点,即网络共享只需提供该文件所在机器的网络地址以及该机器中的文件路径即可。
软链接:创建一个新的文件LINK,文件内存储目标文件的路径,访问这个LINK文件,读取存储的路径后再去访问目标文件。删除软链接并不会删除目标文件,反过来删除目标文件,会导致LINK文件无法读取到目标文件,但LINK文件并不会消失。
硬链接:增加原来的目标文件的一个索引,读取文件就是对原文件的操作。硬链接有一个计数器,删除文件只是将计数器-1,直到计数器为0时,文件才会被真正删除。
上述两种链接方式都存在一个共同的问题,即每个共享文件都有几个文件名。换言之,每增加一条链接,就增加一个文件名。这实质上就是每个用户都使用自己的路径名去访问共享文件。当我们试图去遍历整个文件系统时,将会多次遍历到该共享文件。
硬链接和软链接都是文件系统中的静态共享方法,是属于存储的物理共享。在文件系统中还存在着另外的共享需求,即两个进程同时对同一个文件进行操作,这样的共享可以称为动态共享。
文件保护
为了防止文件共享可能会导致文件被破坏或未经核准的用户修改文件,文件系统必须控制用户对文件的存取,即解决对文件的读、写、执行的许可问题。
为此,必须在文件系统中建立相应的文件保护机制。
文件保护通过口令保护、加密保护和访问控制等方式实现。
其中,口令保护和加密保护是为了防止用户文件被他人存取或窃取,而访问控制则用于控制用户对文件的访问方式。
访问类型
对文件的保护可以从限制对文件的访问类型中出发。可加以控制的访问类型主要有以下几种:
1、读:从文件中读。
2、写:向文件中写。
3、执行:将文件装入内存并执行。
4、添加:将新信息添加到文件结尾部分。
5、删除:删除文件,释放空间。
6、列表清单:列出文件名和文件属性。
此外还可以对文件的重命名、复制、编辑等加以控制。这些高层的功能可以通过系统程序调用低层系统调用来实现。保护可以只在低层提供。
例如,复制文件可利用一系列的读请求来完成。这样,具有读访问用户同时也具有复制和打印的权限了。
访问控制
解决访问控制最常用的方法是根据用户身份进行控制。而实现基于身份访问的最为普通的方法是为每个文件和目录增加一个访问控制列表(Access-Control List, ACL),以规定每个用户名及其所允许的访问类型。
这种方法的优点是可以使用复杂的访同方法。其缺点是长度无法预期并且可能导致复杂的空间管理,使用精简的访问列表可以解决这个问题。
精简的访问列表釆用拥有者、组和其他三种用户类型。
1、拥有者:创建文件的用户。
2、组:一组需要共享文件且具有类似访问的用户。
3、其他:系统内的所有其他用户。
这样只需用三个域列出访问表中这三类用户的访问权限即可。文件拥有者在创建文件时,说明创建者用户名及所在的组名,系统在创建文件时也将文件主的名字、所属组名列在该文件的FCB中。用户访问该文件时,按照拥有者所拥有的权限访问文件,如果用户和拥有者在同一个用户组则按照同组权限访问,否则只能按其他用户权限访问。UNIX操作系统即釆用此种方法。
口令和密码是另外两种访问控制方法。
口令指用户在建立一个文件时提供一个口令,系统为其建立FCB时附上相应口令,同时告诉允许共享该文件的其他用户。用户请求访问时必须提供相应口令。这种方法时间和空间的开销不多,缺点是口令直接存在系统内部,不够安全。
密码指用户对文件进行加密,文件被访问时需要使用密钥。这种方法保密性强,节省了存储空间,不过编码和译码要花费一定时间。
口令和密码都是防止用户文件被他人存取或窃取,并没有控制用户对文件的访问类型。不过单纯的口令和密码是不够的,加密文件的内容才是更安全的做法。
注意两个问题:
1、现代操作系统常用的文件保护方法,是将访问控制列表与用户、组和其他成员访问控制方案一起组合使用。
2、对于多级目录结构而言,不仅需要保护单个文件,而且还需要保护子目录内的文件, 即需要提供目录保护机制。目录操作与文件操作并不相同,因此需要不同的保护机制。
文件系统层次结构
现代操作系统有多种文件系统类型(如FAT32、NTFS、 ext2、ext3、ext4等),因此文件系统的层次结构也不尽相同。
文件系统层次结构:
1) 用户调用接口
文件系统为用户提供与文件及目录有关的调用,如新建、打开、读写、关闭、删除文件,建立、删除目录等。此层由若干程序模块组成,每一模块对应一条系统调用,用户发出系统调用时,控制即转入相应的模块。
2) 文件目录系统
文件目录系统的主要功能是管理文件目录,其任务有管理活跃文件目录表、管理读写状态信息表、管理用户进程的打开文件表、管理与组织在存储设备上的文件目录结构、调用下一级存取控制模块。
3) 存取控制验证
实现文件保护主要由该级软件完成,它把用户的访问要求与FCB中指示的访问控制权限进行比较,以确认访问的合法性。
4) 逻辑文件系统与文件信息缓冲区
逻辑文件系统与文件信息缓冲区的主要功能是根据文件的逻辑结构将用户要读写的逻辑记录转换成文件逻辑结构内的相应块号。
5) 物理文件系统
物理文件系统的主要功能是把逻辑记录所在的相对块号转换成实际的物理地址。
6) 分配模块
分配模块的主要功能是管理辅存空间,即负责分配辅存空闲空间和回收辅存空间。
7) 设备管理程序模块
设备管理程序模块的主要功能是分配设备、分配设备读写用缓冲区、磁盘调度、启动设备、处理设备中断、释放设备读写缓冲区、释放设备等。
文件系统的实现
目录实现
在读文件前,必须先打开文件。
打开文件时,操作系统利用路径名找到相应目录项,目录项中提供了查找文件磁盘块所需要的信息。
目录实现的基本方法有线性列表和哈希表两种。
线性列表
最简单的目录实现方法是使用存储文件名和数据块指针的线性表。创建新文件时,必须首先搜索目录表以确定没有同名的文件存在,然后在目录表后增加一个目录项。删除文件则 根据给定的文件名搜索目录表,接着释放分配给它的空间。若要重用目录项,有许多方法:可以将目录项标记为不再使用,或者将它加到空闲目录项表上,还可以将目录表中最后一个目录项复制到空闲位置,并降低目录表长度。釆用链表结构可以减少删除文件的时间。其优点在于实现简单,不过由于线性表的特殊性,比较费时。
哈希表
哈希表根据文件名得到一个值,并返回一个指向线性列表中元素的指针。这种方法的优点是查找非常迅速,插入和删除也较简单,不过需要一些预备措施来避免冲突。最大的困难是哈希表长度固定以及哈希函数对表长的依赖性。
目录查询是通过在磁盘上反复搜索完成,需要不断地进行I/O操作,开销较大。所以如前面所述,为了减少I/O操作,把当前使用的文件目录复制到内存,以后要使用该文件时只要在内存中操作,从而降低了磁盘操作次数,提高了系统速度。
文件实现
文件分配方式
文件分配对应于文件的物理结构,是指如何为文件分配磁盘块。常用的磁盘空间分配方法有三种:连续分配、链接分配和索引分配。
有的系统(如RDOS操作系统)对三种方法都支持,但是更普遍的是一个系统只提供一种方法的支持。
1) 连续分配
连续分配方法要求每个文件在磁盘上占有一组连续的块,如下图所示。
磁盘地址定义了磁盘上的一个线性排序。这种排序使作业访问磁盘时需要的寻道数和寻道时间最小。
连续分配:
文件的连续分配可以用第一块的磁盘地址和连续块的数量来定义。如果文件有n块长并从位置b开始,那么该文件将占有块b, b+1, b+2, …, b+n-1。
一个文件的目录条目包括开始块的地址和该文件所分配区域的长度。
连续分配支持顺序访问和直接访问。其优点是实现简单、存取速度快。缺点在于,文件长度不宜动态增加,因为一个文件末尾后的盘块可能已经分配给其他文件,一旦需要增加,就需要大量移动盘块。此外,反复增删文件后会产生外部碎片(与内存管理分配方式中的碎片相似),并且很难确定一个文件需要的空间大小,因而只适用于长度固定的文件。
2) 链接分配
链接分配是釆取离散分配的方式,消除了外部碎片,故而显著地提高了磁盘空间的利用率;又因为是根据文件的当前需求,为它分配必需的盘块,当文件动态增长时,可以动态地再为它分配盘块,故而无需事先知道文件的大小。此外,对文件的增、删、改也非常方便。链接分配又可以分为隐式链接和显式链接两种形式。
隐式连接如下图所示。每个文件对应一个磁盘块的链表;磁盘块分布在磁盘的任何地方,除最后一个盘块外,每一个盘块都有指向下一个盘块的指针,这些指针对用户是透明的。目录包括文件第一块的指针和最后一块的指针。
隐式链接分配:
创建新文件时,目录中增加一个新条目。每个目录项都有一个指向文件首块的指针。该指针初始化为NULL以表示空文件,大小字段为0。写文件会通过空闲空间管理系统找到空闲块,将该块链接到文件的尾部,以便写入。读文件则通过块到块的指针顺序读块。
隐式链接分配的缺点在于无法直接访问盘块,只能通过指针顺序访问文件,以及盘块指针消耗了一定的存储空间。隐式链接分配的稳定性也是一个问题,系统在运行过程中由于软 件或者硬件错误导致链表中的指针丢失或损坏,会导致文件数据的丢失。
显式链接是指把用于链接文件各物理块的指针,显式地存放在内存的一张链接表中。 该表在整个磁盘仅设置一张,每个表项中存放链接指针,即下一个盘块号。在该表中,凡是属于某一文件的第一个盘块号,或者说是每一条链的链首指针所对应的盘块号,均作为文件地址被填入相应文件的FCB的“物理地址”字段中。由于查找记录的过程是在内存中进行的,因而不仅显著地提高了检索速度,而且大大减少了访问磁盘的次数。由于分配给文件的所有盘块号都放在该表中,故称该表为文件分配表(File Allocation Table, FAT)。
3) 索引分配。
链接分配解决了连续分配的外部碎片和文件大小管理的问题。但是,链接分配不能有效支持直接访问(FAT除外)。索引分配解决了这个问题,它把每个文件的所有的盘块号都集中放在一起构成索引块(表),如下图所示。
索引分配:
每个文件都有其索引块,这是一个磁盘块地址的数组。索引块的第i个条目指向文件的第i个块。目录条目包括索引块的地址。要读第i块,通过索引块的第i个条目的指针来查找和读入所需的块。
创建文件时,索引块的所有指针都设为空。当首次写入第i块时,先从空闲空间中取得一个块,再将其地址写到索引块的第i个条目。索引分配支持直接访问,且没有外部碎片问 题。其缺点是由于索引块的分配,增加了系统存储空间的开销。索引块的大小是一个重要的问题,每个文件必须有一个索引块,因此索引块应尽可能小,但索引块太小就无法支持大文件。
可以釆用以下机制来处理这个问题。
链接方案:一个索引块通常为一个磁盘块,因此,它本身能直接读写。为了处理大文件,可以将多个索引块链接起来。
多层索引:多层索引使第一层索引块指向第二层的索引块,第二层索引块再指向文件块。这种方法根据最大文件大小的要求,可以继续到第三层或第四层。例如,4096B的块,能在 索引块中存入1024个4B的指针。两层索引允许1048576个数据块,即允许最大文件为4GB。
混合索引:将多种索引分配方式相结合的分配方式。例如,系统既釆用直接地址,又采用单级索引分配方式或两级索引分配方式。
三种分配方式的比较:
文件存储空间管理
1) 文件存储器空间的划分与初始化。
一般来说,一个文件存储在一个文件卷中。文件卷可以是物理盘的一部分,也可以是整个物理盘,支持超大型文件的文件卷也可以由多个物理盘组成,如下图所示。
逻辑卷与物理盘的关系:
在一个文件卷中,文件数据信息的空间(文件区)和存放文件控制信息FCB的空间(目 录区)是分离的。由于存在很多种类的文件表示和存放格式,所以现代操作系统中一般都有 很多不同的文件管理模块,通过它们可以访问不同格式的逻辑卷中的文件。逻辑卷在提供文件服务前,必须由对应的文件程序进行初始化,划分好目录区和文件区,建立空闲空间管理 表格及存放逻辑卷信息的超级块。
2) 文件存储器空间管理。
文件存储设备分成许多大小相同的物理块,并以块为单位交换信息,因此,文件存储设备的管理实质上是对空闲块的组织和管理,它包括空闲块的组织、分配与回收等问题。
①空闲表法
空闲表法属于连续分配方式,它与内存的动态分配方式类似,为每个文件分配一块连续的存储空间。系统为外存上的所有空闲区建立一张空闲盘块表,每个空闲区对应于一个空闲 表项,其中包括表项序号、该空闲区第一个盘块号、该区的空闲盘块数等信息。再将所有空闲区按其起始盘块号递增的次序排列,见下表。
空闲盘块表:
空闲盘区的分配与内存的动态分配类似,同样是釆用首次适应算法、循环首次适应算法等。例如,在系统为某新创建的文件分配空闲盘块时,先顺序地检索空闲盘块表的各表项,直至找到第一个其大小能满足要求的空闲区,再将该盘区分配给用户,同时修改空闲盘块表。 系统在对用户所释放的存储空间进行回收时,也釆取类似于内存回收的方法,即要考虑回收区是否与空闲表中插入点的前区和后区相邻接,对相邻接者应予以合并。
②空闲链表法
将所有空闲盘区拉成一条空闲链,根据构成链所用的基本元素不同,可把链表分成两种形式:空闲盘块链和空闲盘区链。
空闲盘块链是将磁盘上的所有空闲空间,以盘块为单位拉成一条链。当用户因创建文件 而请求分配存储空间时,系统从链首开始,依次摘下适当的数目的空闲盘块分配给用户。当 用户因删除文件而释放存储空间时,系统将回收的盘块依次插入空闲盘块链的末尾。这种方法的优点是分配和回收一个盘块的过程非常简单,但在为一个文件分配盘块时,可能要重复多次操作。
空闲盘区链是将磁盘上的所有空闲盘区(每个盘区可包含若干个盘块)拉成一条链。在每个盘区上除含有用于指示下一个空闲盘区的指针外,还应有能指明本盘区大小(盘块数) 的信息。分配盘区的方法与内存的动态分区分配类似,通常釆用首次适应算法。在回收盘区时,同样也要将回收区与相邻接的空闲盘区相合并。
③位示图法
位示图是利用二进制的一位来表示磁盘中一个盘块的使用情况,磁盘上所有的盘块都有 一个二进制位与之对应。当其值为“0”时,表示对应的盘块空闲;当其值为“1”时,表示对应的盘块已分配。
盘块的分配:
1、顺序扫描位示图,从中找出一个或一组其值为“0”的二进制位。
2、将所找到的一个或一组二进制位,转换成与之对应的盘块号。假定找到的其值为“0” 的二进制位,位于位示图的第i行、第j列,则其相应的盘块号应按下式计算(n代表每行 的位数):
b = n (i-1) + j
3、修改位示图,令map[i, j] = 1。
盘块的回收:
1、将回收盘块的盘块号转换成位示图中的行号和列号。
2、转换公式为
i=(b-1)DIVn+l
j=(b-l)MOD n+1
3、修改位示图,令map[i, j] = 0。
④成组链接法
空闲表法和空闲链表法都不适合用于大型文件系统,因为这会使空闲表或空闲链表太大。在UNIX系统中釆用的是成组链接法,这种方法结合了空闲表和空闲链表两种方法,克服了表太大的缺点。其大致的思想是:
把顺序的n个空闲扇区地址保存在第一个空闲扇区内, 其后一个空闲扇区内则保存另一顺序空闲扇区的地址,如此继续,直至所有空闲扇区均予以链接。系统只需要保存一个指向第一个空闲扇区的指针。假设磁盘最初全为空闲扇区;其成组链接如下图所示。通过这种方式可以迅速找到大批空闲块地址。
成组链接法示意图:
表示文件存储器空闲空间的“位向量”表或第一个成组链块以及卷中的目录区、文件区划分信息都需要存放在辅存储器中,一般放在卷头位置,在UNIX系统中称为“超级块”。
在对卷中文件进行操作前,“超级块”需要预先读入系统空间的主存,并且经常保持主存“超 级块”与辅存卷中“超级块”的一致性。
注意:本书如无特别提示,所使用的位示图法,行和列都是从1开始编号。特别注意,如果题目中指明从0开始编号,则上述的计算方法要进行相应调整。
磁盘的结构
磁盘(Disk)是由表面涂有磁性物质的金属或塑料构成的圆形盘片,通过一个称为磁头的导体线圈从磁盘中存取数据。在读/写操作期间,磁头固定,磁盘在下面高速旋转。
如下所示,磁盘的盘面上的数据存储在一组同心圆中,称为磁道。每个磁道与磁头一样宽, 一个盘面有上千个磁道。磁道又划分为几百个扇区,每个扇区固定存储大小(通常为512B), 一个扇区称为一个盘块。相邻磁道及相邻扇区间通过一定的间隙分隔开,以避免精度错误。
注意,由于扇区按固定圆心角度划分,所以密度从最内而外道递减,磁盘的存储能力受限于最内道的最大密度。
磁盘安装在一个磁盘驱动器中,它由磁头臂、用于旋转磁盘的主轴和用于数据输入/输出的电子设备组成。如下图所示,多个盘片垂直堆叠,组成磁盘组,每个盘面对应一个磁头,所有磁头固定在一起,与磁盘中心的距离相同且一起移动。所有盘片上相对位置相同的磁道组成柱面。按照这种物理结构组织,扇区就是磁盘可寻址的最小存储单位,磁盘地址用“柱面号 • 盘面号 • 扇区号(或块号)”表示。
磁盘按不同方式可以分为若干类型:磁头相对于盘片的径向方向固定的称为固定头磁盘,每个磁道一个磁头;磁头可移动的称为活动头磁盘,磁头臂可以来回伸缩定位磁道。磁盘永久固定在磁盘驱动器内的称为固定盘磁盘;可移动和替换的称为可换盘磁盘。
磁盘调度算法
一次磁盘读写操作的时间由寻找(寻道)时间、延迟时间和传输时间决定:
1) 寻找时间Ts:活动头磁盘在读写信息前,将磁头移动到指定磁道所需要的时间。这个时间除跨越n条磁道的时间外,还包括启动磁臂的时间s,即:
式中,m是与磁盘驱动器速度有关的常数,约为0.2ms,磁臂的启动时间约为2ms。
2)延迟时间Tr:磁头定位到某一磁道的扇区(块号)所需要的时间,设磁盘的旋转速度为r,则:
对于硬盘,典型的旋转速度为5400r/m,相当于一周11.1ms,则Tr为5.55ms;对于软盘,其旋转速度在300~600r/m之间,则Tr为50~100ms。
3) 传输时间Tt:从磁盘读出或向磁盘写入数据所经历的时间,这个时间取决于每次所读/写的字节数b和磁盘的旋转速度:
式中,r为磁盘每秒钟的转数;N为一个磁道上的字节数。
在磁盘存取时间的计算中,寻道时间与磁盘调度算法相关,下面将会介绍分析几种算法,而延迟时间和传输时间都与磁盘旋转速度相关,且为线性相关,所以在硬件上,转速是磁盘性能的一个非常重要的参数。
总平均存取时间Ta可以表示为:
虽然这里给出了总平均存取时间的公式,但是这个平均值是没有太大实际意义的,因为在实际的磁盘I/O操作中,存取时间与磁盘调度算法密切相关。
调度算法直接决定寻找时间,从而决定了总的存取时间。
目前常用的磁盘调度算法有以下几种:
先来先服务(First Come First Served, FCFS)算法
FCFS算法根据进程请求访问磁盘的先后顺序进行调度,这是一种最简单的调度算法,如下图所示。该算法的优点是具有公平性。如果只有少量进程需要访问,且大部分请求都是访问簇聚的文件扇区,则有望达到较好的性能;但如果有大量进程竞争使用磁盘,那么这种算法在性能上往往接近于随机调度。所以,实际磁盘调度中考虑一些更为复杂的调度算法。
FCFS磁盘调度算法:
例如,磁盘请求队列中的请求顺序分别为55、58、39、18、90、160、150、38、184,磁头初始位置是100磁道,釆用FCFS算法磁头的运动过程如上图所示。
磁头共移动了 (45+3+19+21+72+70+10+112+146)=498 个磁道,平均寻找长度=498/9=55.3。
最短寻找时间优先(Shortest Seek Time First, SSTF)算法
SSTF算法选择调度处理的磁道是与当前磁头所在磁道距离最近的磁道,以使每次的寻找时间最短。当然,总是选择最小寻找时间并不能保证平均寻找时间最小,但是能提供比 FCFS算法更好的性能。这种算法会产生“饥饿”现象。如图4-26所示,若某时刻磁头正在 18号磁道,而在18号磁道附近频繁地增加新的请求,那么SSTF算法使得磁头长时间在18 号磁道附近工作,将使184号磁道的访问被无限期地延迟,即被“饿死”。
SSTF磁盘调度算法:
例如,磁盘请求队列中的请求顺序分别为55、58、39、18、90、160、150、38、184,磁头初始位置是100磁道,釆用SSTF算法磁头的运动过程如上图所示。
磁头共移动了 (10+32+3+16+1+20+132+10+24)=248 个磁道,平均寻找长度=248/9=27.5。
扫描(SCAN)算法(又称电梯算法)
SCAN算法在磁头当前移动方向上选择与当前磁头所在磁道距离最近的请求作为下一次服务的对象,如下图所示。由于磁头移动规律与电梯运行相似,故又称为电梯调度算法。SCAN算法对最近扫描过的区域不公平,因此,它在访问局部性方面不如FCFS算法和 SSTF算法好。
SCAN磁盘调度算法:
例如,磁盘请求队列中的请求顺序分别为55、58、39、18、90、160、150、38、184,磁头初始位置是100 磁道。釆用SCAN算法时,不但要知道磁头的当前位置,还要知道磁头的移动方向,假设磁头沿磁道号增大的顺序移动,则磁头的运动过程如上图所示。
磁头共移动了(50+10+24+94+32+3+16+1+20)=250 个磁道,平均寻找长度=250/9=27.8。
循环扫描(Circulair SCAN, C-SCAN)算法
在扫描算法的基础上规定磁头单向移动来提供服务,回返时直接快速移动至起始端而不服务任何请求。由于SCAN算法偏向于处理那些接近最里或最外的磁道的访问请求,所以使用改进型的C-SCAN算法来避免这个问题。
釆用SCAN算法和C-SCAN算法时磁头总是严格地遵循从盘面的一端到另一端,显然,在实际使用时还可以改进,即磁头移动只需要到达最远端的一个请求即可返回,不需要到达磁盘端点。这种形式的SCAN算法和C-SCAN算法称为LOOK和C-LOOK调度。这是因为它们在朝一个给定方向移动前会查看是否有请求。注意,若无特别说明,也可以默认SCAN算法和C-SCAN算法为LOOK和C-LOOK调度。
C-SCAN磁盘调度算法:
例如,磁盘请求队列中的请求顺序分别为55、58、39、18、90、160、150、38、184,磁头初始位置是100磁道。釆用C-SCAN算法时,假设磁头沿磁道号增大的顺序移动,则磁头的运动过程如图4-28所示。
磁头共移动了(50+10+24+166+20+1+16+3+32)=322个磁道,平均寻道长度=322/9=35.8。
对比以上几种磁盘调度算法:
FCFS算法太过简单,性能较差,仅在请求队列长度接近于1时才较为理想;
SSTF算法较为通用和自然;
SCAN算法和C-SCAN算法在磁盘负载较大时比较占优势。
除减少寻找时间外,减少延迟时间也是提高磁盘传输效率的重要因素。
可以对盘面扇区进行交替编号,对磁盘片组中的不同盘面错位命名。
假设每个盘面有8个扇区,磁盘片组共8个盘面,则可以釆用如下图所示的编号。
磁盘片组扇区编号:
磁盘是连续自转设备,磁头读/写一个物理块后,需要经过短暂的处理时间才能开始读/写下一块。假设逻辑记录数据连续存放在磁盘空间中,若在盘面上按扇区交替编号连续存放,则连续读/写多个记录时能减少磁头的延迟时间;同柱面不同盘面的扇区若能错位编号,连续读/写相邻两个盘面的逻辑记录时也能减少磁头延迟时间。
由于传输时间由磁盘转速决定,所以无法通过其他方法减少传输时间。以上图为例,在随机扇区访问情况下,定位磁道中的一个扇区平均需要转过4个扇区,这时,延迟时间是传输时间的4倍,这是一种非常低效的存取方式。理想化的情况是不需要定位而直接连续读取扇区,没有延迟时间,这样磁盘数据存取效率可以成倍提高。但是由于读取扇区的顺序是不可预测的,所以延迟时间不可避免。上图中的编号方式是读取连续编号扇区时的一种方法。
磁盘的管理
磁盘初始化
一个新的磁盘只是一个含有磁性记录材料的空白盘。在磁盘能存储数据之前,它必须分成扇区以便磁盘控制器能进行读和写操作,这个过程称为低级格式化(物理分区)。低级格式化为磁盘的每个扇区釆用特别的数据结构。每个扇区的数据结构通常由头、数据区域(通常为512B大小)和尾部组成。头部和尾部包含了一些磁盘控制器所使用的信息。
为了使用磁盘存储文件,操作系统还需要将自己的数据结构记录在磁盘上:第一步将磁盘分为由一个或多个柱面组成的分区(即我们熟悉的C盘、D盘等形式的分区);第二步对物理分区进行逻辑格式化(创建文件系统),操作系统将初始的文件系统数据结构存储到磁盘上,这些数据结构包括空闲和已分配的空间以及一个初始为空的目录。
引导块
计算机启动时需要运行一个初始化程序(自举程序),它初始化CPU、寄存器、设备控制器和内存等,接着启动操作系统。为此,该自举程序应找到磁盘上的操作系统内核,装入内存,并转到起始地址,从而开始操作系统的运行。
自举程序通常保存在ROM中,为了避免改变自举代码需要改变ROM硬件的问题,故只在ROM中保留很小的自举装入程序,将完整功能的自举程序保存在磁盘的启动块上,启动块位于磁盘的固定位(会给予保护)。拥有启动分区的磁盘称为启动磁盘或者系统磁盘。
坏块
由于磁盘有移动部件且容错能力弱,所以容易导致一个或多个扇区损坏。部分磁盘甚至从出厂时就有坏扇区。根据所使用的磁盘和控制器,对这些块有多种处理方式。
对于简单磁盘,如电子集成驱动器(IDE)。坏扇区可手工处理,如MS-DOS的Format命令执行逻辑格式化时便会扫描磁盘以检查坏扇区。坏扇区在FAT表上会标明,因此程序不会使用。
对于复杂的磁盘,如小型计算机系统接口(SCSI),其控制器维护一个磁盘坏块链表。该链表在出厂前进行低级格式化时就初始化了,并在磁盘的整个使用过程中不断更新。
低级格式化将一些块保留作为备用,对操作系统透明。控制器可以用备用块来逻辑地替代坏块,这种方案称为扇区备用。
总结思考
磁盘结构
引导控制块(Boot Control Block)包括系统从该分区引导操作系统所需要的信息。
如果磁盘没有操作系统,那么这块的内容为空。它通常为分区的第一块。
UFS称其为引导块(Boot Block);
NTFS称其为分区引导扇区(Partition Boot Sector)。
分区控制块(Partition Control Block)包括分区详细信息,如分区的块数、块的大小、空闲块的数量和指计、空闲FCB的数量和指针等。
UPS称其为超级块(Superblock);
NTFS称其为主控文件表(Master File Table)。
内存结构
内存分区表包含所有安装分区的信息。
内存目录结构用来保存近来访问过的目录信息。对安装分区的目录,可以包括一个指向分区表的指针。
系统范围的打开文件表,包括每个打开文件的FCB复制和其他信息。
单个进程的打开文件表,包括一个指向系统范围内已打开文件表中合适条目和其他信息的指针。
文件系统实现概述
为了创建一个文件,应用程序调用逻辑文件系统。逻辑文件系统知道目录结构形式,它将分配一个新的FCB给文件,把相应目录读入内存,用新的文件名更新该目录和FCB,并将结果写回到磁盘。
典型的FCB:
一旦文件被创建,它就能用于I/O,不过首先要打开文件。调用open将文件名传给文件系统,文件系统根据给定文件名搜索目录结构。部分目录结构通常缓存在内存中以加快目录 操作。找到文件后,其FCB复制到系统范围的打开文件表。该表不但存储FCB,也有打开该文件的进程数量的条目。
然后,单个进程的打开文件表中会增加一个条目,并通过指针将系统范围的打开文件表的条目同其他域(文件当前位置的指针和文件打开模式等)相连。调用open返回的是一个 指向单个进程的打开文件表中合适条目的指针。所有文件操作都是通过该指针进行。
文件名不必是打开文件表的一部分,因为一旦完成对FCB在磁盘上的定位,系统就不再使用文件名了。对于访问打开文件表的索引,UNIX称之为文件描述符(File Descriptor);而Windows 2000称之为文件句柄(File Handle)。因此,只要文件没有被关闭,所有文件操作通过打开文件表查表来进行。
当一个进程关闭文件,就删除一个相应的单个进程打开文件表的条目即目录项,系统范围内打开文件表的打开数也会递减。
当打开文件的所有用户都关闭了一个文件时,更新的文件信息会复制到磁盘的目录结构中,系统范围的打开文件表的条目也将删除。
在实际中,系统调用open会首先搜索系统范围的打开文件表以确定某文件是否已被其他进程所使用。
如果是,就在单个进程的打开文件表中创建一项,并指向现有系统范围的打开文件表的相应条目。
该算法在文件已打开时,能节省大量开销。
混合索引分配的实现
混合索引分配已在UNIX系统中釆用。
在UNK SystemV的索引结点中,共设置了13个地址项,即iaddr(O)~iaddr(12),如下图所示。
在BSD UNIX的索引结点中,共设置了13个地址项,它们都把所有的地址项分成两类,即直接地址和间接地址。
UNIX系统的inode结构示意图:
1) 直接地址
为了提高对文件的检索速度,在索引结点中可设置10个直接地址项,即用iaddr(O)~iaddr(9)来存放直接地址。
换言之,在这里的每项中所存放的是该文件数据所在盘块的盘块号。
假如每个盘块的大小为4KB,当文件不大于40KB时,便可直接从索引结点中读出该文件的全部盘块号。
2) 一次间接地址
对于大、中型文件,只釆用直接地址并不现实。可再利用索引结点中的地址项iaddr(lO) 来提供一次间接地址。这种方式的实质就是一级索引分配方式。图中的一次间址块也就是索引块,系统将分配给文件的多个盘块号记入其中。在一次间址块中可存放1024个盘块号, 因而允许文件长达4MB。
3) 多次间接地址
当文件长度大于4MB+40KB(—次间址与10个直接地址项)时,系统还须釆用二次间址分配方式。这时,用地址项iaddr(11)提供二次间接地址。该方式的实质是两级索引分配方 式。系统此时是在二次间址块中记入所有一次间址块的盘号。在釆用二次间址方式时,文件最大长度可达4GB。同理,地址项iaddr(12)作为三次间接地址,其所允许的文件最大长度可达4TB。