文件系统
为什么需要文件系统?
1)所有的计算机应用程序都需要存储和检索信息,进程运行时,可以在它自己的地址空间存储一定量的信息,但存储容量受虚拟地址空间大小的限制。
2)进程终止时,它保存的信息也随之丢失。
3)经常需要多个进程同时存取同一信息(或者其中部分信息)。
长期存储信息有三个基本要求:
1)能够存储大量信息。
2)使用信息的进程终止时,信息仍旧存在。
3)必须能使多个进程并发存取有关信息。
有关文件的几点说明:
就像我们看到的操作系统提取处理器的概念来建立进程的抽象,以及提取物理存储器的概念来建立进程(虚拟)地址空间的抽象那样,我们可以用一个新的抽象——文件来解决这个问题。(也就是说文件时对磁盘的抽象。)
文件是进程创建的信息逻辑单元。
文件不仅仅被用来对磁盘建模,以替代对随机存储器(RAM)的建模。
进程可以读取已经存在的文件,并在需要时建立新的文件。存储在文件中的信息必须是持久的,也就是说,不会因为进程的创建与终止而受到影响。一个文件应只在其所有者明确删除它的情况下才会消失。
文件是受操作系统管理的。
1、文件
1.1文件命名
文件是一种抽象机制,它提供了一种在磁盘上保留信息而且方便以后读取的方法。这种方法可以使用户不用了解存储信息的方法、位置和实际磁盘工作方式等有关细节。
在进程创建文件时,它给文件命名。在进程终止时,该文件仍旧存在,并且其他进程可以通过这个文件名对它进行访问。
文件的具体命名规则在各个系统中是不同的,不过所有的现代操作系统都允许用1至8个字母组成的字符串作为合法的文件名。
一些典型的文件扩展名:
1.2文件结构
文件可以有多种构造方式
1)字节序列:
把文件看成字节序列为操作系统提供了最大的灵活性。用户程序可以向文件中加入任何内容,并以任何方便的形式命名。操作系统不提供任何帮助,但也不会构成阻碍。
2)记录序列:
文件是具有固定长度记录的序列,每个记录都有其内部结构。把文件作为记录序列的中心思想是:读操作返回一个记录,而写操作重写或追加一个记录。
3)树
文件在这种结构中由一棵记录树构成,每个记录并不具有同样的长度,而记录的固定位置上有一个“键”字段。这棵树按“键”字段进行排序,从而可以对特定“键”进行快速查找。
1.3文件类型
普通文件和目录、字符特殊文件、块特殊文件
普通文件一般分为ASCII文件和二进制文件。
ASCII文件的最大优势是可以显示和打印,还可以用任何文本编辑器进行编辑。
二进制文件有一定的内部结构,使用该文件的程序才了解这种结构。
1.4文件存取
1)顺序存取(sequential access)
2)随机存取文件(random access file)
随机存取文件对很多应用程序而言是必不可少的,如数据库系统。
1.5 文件属性
文件都有文件名和数据。另外,所有的操作系统还会保存其他与文件相关的信息,如文件创建的日期和
时间、文件大小等。这些附加信息称为文件属性(attribute),有些人称之为元数据(metadata)。
一些文件的常用属性:
前4个属性与文件保护相关,它们指出了谁可以存取这个文件,谁不能存取这个文件。
标志是一些位或短的字段,用于控制或启用某些特殊属性。例如,隐藏文件不在文件列表中出现。
记录长度、键的位置和键的长度等字段只能出现在用关键字查找记录的文件里,它们提供了查找关键字所需的信息。
时间字段记录了文件的创建时间、最近一次存取时间以及最后一次修改时间,它们的作用不同。例如,目标文件生成后被修改的源文件需要重新编译生成目标文件。
1.6 文件操作
使用文件的目的是存储信息并方便以后的检索。对于存储和检索,不同系统提供了不同的操作。以下是与文件有关的最常用的一些系统调用:
1)create。创建不包含任何数据的文件。该调用的目的是表示文件即将建立,并设置文件的一些属性。
2)delete。当不再需要某个文件时,必须删除该文件以释放磁盘空间。任何文件系统总有一个系统调用
用来删除文件。
3)open。在使用文件之前,必须先打开文件。open调用的目的是:把文件属性和磁盘地址表装入内存,
便于后续调用的快速存取。
4)close。存取结束后,不再需要文件属性和磁盘地址,这时应该关闭文件以释放内部表空间。很多系统
限制进程打开文件的个数,以鼓励用户关闭不再使用的文件。磁盘以块为单位写入,关闭文件时,写入该文
件的最后一块,即使这个块还没有满。
5)read。在文件中读取数据。一般地,读出数据来自文件的当前位置。调用者必须指明需要读取多少数
据,并且提供存放这些数据的缓冲区。
6)write。向文件写数据,写操作一般也是从文件当前位置开始。如果当前位置是文件末尾,文件长度增
加。如果当前位置在文件中间,则现有数据被覆盖,并且永远丢失。
7)append。此调用是write的限制形式,它只能在文件末尾添加数据。若系统只提供最小系统调用集合,
则通常没有append。很多系统对同一操作提供了多种实现方法,这些系统中有时有append调用。
8)seek。对于随机存取文件,要指定从何处开始取数据,通常的方法是用seek系统调用把当前位置指针
指向文件中特定位置。seek调用结束后,就可以从该位置开始读写数据了。
9)get attributes。进程运行常需要读取文件属性。例如,UNIX中make程序通常用于管理由多个源文件组
成的软件开发项目。在调用make时,检查全部源文件和目标文件的修改时间,实现最小编译,使得全部文
件都为最新版本。为达到此目的,需要查找文件的某一些属性,特别是修改时间。
10)set attributes。某些属性是可由用户设置的,在文件创建之后,用户还可以通过系统调用set attributes
来修改它们。保护模式信息是一个显著的例子,大多数标志也属于此类属性。
11)rename。用户常常要改变已有文件的名字,rename系统调用用于这一目的。严格地说,设置这个系
统调用不是十分必要的,因为可以先把文件复制到一个新文件名的文件中,然后删除原来的文件。
2、目录
文件系统通常提供目录或文件夹用于记录文件,在很多系统中目录本身也是文件。
2.1 一级目录系统
目录系统的最简单形式是在一个目录中包含所有的文件。
2.2 层次目录系统
用户可以创建任意数量的子目录,这种能力为用户组织其工作提供了强大的结构化工具。因此,几乎所
有现代文件系统都是用这个方式组织的。
2.3 路径名
目录树组织文件系统时,需要有某种方法指明文件名。常用的方法有两种。第一种是,每个文件都赋予一个绝对路径名(absolute path name),它由从根目录到文件的路径组成。另一种指定文件名的方法是使用相对路径名(relative path name)。它常和工作目录(workingdirectory)(也称作当前目录(current directory))一起使用。
2.4 目录操作
1)create。创建目录。除了目录项“.”和“…”外,目录内容为空。目录项“.”和“…”是系统自动放在目录中的
(有时通过mkdir程序完成)。
2)delete。删除目录。只有空目录可删除。只包含目录项“.”和“…”的目录被认为是空目录,这两个目录项
通常不能删除。
3)opendir。目录内容可被读取。例如,为列出目录中全部文件,程序必须先打开该目录,然后读其中全
部文件的文件名。与打开和读文件相同,在读目录前,必须打开目录。
4)closedir。读目录结束后,应关闭目录以释放内部表空间。
5)readdir。系统调用readdir返回打开目录的下一个目录项。以前也采用read系统调用来读目录,但这方
法有一个缺点:程序员必须了解和处理目录的内部结构。相反,不论采用哪一种目录结构,readdir总是以标
准格式返回一个目录项。
6)rename。在很多方面目录和文件都相似。文件可换名,目录也可以。
7)link。连接技术允许在多个目录中出现同一个文件。这个系统调用指定一个存在的文件和一个路径
名,并建立从该文件到路径所指名字的连接。这样,可以在多个目录中出现同一个文件。这种类型的连接,
增加了该文件的i节点(i-node)计数器的计数(记录含有该文件的目录项数目),有时称为硬连接(hard
link)。
8)unlink。删除目录项。如果被解除连接的文件只出现在一个目录中(通常情况),则将它从文件系统
中删除。如果它出现在多个目录中,则只删除指定路径名的连接,依然保留其他路径名的连接。在UNIX
中,用于删除文件的系统调用(前面已有论述)实际上就是unlink。
3 文件系统的实现
文件和目录是怎样存储的、磁盘空间是怎样管理的以及怎样使系统有效而可靠地工作?
3.1 文件系统布局
文件系统存放在磁盘上。多数磁盘划分为一个或多个分区,每个分区中有一个独立的文件系统。
磁盘的0号扇区称为主引导记录(Master Boot Record,MBR),用来引导计算机。
除了引导块之外,第一个是超级块(superblock),超级块包含文件系统的所有关键参数,在计算机启动时,或者在该文件系统首次使用时,把超级块读内存。超级块中的典型信息包括:确定文件系统类型用的魔数、文件系统中数据块的数量以及其他重要的管理信息。
接着是文件系统中空闲块的信息,例如,可以用位图或指针列表的形式给出。
接着可能是根目录,它存放文件系统目录树的根部。最后,磁盘的其他部分存放了其他所有的目录和文件。
3.2 文件的实现
文件存储的实现的关键问题是记录各个文件分别用到哪些磁盘块。不同操作系统采用不同的方法。
3.2.1.连续分配
最简单的分配方案是把每个文件作为一连串连续数据块存储在磁盘上。
优点:
实现简单、读操作性能较好。
缺点:随着时间的推移,磁盘会变得零碎。
3.2.2.链表分配
每个块的第一个字作为指向下一块的指针,块的其他部分存放数据。
以充分利用每个磁盘块。不会因为磁盘碎片(除了最后一块中的内部碎片)而浪费存储空间。顺序读文件非常方便,但是随机存取却相当缓慢。会制引发了额外的开销。
3.2.3.在内存中采用表的链表分配
取出每个磁盘块的指针字,把它放在内存的一个表中,就可以解决上述链表的两个不足。
按这类方式组织,整个块都可以存放数据。进而,随机存取也容易得多。虽然仍要顺着链在文件中查找
给定的偏移量,但是整个链表都存放在内存中,所以不需要任何磁盘引用。与前面的方法相同,不管文件有
多大,在目录项中只需记录一个整数(起始块号),按照它就可以找到文件的全部块。
缺点是必须把整个表都存放在内存中。
3.2.4.i节点
最后一个记录各个文件分别包含哪些磁盘块的方法是给每个文件赋予一个称为i节点(index-node)的数据结构,其中列出了文件属性和文件块的磁盘地址。
给定i节点,就有可能找到文件的所有块。相对于在内存中采用表的方式而言,这种机制具有很大的优势,即只有在对应文件打
开时,其i节点才在内存中。如果每个i节点占有n个字节,最多k个文件同时打开,那么为了打开文件而保留i节点的数组所占据的全部内存仅仅是kn个字节。只需要提前保留少量的空间。
3.3目录的实现
在读文件前,必须先打开文件。打开文件时,操作系统利用用户给出的路径名找到相应目录项。目录项
中提供了查找文件磁盘块所需要的信息。目录系统的主要功能是把ASCII文件名映射成定位文件数据所需的信息。
1)把文件属性直接存放在目录项中。目录中有一个固定大小的目录项列表,每个文件对应一项,其中包含一个(固定长度)文件名、一个文件属性结构以及用以说明磁盘块位置的一个或多个磁盘地址(至某个最大值)。
2)对于采用i节点的系统,还可以把文件属性存放在i节点中而不是目录项中。
3.4 共享文件
当几个用户同在一个项目里工作时,他们常常需要共享文件。其结果是,如果一个共享文件同时出现在属于不同用户的不同目录下,工作起来就很方便。
3.5 日志结构文件系统
是CPU的运行速度越来越快,磁盘容量越来越大,价格也越来越便宜(但是磁盘速度并没有增快多少),同时内存容量也以指数形式增长。而没有得到快速发展的参数是磁盘的寻道时间。所以这些问题综合起来,便成为影响很多文件系统性能的一个瓶颈。为此,Berkeley设计了一种全新的文件系统,试图缓解这个问题,即日志结构文件系统(Log-structured FileSystem,LFS)。
促使设计LFS的主要原因是,CPU的运行速度越来越快,RAM内存容量变得更大,同时磁盘高速缓存也迅速地增加。进而,不需要磁盘访问操作,就有可能满足直接来自文件系统高速缓存的很大一部分读请求。所以从上面的事实可以推出,未来多数的磁盘访问是写操作,这样,在一些文件系统中使用的提前读机制(需要读取数据之前预取磁盘块),并不能获得更好的性能。
更为糟糕的情况是,在大多数文件系统中,写操作往往都是零碎的。一个50µs的磁盘写操作之前通常需要10ms的寻道时间和4ms的旋转延迟时间,可见零碎的磁盘写操作是极其没有效率的。
3.6 日志文件系统
这里的基本想法是保存一个用于记录系统下一步将要做什么的日志。这样当系统在完成它们即将完成的任务前崩溃时,重新启动后,可以通过查看日志,获取崩溃前计划完成的任务,并完成它们。这样的文件系统被称为日志文件系统,并已经被实际应用。
4 文件系统管理和优化
4.1磁盘空间管理
1)块的大小
块小,一个文件将会由多个块进行组织,在读取文件时就要访问文件对应的所有的块,而每读一块都会有寻道和旋转延迟时间,会造成文件读取缓慢,块大,则会导致有的块没有放满,使得空间利用率降低。
分配单位很小意味着每个文件由很多块组成,每读一块都有寻道和旋转延迟时间,所以,读取由很多小块组成的文件会非常慢。
对一个块的访问时间完全由寻道时间和旋转延迟所决定,所以若要花费9ms的代价访问一个盘块,那么取的数据越多越好。
性能与空间利用率天生就是矛盾的。小的块会导致低的性能但是高的空间利用率。
2).记录空闲块
磁盘块链表
位图
3)磁盘配额
为了防止人们贪心而占有太多的磁盘空间,多用户操作系统常常提供一种强制性磁盘配额机制。其思想是系统管理员分给每个用户拥有文件和块的最大数量,操作系统确保每个用户不超过分给他们的配额。