在操作系统课程中,文件管理被拆分为两条主线:一条是用户视角的文件的逻辑结构,另一条是系统视角的文件的物理结构。软考命题人对这两者的区分极其较真,很多考生失分并非因为不会算,而是因为从一开始就没有分清逻辑结构与物理结构各自的讨论对象。文件的逻辑结构回答的是"用户看到的文件是什么样的",它描述文件内部记录的组织方式,通常分为有结构的记录式文件和无结构的流式文件两类。记录式文件内部由若干逻辑记录构成,这些记录定长或变长排列;流式文件则是无结构的字节序列,把整个文件看成一段连续的字节流。逻辑结构关心的是用户如何组织数据,与存储介质没有任何关系。
文件的物理结构回答的是另一个截然不同的问题:文件究竟如何存放在磁盘上。当用户说"我要读文件的第几个逻辑块"时,操作系统必须把这个逻辑块号翻译成磁盘上真实存在的物理块号,这个翻译过程所依据的规则,就是文件的物理结构。因此,文件的物理结构本质上是逻辑块号到物理块号的映射关系,它决定了文件在存储介质上的分配方式、访问速度和空间利用率。软考教材通常把文件物理结构归纳为连续结构、链接结构、索引结构三种基本形态,也有教材加入多重索引结构作为索引结构的延伸。
理解这一层定义,需要先厘清几个底层术语。磁盘的最小读写单位是扇区,通常为512字节,但操作系统很少直接以扇区为单位管理文件,而是把若干连续扇区合并为一个物理块,也称盘块或簇。物理块是文件分配的基本单位,文件占用物理块时以整块为单位,不足一块也要占满一块。逻辑块则是用户视角的抽象,文件被均匀切分为与物理块等长的逻辑块。当物理块大小确定后,逻辑块与物理块的对应关系便由具体的物理结构决定:连续结构中对应关系是一条线性公式,链接结构中对应关系是一条指针链,索引结构中对应关系是一张索引表。三种结构各有取舍,软考考查的重点正是这三者之间的优缺点权衡与计算细节。
值得强调的是,物理块、盘块、簇这三个词在软考语境中经常混用,指的都是文件分配的基本单位,只是不同教材与不同文件系统的叫法不同,考生在阅读题干时不必在术语上纠结。而扇区、磁道、柱面则是更底层的物理概念,与文件的分配结构没有直接对应关系,命题人有时会故意把它们掺进选项里制造干扰,考生要保持概念层次的清晰,切勿被这些底层术语带偏。
连续分配是最朴素、最直观的文件存放方式。它要求一个文件在磁盘上占据一段连续的物理块,目录项中只需记录两样东西:文件的起始物理块号和文件占用的物理块数量。有了这两个信息,逻辑块号到物理块号的换算异常简单,物理块号等于起始块号加上逻辑块号。正因为换算只涉及一次加法,连续分配的随机访问能力极强,无论是顺序读写还是任意定位,系统都能立刻算出目标块的位置,无需任何额外查表或遍历。
连续分配在顺序访问与随机访问两个维度上都表现优异,磁盘寻道次数少,磁头可以沿连续轨道顺序移动,传输效率高。这种特性决定了它非常适合只读介质,例如光盘上的文件系统就普遍采用连续分配。然而,连续分配的代价同样显著。文件在创建时就必须一次性确定它将来需要多大的连续空间,这要求系统能够预知文件的最终大小。对于需要不断追加内容、大小动态变化的文件而言,这种预知几乎不可能,一旦预留空间不足,文件便无法在原位置继续增长,只能整体搬迁到更大的连续区域,搬迁开销巨大。
为连续分配挑选空闲区域的算法,属于可变分区分配算法的范畴,软考常考的有首次适应、最佳适应、最坏适应和循环首次适应四种。首次适应算法从空闲区链表的起始位置开始查找,找到第一个能够容纳文件的空闲区就分配,剩余部分继续留在空闲链表中。它倾向于优先使用低地址区域的空闲区,在高地址区域保留大块连续空间。最佳适应算法则遍历整个空闲链表,挑选大小最接近文件需求的那个空闲区,目标是让剩余碎片尽可能小,但结果往往是把大空闲区切得七零八落,产生大量难以利用的微小碎片。最坏适应算法反其道而行之,每次都挑选最大的空闲区,希望切分后剩余部分仍然足够大,能够继续满足后续分配需求。循环首次适应是首次适应的改进,它不总是从链表头部开始,而是从上次分配结束的位置继续向后查找,使空闲区分布更均匀。这四种算法的核心矛盾,始终落在连续分配无法回避的外部碎片问题上。
内部碎片与外部碎片是软考最爱考查的一对概念,考生务必彻底分清。内部碎片指的是分配给某个文件的区域内,因分配单位过大而无法被利用的那部分空间。例如物理块大小为4KB,而文件实际只有3KB,剩下1KB虽然已经划归该文件,却存不下任何其他内容,这1KB就是内部碎片。内部碎片产生于分配粒度与需求不匹配,是"分出去了却用不上"的空间。
外部碎片则是"分不出去"的空间。连续分配运行一段时间后,经过反复的分配与释放,磁盘上会散布着许多各自独立、互不相邻的小块空闲区。每个小块单独拿出来都太小,无法满足任何文件的连续空间需求,但它们加在一起的总量却相当可观。这些被浪费掉的、位于空闲区之间的零散空间就是外部碎片。内部碎片靠缩小分配粒度来缓解,而外部碎片只能依靠紧凑技术——把已分配的文件在磁盘上整体搬移、重新拼拢,腾出大块连续空闲区——来消除,紧凑的代价是大量磁盘读写。连续分配以外部碎片为最大痛点,链接分配与索引分配之所以被发明,根本目的就是彻底摆脱外部碎片。
链接分配放弃了"一个文件必须连续存放"的约束,允许文件分散在磁盘的任意物理块中,靠指针把这些物理块串成一条链。隐式链接是最早的实现方式,它把指向下一块的指针直接存放在每个物理块的末尾。目录项只需记录链首的起始块号和末块号,读文件时从起始块出发,每读完一块就顺着块内指针找到下一块,直到末尾。隐式链接彻底消除了外部碎片,因为任何零散的单块都能被链入文件;文件也可以自由增长,只需在链尾再挂一个新块即可。
但隐式链接的缺点同样致命。指针占据了物理块的一部分空间,导致每个块能容纳的实际数据变少,块大小不再是2的整数次幂时还会带来换算麻烦。更严重的是随机访问能力几乎丧失,要读取文件的第N个逻辑块,必须从链首开始逐个块地顺着指针走N步,磁盘访问次数与块号成正比,性能随文件变大而急剧恶化。此外,链条一旦在某处断裂,断点之后的所有数据都将无法访问,可靠性令人担忧。正因为隐式链接随机访问太慢,它难以支撑对性能有要求的生产场景,显式链接便应运而生。
显式链接的改进思路是:既然指针分散在块内导致随机访问要反复读盘,那就把指针全部集中起来,单独放到一张表里。这张表就是文件分配表,简称为FAT。FAT是一个数组,下标对应物理块号,表项里存放的是该物理块的后继块号,文件链的末尾用特殊值标记。目录项仍然只需记录起始块号,但此后要定位第N个块时,无需再逐一读取数据块,只需在内存中顺着FAT表项跳跃即可。只要把FAT表常驻内存,随机访问就变成了纯粹的内存查表操作,几乎不产生额外磁盘读写。
FAT方案既保留了链接分配无外部碎片、可动态增长的全部优点,又把随机访问从磁盘级提速到内存级,是链接分配在工程上的集大成者,FAT12、FAT16、FAT32乃至早期的DOS与Windows文件系统都基于这一思想。软考命题人常把FAT与索引分配放在一起让考生辨析,关键区分点在于:FAT的指针表是全局共享的一张表,所有文件的链都登记在同一个FAT里;而索引分配是每个文件各自拥有一张专属的索引表。此外,FAT表项存的是"下一块地址"的链式关系,索引表的表项存的是"本文件的全部块地址"的并列关系,二者数据结构形态截然不同。
索引分配为每个文件单独建立一张索引表,索引表的每一个表项记录该文件一个物理块的块号,这张索引表本身也占用一个或多个物理块,称为索引块。目录项记录的不再是起始块号,而是索引块所在的位置。要访问文件的第N个逻辑块,只需在索引块中读出第N个表项,即可得到目标物理块号,随机访问只需一次索引块查找加一次数据块读取。索引分配同样没有外部碎片,文件可以自由增长,且随机访问性能远优于隐式链接,是三种结构中最均衡的方案。
单级索引的问题在于,索引块大小有限,能够登记的表项数量有限,从而限制了单个文件的最大容量。若物理块大小为1KB、每个块地址占4字节,则一个索引块最多容纳256个表项,单级索引能管理的文件最大只有256KB。对于动辄数GB的文件,单级索引远远不够,于是多级索引与混合索引成为必然选择。