文件是由操作系统来管理的,包括文件的结构、文件的命名、文件的使用、文件的保护和文件的实现等,这些都是在操作系统的设计当中需要解决的问题。总之,在一个操作系统中,负责处理文件相关事宜的部分,就称为文件系统。
可以从两个观点来看待文件系统:
用户观点:所谓用户,就是计算机的使用者以及应用程序的编程人员。对于他们来说,他们关心的是文件系统所提供的对外的用户接口,包括文件如何命名、如何保护、如何访问(创建、打开、关闭、读和写等)
操作系统观点:对于操作系统的设计者来说,他们关心的是如何来实现与文件有关的各个功能模块,包括如何来管理存储空间、文件系统的布局、文件的存储位置如何安排等。
1、文件
1.1文件的基本概念
文件时一种抽象的概念,它提供了一种把信息保存在磁盘等外部存储设备上,并且便于以后访问的方法。这种抽象体现在用户不必关心具体的实现细节。
- 文件的命名
文件名.后缀
- 文件的结构
主要有三种形式:
①无结构:整个文件由一序列无结构的字节流组成。
②简单的记录结构:用每一行作为一个记录,每个记录的长度可以是固定不变的,也可以是各不相同的。
③复杂结构:文件的结构形式比较复杂。比如,可以把文件中的所有记录组织成一颗树的形式,即树形结构。
记录结构和复杂结构都是由操作系统来指定文件的逻辑结构,使用起来很不方便。
UNIX和Windows,它们采用的都是无结构形式,用户可以将信息以任意格式写入文件中。
- 文件的分类
①普通文件:ASCII文件,里面包含的是一行行的文本。二进制文件,通常具有内部的逻辑结构,为相关的应用程序所了解。 ②目录文件:用来管理文件系统的组织结构的一种系统文件,这是一种专用的特殊文件。
在UNIX中,还有两种特殊文件,其实就是输入输出设备,在UNIX中将输入输出设备看成是一种文件。包括字符特殊文件和块特殊文件。
- 文件的属性
1.2、文件的使用
文件的使用指的是操作系统提供的与文件有关的系统调用。 文件的访问指的是围绕文件内容的读写进行的各种文件操作:打开,关闭,读,写文件。添加操作,定位操作。 文件的控制指的是围绕文件属性的控制进行的各种文件操作:创建,删除,获取文件属性,设置文件属性,修改文件名。
2、目录
2.1、目录的基本概念
目录也称为文件夹,它是一张表格,里面记录了在该目录下的每一个文件的文件名和其它一些管理信息。每一个文件都会占用这张表格中的某一行,即一个目录项。
这张表格本身是以文件的形式存放在磁盘上,也就是说,把每一个目录项变为一个字节流,然后把各个目录项依次存放在一个文件中。
2.2、目录的结构
- 一级目录
对于系统中的所有文件,只创建一个目录文件,即一张线性表格,每个文件占用其中的一个目录项。
- 二级目录
基本思路:把目录分为两级,第一级称为是根目录,第二级称为用户子目录。根目录只有一个,而用户子目录可以有多个。每个用户的所有文件都保存在相应的用户子目录中。
- 多级目录
现代操作系统一般采用的都是多级目录结构,也称为是树状目录结构或层次目录结构,其形状好似一颗倒立的树。
绝对路径名:对于一个文件或目录,可以用从根目录开始依次经由的各级目录名,再加上最终的文件名或目录名来表示。\spll\mail\copy\all
相对路径名:在当前目录下,如果需要访问一个文件或目录时,可以使用相对于这个当前目录的部分路径名,即相对路径名。
例如:假如当前工作目录是:\spll\mail\copy,那么以下两个路径名是完全等价的:
\spll\mail\copy\all 绝对路径名
all 相对路径名
3、文件系统的实现
文件系统将磁盘空间划分为一个个大小相同的数据块,即物理块。物理块是由一个或多个连续的扇区组成的。 与此同时,把应用程序提交给文件系统的字节流(即逻辑地址空间)也划分为大小相同的逻辑块。然后在文件系统的内部,以块来作为基本的处理单元,把每一个逻辑块保存在一个物理块中。
3.1、文件系统的布局
将磁盘分区以后,磁盘的扇区0称为主引导记录(Master Boot Record,MBR),它主要用来启动计算机。在MBR的末尾有一个分区表,里面记录了每一个分区的起始扇区和大小。在磁盘的多个分区中,有一个是活动分区,操作系统就保存在该分区中。
- 当一台计算机启动后,位于主板上的BIOS(Basic Input Output System,基本输入输出系统)程序就会被执行。
- 该程序会去检查并设置系统当中的各种硬件资源,然后去查询一个表格,看看是从什么地方启动的,是软盘、硬盘还是光盘。
- 如果是从硬盘启动的,那么它就会把MBR中的引导程序装入到内存运行,
- 然后该引导程序就会去查询硬盘上的分区表,看看哪一个是活动分区。
- 如果第二个是活动分区,就会把该分区的引导块当中的程序装入到内存中去运行。当这个程序开始运行时,它就会把保存在这个磁盘分区中的操作系统程序,一步步地装入到内存中去运行。
3.2、文件的实现
文件的实现需要解决以下三个问题: 如何来描述一个文件,用什么数据结构来记录文件的各种管理信息? 如何来存放文件,如何把文件的各个连续的逻辑块存放在磁盘上的空闲物理块当中,并且记录这些逻辑块与物理块之间的映射关系? 如何来实现与文件有关的各种系统调用,如打开文件、读写文件等?
- 文件控制块
文件控制块(File Control Block,FCB)是操作系统为管理文件而设置的一种数据结构,里面存放了与一个文件有关的所有管理信息,它是文件存在的标志。
文件控制块的主要内容:
①文件的类型和长度
②文件的所有者、文件的访问权限
③文件的创建时间、最后访问时间、最后修改时间
④文件所在的物理块信息,即该文件在磁盘上的存放位置,它的每一个逻辑块被存放在磁盘的哪一个物理块中。
总之,只有知道了这些信息,文件系统才能对这个文件的数据进行访问。
备注:
FCB和进程控制块PCB有些类似,在一个进程的PCB中,保存的是与该进程有关的所有管理信息。但是区别是:PCB是存放在内存中,而且是在进程运行时创建,在进程结束时撤销;而FCB是存放在磁盘上的。
- 文件的物理结构
文件的物理结构讨论的是如何把一个文件存放在磁盘等物理介质上。具体说,就是以块为单位,研究如何把文件的一个个连续的逻辑块分别存放在不同的物理块中,即研究逻辑块与物理块的映射关系。(有点类似于,逻辑页面和物理页面之间的映射关系。)
一般,文件的物理结构只要有三种形式:连续结构,链表结构和索引结构。
- 连续结构
连续结构也称为顺序结构,它的基本思想是把文件的各个逻辑块按照顺序存放在若干个连续的物理块中。也就是说,文件的逻辑地址是连续的;把它存放在磁盘上以后,物理地址也是连续的。
优点:
①简单、易于实现。对于文件系统来说,记住第一个物理块的编号和物理块的个数,即可通过简单的加法来实现逻辑块到物理块的映射关系。
②由于物理块是顺序存放的,所以在访问文件时,只要将磁头定位到第一个物理块,即可顺序地读取每一个数据块,而不用再去移动磁头,或等待相应的扇区旋转到磁头的下方。这样,磁盘的访问速度是非常快的。
缺点:
①随着磁盘上的文件的增加和删除,将会形成已占有物理块与空闲物理块之间相互交错的情形。这样那些小的,无法再利用的物理块,就成了外碎片。
②文件的大小不能动态地增长,在创建一个新的文件时,必须事先指定该文件的大小,这样文件系统才知道应该把哪一片连续的空闲空间分配给它。但是在新建一个文件的时候,我们往往不知道文件的大小,或者说文件的大小是变化的。
总之,连续结构的文件系统已经不常用了。但是在CD-ROM、DVD和其它一些一次性写入的光学存储介质中,连续结构被广泛应用。
- 链表结构
链表结构的基本思路是:把文件的各个逻辑块依次存放在若干个物理块中,这些物理块既可以是连续的,也可以是不连续的,然后在各个物理块之间通过指针连接起来,前一个物理块指向下一个物理块。
每一个物理块由两个部分组成:指针和数据区。
对于文件系统来说,它只要记住这个链表结构的首结点指针,就可以定位到该文件中的任何一个物理块,这相当于是链表的遍历。
缺点:
①在访问一个文件时,只能进行顺序访问,不能随机访问,否则速度会很慢。只能从头开始访问。
②每个物理块上的数据存储空间不再是2的整数次幂,因为指针要占用若干个字节,这样,就使得文件的访问不太方便。
- 带有文件分配表的链表结构
基本思路:在链表结构的基础上,把每一个物理块当中的链表指针抽取出来,单独组成一个表格,即文件分配表(File Allocation Table,FAT),然后把它存放在内存中。
备注:FAT是存放在磁盘上的,然后在需要的时候再调入到内存中。
如何知道一个逻辑块所对应的物理块地址呢?
- 在链表结构中,这个地址信息保存在一条链表中,而这个链表中每个指针又保存在磁盘上,是和文件的数据混合在一起的,既占用磁盘空间,访问速度又慢。
- 如果有文件分配表的话:一方面,可以把指针信息拿出来放在内存中;另一方面,查询这个FAT表,找到相应的物理块地址,然后根据这个地址再去访问磁盘。只要访问一次就可以了。
如何来实现文件分配表?
在整个文件系统中,只设置一个一维的线性表格。它的表格项的个数等于磁盘上物理块的个数(缺点:太大,包括那些没有打开的文件),并且按照物理块编号的顺序来建立索引。对于一个文件,在它的文件控制块(FCB)中记录了该文件的第一个物理块的编号x1,然后再FAT表的第x1项中,记录了该文件的第二个物理块编号x2,依次类推,形成一条链表。然后在最后一条FAT表项中,存放一个特殊的文件结束的标识。
- 索引结构
索引结构的基本思路:把一个文件中的每一个逻辑块所对应的物理块编号直接记录在文件的FCB中,称为i结点(索引结点)。这样,对于系统中的每一个文件,都有一个自己的索引结点。
在索引结构方式下,只要把那些正在使用的文件的索引结点装入到内存即可,而对于那些没有被打开的文件,则不必装入到内存。
在这种方式下,目录项的内容已经发生了变化。里面存放的不是文件的首个物理块的编号,而是该文件的i结点所在的物理块编号。
文件名 | 索引块 |
main.c | 20 |
在打开main.c文件时,首先通过该目录项,查询到它的i结点的存放位置,此时是在物理块20,然后把该物理块的内容装入到内存中,再去查询它里面的地址映射表,这样就可以知道该逻辑块是存放在哪一个物理块中,然后就可以直接去访问磁盘了。
3.3、目录的实现
文件系统如何根据一个文件名或路径名,来定位到相应的文件,了解到该文件的各种属性信息以及它在磁盘上的存储位置等?也就是要把它的文件控制块的内容读进来,这样才能进行下一步操作。 下面是目录的实现方法,需要解决以下三个问题:
目录项的内容、长文件名问题和目录的搜索方法。
- 目录项的内容
直接法:把文件控制块的内容保存在目录项中,即目录项 = 文件名 + FCB,其中包括文件的各种属性信息和它在磁盘上的存放位置。比如:MS-DOS,Windows
间接法:每一个文件的FCB不是保存在它的目录项中,而是单独存放。把系统中所有的文件的FCB统一保存在磁盘的某一个位置,然后再每一个目录项中,目录项 = 文件名 + FCB的地址。比如:UNIX
文件名 | FCB |
f1.exe | FCB1 |
f2.c | FCB2 |
f3.txt | FCB3 |
文件名 | FCB索引 |
f1.exe | FCB1的地址 |
f2.c | FCB2的地址 |
f3.txt | FCB3的地址 |
- 长文件名问题
如何实现这种长文件名的机制?
- 有三种方法在目录项中,将文件名的长度固定为255个字符,即为每一个文件名都预留255个字节的空间。缺点就是,会造成浪费大量的目录空间。
- 每一个目录项的长度是可变的,对于不同长度的文件名,其目录项的长度是不一样的。每一个目录项的内容分为三个部分:目录项的长度,文件的属性信息和文件名。
- 每一个目录项本身的长度是固定的,然后把长度可变的文件名统一放在目录文件的末尾。这样,当一个目录项被释放以后,它所占用的空间就能方便地回收和利用。
- 目录的搜索方法
目录的搜索:就是在一个目录文件中,当给定了一个文件名以后,如何定位到相应的目录项。寻找目录项的目的在于取出该文件的FCB,这样就能访问它的各种属性信息。
难点:用户给出的是ASCII形式的文件名,系统如何通过这个文件名来作为索引?
目录的搜索方法主要有两种:
- 线性搜索:对目录中的每一个目录项,依次与该文件名进行比较,直到找到相应的目录项。
- Hash表:用Hash表的方法来加速搜索速度。
3.4、系统调用的实现
在文件系统内部,如何实现文件的打开、关闭、读和写等各种系统调用函数?
- 数据结构
①位于外存上的数据结构:
目录结构:用来组织文件,可以通过文件名来寻找相应的FCB。
文件控制块:记录了文件的各种属性信息和文件在外存的存储信息。
②位于内存中的数据结构:
系统内打开文件表:它记录了在整个系统中,所有被打开的文件,它们的文件控制块和共享计数值等信息。
进程内打开文件表:每一个进程都有这样一张表格,它描述了在进程内部所有打开的文件,包括每一个文件的打开方式,当前读写指针,该文件在系统打开文件表中的索引等信息。
- 打开文件
打开一个文件的系统调用一般形式为:
fd = open(文件路径名,打开方式);
- 关闭文件
文件在使用完毕后必须关闭,系统调用函数为:close(fd);
- 读文件
读文件的系统调用函数为:int read(fd, userBuf, size);
fd是文件指针,userBuf是用户提供的一个缓冲区,size是需要读取的字节数。功能是:从文件fd的当前位置开始,顺序读取大小为size的数据块,并保存在缓冲区userBuf中。
3.5、空闲空间管理
为了记录磁盘上的空闲空间,系统会维护一个空闲空间的列表,它记录了磁盘上所有的空闲物理块。
主要有三种方法:位图法,链表法,索引法。
- 位图法
用位图来表示磁盘的空闲空间列表。每一个物理块用一个位来表示。如果该物理块空闲,则相应位为1,;如果该物理块被分配,则相应位为0;
- 链表法
用链表来表示磁盘的空闲空间列表。在每一个空闲的物理块上都有一个指针,然后把所有的空闲块通过这个指针连接起来,从而形成一条链表。系统只要记住这条链表的首结点指针,就可以一个接一个去访问所有的空闲物理块。
- 索引法
它不是把所有的空闲物理块链接成一条链表,而是单独预留少量的一些空闲物理块,把它们链接成一条链表。