文件的概念和定义
文件(File)是操作系统中的一个重要概念。在系统运行时,计算机以进程为基本单位进行资源的调度和分配;而在用户进行的输入、输出中,则以文件为基本单位。大多数应用程序的输入都是通过文件来实现的,其输出也都保存在文件中,以便信息的长期存及将来的访问。当用户将文件用于应用程序的输入、输出时,还希望可以访问文件、修改文件和保存文件等,实现对文件的维护管理,这就需要系统提供一个文件管理系统,操作系统中的文件系统(File System)就是用于实现用户的这些管理要求。
从用户的角度看,文件系统是操作系统的重要部分之一。用户关心的是如何命名、分类和查找文件,如何保证文件数据的安全性以及对文件可以进行哪些操作等。而对其中的细节,如文件如何存储在辅存上、如何管理文件辅存区域等关心甚少。
文件系统提供了与二级存储相关的资源的抽象,让用户能在不了解文件的各种属性、文件存储介质的特征以及文件在存储介质上的具体位置等情况下,方便快捷地使用文件。
用户通过文件系统建立文件,提供应用程序的输入、输出,对资源进行管理。首先了解文件的结构,我们通过自底向上的方式来定义。
1) 数据项
数据项是文件系统中最低级的数据组织形式,可分为以下两种类型:
2) 记录
记录是一组相关的数据项的集合,用于描述一个对象在某方面的属性,如一个考生报名记录包括考生姓名、出生日期、报考学校代号、身份证号等一系列域。
3) 文件
文件是指由创建者所定义的一组相关信息的集合,逻辑上可分为有结构文件和无结构文件两种。在有结构文件中,文件由一组相似记录组成,如报考某学校的所有考生的报考信息记录,又称记录式文件;而无结构文件则被看成是一个字符流,比如一个二进制文件或字符文件,又称流式文件。
虽然上面给出了结构化的表述,但实际上关于文件并无严格的定义。通常在操作系统中将程序和数据组织成文件。文件可以是数字、字母或二进制代码,基本访问单元可以是字节、 行或记录。文件可以长期存储于硬盘或其他二级存储器中,允许可控制的进程间共享访问,能够被组织成复杂的结构。
文件有一定的属性,这根据系统的不同而有所不同,但是通常都包括如下属性:
①名称:文件名称唯一,以容易读取的形式保存。
②标识符:标识文件系统内文件的唯一标签,通常为数字,它是对人不可读的一种内部名称。
③类型:被支持不同类型的文件系统所使用。
④位置:指向设备和设备上文件的指针。
⑤大小:文件当前大小(用字节、字或块表示),也可包含文件允许的最大值。
⑥保护:对文件进行保护的访问控制信息。
⑦时间、日期和用户标识:文件创建、上次修改和上次访问的相关信息,用于保护、 安全和跟踪文件的使用。
所有文件的信息都保存在目录结构中,而目录结构也保存在外存上。文件信息当需要时再调入内存。通常,目录条目包括文件名称及其唯一标识符,而标识符定位其他属性的信息。
文件属于抽象数据类型。为了恰当地定义文件,就需要考虑有关文件的操作。操作系统提供系统调用,它对文件进行创建、写、读、定位和截断。.
①创建文件:创建文件有两个必要步骤,一是在文件系统中为文件找到空间;二是在目录中为新文件创建条目,该条目记录文件名称、在文件系统中的位置及其他可能信息。
②写文件:为了写文件,执行一个系统调用,指明文件名称和要写入文件的内容。对于给定文件名称,系统搜索目录以查找文件位置。系统必须为该文件维护一个写位置的指针。每当发生写操作,便更新写指针。
③读文件:为了读文件,执行一个系统调用,指明文件名称和要读入文件块的内存位置。同样,需要搜索目录以找到相关目录项,系统维护一个读位置的指针。每当发生读操作时,更新读指针。一个进程通常只对一个文件读或写,所以当前操作位置可作为每个进程当前文件位置指针。由于读和写操作都使用同一指针,节省了空间也降低了系统复杂度。
④文件重定位(文件寻址):按某条件搜索目录,将当前文件位置设为给定值,并且不会读、写文件。
⑤删除文件:先从目录中找到要删除文件的目录项,使之成为空项,然后回收该文件所占用的存储空间。
⑥截断文件:允许文件所有属性不变,并删除文件内容,即将其长度设为0并释放其空间。
这6个基本操作可以组合执行其他文件操作。例如,一个文件的复制,可以创建新文件、 从旧文件读出并写入到新文件。
因为许多文件操作都涉及为给定文件搜索相关目录条目,许多系统要求在首次使用文件时,使用系统调用open,将指明文件的属性(包括该文件在外存上的物理位置)从外存拷贝到内存打开文件目录表的一个表目中,并将该表目的编号(或称为索引)返回给用户。操作系统维护一个包含所有打开文件信息的表(打开文件表,open-file table)。当用户需要一个文件操作时,可通过该表的一个索引指定文件,就省略了搜索环节。当文件不再使用时,进程可以关闭它,操作系统从打开文件表中删除这一条目。
大部分操作系统要求在文件使用之前就被显式地打开。操作open会根据文件名搜索目录,并将目录条目复制到打开文件表。如果调用open的请求(创建、只读、读写、添加等)得到允许,进程就可以打开文件,而open通常返回一个指向打开文件表中的一个条目的指针。通过使用该指计(而非文件名)进行所有I/O操作,以简化步骤并节省资源。
整个系统表包含进程相关信息,如文件在磁盘的位置、访问日期和大小。一个进程打开一个文件,系统打开文件表就会为打开的文件增加相应的条目。当另一个进程执行open时,只不过是在其进程打开表中增加一个条目,并指向整个系统表的相应条目。通常,系统打开文件表的每个文件时,还用一个文件打开计数器(Open Count),以记录多少进程打开了该文件。每个关闭操作close则使count递减,当打开计数器为0时,表示该文件不再被使用。系统将回收分配给该文件的内存空间等资源,若文件被修改过,则将文件写回外存,并将系统打开文件表中相应条目删除,最后释放文件的文件控制块(File Control Block, FCB)。
每个打开文件都有如下关联信息:
文件的逻辑结构是从用户观点出发看到的文件的组织形式。文件的物理结构是从实现观点出发,又称为文件的存储结构,是指文件在外存上的存储组织形式。文件的逻辑结构与存储介质特性无关,但文件的物理结构与存储介质的特性有很大关系。
按逻辑结构,文件有无结构文件和有结构文件两种类型:无结构文件和有结构文件。
无结构文件是最简单的文件组织形式。无结构文件将数据按顺序组织成记录并积累保存,它是有序相关信息项的集合,以字节(Byte)为单位。由于无结构文件没有结构,因而对记录的访问只能通过穷举搜索的方式,故这种文件形式对大多数应用不适用。但字符流的无结构文件管理简单,用户可以方便地对其进行操作。所以,那些对基本信息单位操作不多的文件较适于釆用字符流的无结构方式,如源程序文件、目标代码文件等。
有结构文件按记录的组织形式可以分为:
1) 顺序文件。
文件中的记录一个接一个地顺序排列,记录可以是定长的或变长的,可以顺序存储或以链表形式存储,在访问时需要顺序搜索文件。顺序文件有以下两种结构:
第一种是串结构,记录之间的顺序与关键字无关。通常的办法是由时间决定,即按存入时间的先后排列,最先存入的记录作为第1个记录,其次存入的为第2个记录,依此类推。
第二种是顺序结构,指文件中的所有记录按关键字顺序排列。
在对记录进行批量操作时,即每次要读或写一大批记录,对顺序文件的效率是所有逻辑文件中最高的;此外,也只有顺序文件才能存储在磁带上,并能有效地工作,但顺序文件对查找、修改、增加或删除单个记录的操作比较困难。
2) 索引文件。
如图4-1所示。对于定长记录文件,如果要查找第i个记录,可直接根据下式计算来获得第i个记录相对于第一个记录的地址:
然而,对于可变长记录的文件,要查找第i个记录时,必须顺序地查找前i-1个记录,从而获得相应记录的长度L,然后才能按下式计算出第i个记录的首址:
注意:假定每个记录前用一个字节指明该记录的长度。
变长记录文件只能顺序查找,系统开销较大。为此可以建立一张索引表以加快检索速度,索引表本身是定长记录的顺序文件。在记录很多或是访问要求高的文件中,需要引入索引以提供有效的访问。实际中,通过索引可以成百上千倍地提高访问速度。
3) 索引顺序文件。
索引顺序文件是顺序和索引两种组织形式的结合。索引顺序文件将顺序文件中的所有记录分为若干个组,为顺序文件建立一张索引表,在索引表中为每组中的第一个记录建立一个索引项,其中含有该记录的关键字值和指向该记录的指针。
如图4-2所示,主文件名包含姓名和其他数据项。姓名为关键字,索引表中为每组的第一个记录(不是每个记录)的关键字值,用指计指向主文件中该记录的起始位置。索引表只包含关键字和指计两个数据项,所有姓名关键字递增排列。主文件中记录分组排列,同一个组中关键字可以无序,但组与组之间关键字必须有序。查找一个记录时,通过索引表找到其所在的组,然后在该组中使用顺序查找就能很快地找到记录。
对于含有N个记录的顺序文件,查找某关键字值的记录时平均需要查找N/2次。在索引顺序文件中,假设N个记录分为N1/2组,索引表中有N1/2个表项,每组有N1/2个记录,在查找某关键字值的记录时,先顺序查找索引表,需要查找N1/2/2次,然后再在主文件中对应的组中顺序查找,也需要查找N1/2/2次,这样总共查找N1/2/2+N1/2/2=N1/2次。显然,索引顺序文件提高了查找效率,如果记录数很多,可以釆用两级或多级索引。
索引文件和索引顺序文件都提高了存取的速度,但因为配置索引表而增加了存储空间。
4) 直接文件或散列文件(Hash File)
给定记录的键值或通过Hash函数转换的键值直接决定记录的物理地址。这种映射结构不同于顺序文件或索引文件,没有顺序的特性。
散列文件有很高的存取速度,但是会引起冲突,即不同关键字的散列函数值相同。
与文件管理系统和文件集合相关联的是文件目录,它包含有关文件的信息,包括属性、 位置和所有权等,这些信息主要是由操作系统进行管理。首先我们来看目录管理的基本要求: 从用户的角度看,目录在用户(应用程序)所需要的文件名和文件之间提供一种映射,所以目录管理要实现“按名存取”;目录存取的效率直接影响到系统的性能,所以要提高对目录的检索速度;在共享系统中,目录还需要提供用于控制访问文件的信息。此外,文件允许重名也是用户的合理和必然要求,目录管理通过树形结构来解决和实现。
同进程管理一样,为实现目录管理,操作系统中引入了文件控制块的数据结构。
1) 文件控制块。
文件控制块(FCB)是用来存放控制文件需要的各种信息的数据结构,以实现“按名存取”。FCB的有序集合称为文件目录,一个FCB就是一个文件目录项。为了创建一个新文件,系统将分配一个FCB并存放在文件目录中,成为目录项。
FCB主要包含以下信息:
2) 索引结点。
在检索目录文件的过程中,只用到了文件名,仅当找到一个目录项(查找文件名与目录项中文件名匹配)时,才需要从该目录项中读出该文件的物理地址。也就是说,在检索目录时,文件的其他描述信息不会用到,也不需调入内存。因此,有的系统(如UNIX,见表4-1)釆用了文件名和文件描述信息分开的方法,文件描述信息单独形成一个称为索引结点的数据结构,简称为 i 结点。在文件目录中的每个目录项仅由文件名和指向该文件所对应的i结点的指针构成。
文件名 | 索引结点编号 |
---|---|
文件名1 | |
文件名2 | |
一个FCB的大小是64字节,盘块大小是1KB,则在每个盘块中可以存放16个FCB(注意,FCB必须连续存放)。而在UNIX系统中一个目录项仅占16字节,其中14字节是文件名,2字节是 i 结点指针。在1KB的盘块中可存放64个目录项。这样,可使查找文件时平均启动磁盘次数减少到原来的1/4,大大节省了系统开销。
存放在磁盘上的索引结点称为磁盘索引结点,UNIX中的每个文件都有一个唯一的磁盘索引结点,主要包括以下几个方面:
在理解一个文件系统的需求前,我们首先来考虑在目录这个层次上所需要执行的操作,这有助于后面文件系统的整体理解。
操作时,考虑以下几种目录结构:
1) 单级目录结构。
在整个文件系统中只建立一张目录表,每个文件占一个目录项,如图4-3所示。
当访问一个文件时,先按文件名在该目录中查找到相应的FCB,经合法性检查后执行相应的操作。当建立一个新文件时,必须先检索所有目录项以确保没有“重名”的情况,然后在该目录中增设一项,把FCB的全部信息保存在该项中。当删除一个文件时,先从该目录中找到该文件的目录项,回收该文件所占用的存储空间,然后再清除该目录项。
单级目录结构实现了 “按名存取”,但是存在查找速度慢、文件不允许重名、不便于文件共享等缺点,而且对于多用户的操作系统显然是不适用的。
2) 两级目录结构。
单级目录很容易造成文件名称的混淆,可以考虑釆用两级方案,将文件目录分成主文件目录(Master File Directory, MFD)和用户文件目录(User File Directory, UFD)两级,如图4-4所示。
主文件目录项记录用户名及相应用户文件目录所在的存储位置。用户文件目录项记录该用户文件的FCB信息。当某用户欲对其文件进行访问时,只需搜索该用户对应的UFD,这既解决了不同用户文件的“重名”问题,也在一定程度上保证了文件的安全。
两级目录结构可以解决多用户之间的文件重名问题,文件系统可以在目录上实现访问限制。但是两级目录结构缺乏灵活性,不能对文件分类。
3) 多级目录结构(树形目录结构)。
将两级目录结构的层次关系加以推广,就形成了多级目录结构,即树形目录结构,如图4-5所示。
用户要访问某个文件时用文件的路径名标识文件,文件路径名是个字符串,由从根目录出发到所找文件的通路上的所有目录名与数据文件名用分隔符链接起来而成。从根目录出发的路径称绝对路径。当层次较多时,每次从根目录查询浪费时间,于是加入了当前目录,进程对各文件的访问都是相对于当前目录进行的。当用户要访问某个文件时,使用相对路径标识文件,相对路径由从当前目录出发到所找文件通路上所有目录名与数据文件名用分隔符“/”链接而成。
图4-5是Linux操作系统的目录结构,“/dev/hda”就是一个绝对路径。若当前目录为 “/bin”,则“./ls”就是一个相对路径,其中符号表示当前工作目录。
通常,每个用户都有各自的“当前目录”,登录后自动进入该用户的“当前目录”。操作系统提供一条专门的系统调用,供用户随时改变“当前目录”。例如,UNIX系统中, “/etc/passwd”文件就包含有用户登录时默认的“当前目录”,可用cd命令改变“当前目录”。
树形目录结构可以很方便地对文件进行分类,层次结构清晰,也能够更有效地进行文件的管理和保护。但是,在树形目录中查找一个文件,需要按路径名逐级访问中间结点,这就增加了磁盘访问次数,无疑将影响查询速度。
4) 无环图目录结构。
树形目录结构可便于实现文件分类,但不便于实现文件共享,为此在树形目录结构的基础上增加了一些指向同一结点的有向边,使整个目录成为一个有向无环图。引入无环图目录结构是为了实现文件共享,如图4-6所示。
当某用户要求删除一个共享结点时,若系统只是简单地将它删除,当另一共享用户需要访问时,却无法找到这个文件而发生错误。为此可以为每个共享结点设置一个共享计数器,每当图中增加对该结点的共享链时,计数器加 1;每当某用户提出删除该结点时,计数器减1。仅当共享计数器为0时,才真正删除该结点,否则仅删除请求用户的共享链。
共享文件(或目录)不同于文件拷贝(副本)。如果有两个文件拷贝,每个程序员看到的是拷贝而不是原件;但如果一个文件被修改,那么另一个程序员的拷贝不会有改变。对于共享文件,只存在一个真正文件,任何改变都会为其他用户所见。
无环图目录结构方便实现了文件的共享,但使得系统的管理变得更加复杂。