在多程序多用户的系统上,读取数据有以下问题:
通过前面的学习, 我们知道 操作系统对处理器进行抽象 建立了进程这个概念; 通过对物理存储器的抽象建立了 虚拟地址空间的概念, 现在,为了解决问题, 就创建了 文件 这个抽象概念。
操作系统处理文件的部分 称为文件系统。文件是授操作系统管理的。有关文件的构造、命令、访问、使用、保护、实现和管理方法都是操作系统设计的主要内容。从总体上看,操作系统中的处理文件的部分称为文件系统。
如homepage.html, 圆点前面是名字,圆点以后是文件扩展名,.html表示 这是一个html文件。在某些系统中,文件扩展名是一种约定,指定为某一类文件,但是操作系统并不强制采用这种形式,常见扩展名如下:
文件有许多构造方式,在图中列出了常用的三种构造方式:
下图是一个可执行二进制文件和一个存档普通文件的结构,可执行二进制文件的结构有:文件头、正文、数据、重定位位,符号表。
所有的操作系统还会保存其他与文件相关的信息,如文件创建的日期和时间、文件大小等,这些附加信息称为文件属性。
文件系统通常提供目录或文件夹用于记录文件的位置。
在早期系统或者限制一些简单的嵌入式系统中,采用,目录只有一个层级,如下,方形表示目录文件,原型表示普通文件:
多数计算机系统都采用多层次目录系统,如下:
在层次目录系统中,每个文件都有一个绝对路径名,它是从根目录到文件的路径,例如 /usr/ast/mailbox。不同系统的路径的分隔符不同,有 / 或者 \ 或者 > , 如下:
另外一种指定文件名的方法是使用相对路径。它常常和工作目录一起使用。每个进程都会有自己的工作目录。在操作系统的每个目录有两个特殊目录项: “.”和“…”, 读作dot和dotdot, dot指当前目录, dotdot指父目录。
文件系统放在磁盘上,多数磁盘划分为一个或多个分区,每个分区有一个独立的文件系统。磁盘的0号扇区称为主引导记录(Master Boot Record,MBR),用来引导计算机。MBR的结尾是分区表,该表给出了每个分区的其实和结束位置。表中的一个分区标记为活动分区,计算机启动时,BIOS读入并执行MBR,MBR第一件事就是确定活动分区,然后读入它的第一个块,称为引导块(boot block),然后执行它。引导块中的程序将装载该分区中的操作系统。为了统一,每个分区都从第一个引导块开始,即使它并不包含操作系统,不过也不排除它未来不会安装操作系统。
除了都是从引导块开始,不同文件系统的磁盘分区的布局是不同的,下图是一种文件系统的结构布局, 第一个是超级块(superblock),包含了文件系统的所有关键参数,在计算机启动或者该文件系统首次使用时,超级快会被读入内存,其中的典型信息有:文件系统类型使用的魔数,文件系统中块的数量以及其他重要管理信息,示例图如下:
接着就是文件系统中的空闲块的信息,列如,可以用位图或指针列表的形式给出。后面跟着的是一组i节点,这是一个数据结构的数组。最后磁盘的其他部分存放了其他所有的目录和文件。
文件存储的关键问题是记录各个文件分别用到哪些磁盘块,主要有:连续分配、链表分配、采用内存中的表进行链表分配,i节点。
文件作为一串连续数据块存储在磁盘上。简单且效率高,但是随着时间分配,磁盘变得零碎,如果某个连续空间比较小,而文件比较大,就不会放入这个空间,造成浪费。最终形成很多磁盘空洞,这时,如果重新挪动位置压缩磁盘空间,代价就太大了。因此,这个比较适合一次性写入的介质。
磁盘每个块的第一个字作为指向下一个块的指针,块的其他部分存放数据。 链表不会浪费空间,但是随机访问很慢。
存在的问题:
对链表分配的优化,就是把每个磁盘块的指针,存放在内存的一个表中,就可以解决链表分配的不足。当有连续块是,就可存放在连续块中,当连续块不够,就指向其余空闲的块中。内存中这样的表格称为 文件分配表(File Allocation Table, FAT)。 示例图如下:
不过这种方法的缺点就是必须把整个表存在内存中,如果磁盘很大,这个表就会特别大,因此FAT并不怎么适合大型磁盘中。
给每个文件赋予一个名字为 i节点(index-node)的数据结构,用来记录文件包含了哪些磁盘块。只要给定i节点,就能找到文件的所有块。这种方式的优势在于,只有文件被打开时,其i节点才在内存中,从而节省了内存。
如果每个i节点只能存储固定数量的磁盘地址,那么一个文件包含的磁盘块超过了固定数量怎么办呢? 其中一种解决办法就是最后一个地址指向一个包含额外地址的底盘块,升级版是最后两个或三个地址都可以指向其他存放地址的磁盘块,示例图:
在读取文件前,必须先要打开文件,打开文件时,操作系统利用用户给出的路径名找到相应的目录项。目录项中提供了查找文件磁盘所需要的信息。目录系统地主要功能是把ASCII文件名映射成定位文件数据所需要的信息。
要找到文件,就要根据路径名去找,路径就涉及到目录以及在何处去存放文件的属性。实现方式有两种:1.简单目录,即在目录文件中有一个固定大小的目录项列表,每个文件对应一项,,其中有(固定长度)的文件名、属性的结构体、磁盘块的位置的一个或多个地址。 2.对于采用了i节点的系统,可以把属性存放在i节点中,而不是目录项中,这样,目录项中就只需要存放文件名和i节点号了,这种方式更好。两种方式的目录项示例图如下:
还有另一个问题,就是文件名的长度以及文件名的保存。也就是说如何处理可变长度文件名字:
第一种简单的方式就是给予文件名一个固定的长度限制,同时给文件名分配固定大小的目录空间。但是如果文件名用不了这么多长度,就白白浪费了。
第二种方式是 每个目录项前面部分是固定格式的数据,如目录项长度、文件属性,然后接上可变的任意长度的文件名,文件名以特殊字符(通常为0)结束。如下图图a, 这个方法的缺点就是每个文件项的大小不一,如果移走一个文件,再新来一个文件,新文件的文件项不一定刚好适。
第三种方式就是目录项有一个固定长度,而将文件名放在目录后面的堆中,这样移走一个文件,下一个文件进来了,也刚好可以适合这个空隙。不过,就必须对堆进行管理。示例图如下:
在上图中,目录C下有一个文件和目录B共享, 有两种方式,一种是链接(link),共享文件的所有者是C,但是这个文件在C的被引用目录项的计数为2,当在C目录下去删除共享文件时, 如果共享文件发现计数为2,就不会去删除这个文件,而是把计数减1,直到计数为0,才会删除。这个或者叫硬链接。例图如下:
另一种方法为符号链接(symbolic linking),或者叫软连接。通过让操作系统建立一个类型为LINK的新文件,并把该文件放在B的目录下,得到B与C的文件存成在一个链接。新的文件中只包含了它所链接的文件的路径名。当B读取该链接文件的时候,操作系统查看到要读的文件是LINK类型,则找到该文件所链接的文件的名字,并且去读那个文件。
问题:可见零碎的磁盘从中是及其没有效率的。
LFS的设计者决定重新设计一种UNIX文件系统,该系统即使面对一个大部分由于零碎的随机写操作组成的任务,统一也能够充分利用磁盘的带宽。其基本思想是将整个磁盘结构化为一个日志。每隔一段时间,别缓存在内存中所有未决定的写操作都放到一个单独的段中,作为在日志末尾的一个邻接段写入磁盘。
保存一个用于记录系统下一步需要做什么的日志,这样,如果系统在即将完成任务时崩溃,重新启动后,可以通过查看日志,获取崩溃前计划完成的任务,并重新去完成它,完成后擦除日志项。
绝大多数UNIX操作系统都在使用虚拟操作系统。概念尝试将多种文件系统统一成一个有序的结构。关键的思想是抽象出所有文件系统都共有的部分,并将这部分代码放在单独的一层,该层调用底层的实际文件系统来管理数据。
几乎所有的文件系统都是通过块来管理存储的,文件系统把文件分割成固定大小的块。
如果分配的单元过小,则浪费了空间。如果分配的单元太小,则浪费了磁盘时间。
这个通过研究曲线决定,目前最佳为64k, 如下图:
第一种方法是采用磁盘块链表,链表中每个块包含尽可能多的空闲磁盘快号。
另一种空闲磁盘管理方法就是采用位图。n个块的磁盘需要n个位图。
如果空闲块倾向于成为一个长的连续分块的话,则空闲列表系统可以改成记录连续分块而不是单个分块。
为防止人们贪心占用过多空间,由管理员为每位用户分配最大拥有文件和块的数量,操作系统确保每个用户不超过分给他们的配额。
为了防止文件系统被破坏,通常需要对文件进行备份。主要是为了解决两个问题:
同时备份文件不要防在同一个地方,应在不同的地方保存,不过又会增加安保的难度,不过这个不在讨论之列。
为文件做备份费时费空间,因此要选择有意义的目录,如临时文件目录就不用备份了。备份一些特定目录或者重要目录。
其次,对前一次备份没做更改而再去备份也是一种浪费,因此有了增量转储的思想。最简单的增量转储形式就是每周或每月做一次全面备份,而每天只需要对从上一次转储后改变的文件备份即可。不过这个回复起来比较复杂。所以通常用更复杂的增量转储方法。
第三、既然转储文件时海量的,那么需不需要压缩也应该考虑。
第四、记录文件系统的瞬时快照,赋值关键数据结构,其余留待空闲时在备份
第五、非技术性问题,注意备份文件的安保。
物理转储:从磁盘第0块开始,直到最后一块, 全部复制。
逻辑转储:从一个或几个指定目录开始,递归的转储给定日期以后更改的文件及目录。
一种处理方法就是磁盘块计数表,使用的块和空闲的块, 加起来, 各个位都为1,否则就是一致性出错。
块告诉缓存逻辑上属于磁盘,但实际上基于性能考虑保存在内存中。告诉缓存块的换入换出,可参考第二章的FIFO算法,LRU算法等页面置换算法。 为了防止计算机系统崩溃导致文件系统破坏,UNIX无限循环没30s调用一次sync,强制将修改过的块立即写到磁盘,这样,就算系统崩溃,丢失数据不会超过30s。
提前将即将调用的块写入告诉缓存,明显可以提升执行效率。通常使用与顺序读文件。对于随机读文件,先按照顺序提前读入,即使一开始错了也没关系,然后真正通过读入的块的文件关联性,去提前加载下一个块, 从而提升命中率。
不是有可能顺序访问的块放在一起,最好在一个柱面上,较少磁臂运动时间。
在初始安装操作系统后,从磁盘开始位置,一个接着一个地连续安装了程序和文件。所有的空闲磁盘空间放在一个单独的,与被安装的文件邻近的单元里。但是随着时间的流逝,文件被不断的创建于删除,于是磁盘会产生很多碎片,文件与空穴到处都是。定期整理磁盘,减少碎片。
UNIX V7 文件系统
对于大型文件,通常采用前面讲 i节点 时,图4-13使用的方法,就是最后几个地址存放的是另外的块, 快中存放的还是文件块的地址。如果文件很大,可以使用两次间接块甚至三次间接块,如下图:
例如查找目录 /usr/ast/mbox的过程,如下图,其中.表示当前目录的i节点号, …表示父目录i节点号: