Skip to content

算法Algorithm

文件系统空闲区间管理

Free-space extent management · 空闲区间双索引 · First fit and best fit ranges

把设备空闲块组织成最大连续区间,维护地址和长度两种索引,执行首次适配、最佳适配与左右合并,并用双向反例区分策略。

要给一个文件取得连续7块,知道“设备还剩14块”不够。块分配必须找到真正可交付的地址;把连续空闲块合成区间,能直接回答哪里有一整段空间,也能解释为什么总量足够却分配失败。

形式陈述 ​

两个索引看的是同一组空闲块 ​

在块号0到31的教学池中,块0保留给元数据。空闲区间写作半开区间 [a,b),长度为 b−a>0。集合 F 中的区间彼此不重叠、也不相邻:如果两段相邻,就已经合并。因此每一条记录都是一段最大空闲区间。

每个非保留块恰属于空闲集合或某个已分配所有者,不能同时出现。本页先假设单线程串行操作、元数据有预留空间、没有快照共享;请求长度为任意正整数,无幂二取整。释放必须交回一个真实已分配且尚未释放的范围。

为同一个 F 建两个索引:地址索引按起点 a 排序;长度索引按 (b−a,a) 排序。后者以地址打破同长度平局。两者不是两份可以独立相信的空闲空间:它们必须始终精确描述同一组区间,所有插入、删除和切割都要同步更新。

两种选址合同 ​

请求 r 块时,首次适配(first fit)取地址最小、且长度至少为 r 的区间;最佳适配(best fit)取长度最小的合格区间,同长度再取地址最小者。本页都从选中区间的左端交付 [a,a+r),剩余后缀 [a+r,b) 非空才保留。

若没有合格区间,返回失败,空闲索引与所有权记录完全不变。“先改了一半索引再报告失败”并不满足这个合同。它会让下一请求见到一个从未真实存在过的空闲状态。

直觉

分配找大小,释放找邻居 ​

长度索引可直接寻找不小于 r 的最小键,适合最佳适配。地址索引让释放者寻找左前驱与右后继,判断是否正好接壤。XFS的空闲空间管理便有按起点和按块数排序的两棵树;本页使用它们的有序查找接口,不要求先实现树的分裂与旋转。

如果索引是普通平衡搜索树,最佳适配和更新可在 O(1+log⁡(m+1)) 时间内完成,其中 m=|F|;空树按常数时间处理。首次适配直接扫描地址序列最坏仍为 O(1+m)。要把首次适配也加速,需要在子树中增加最大可用长度等摘要,不能仅凭“用了树”就宣布所有选择都是对数时间。

最佳适配希望少留下零头;首次适配希望尽早找到合格位置。每次局部选择都可能改变未来的大区间形状。以下两条不同的请求序列说明,两种政策都不能只靠名称获得总体优越性。

例子与边界

请求5、7:保住大段的一方成功 ​

每个实验重新从 F={[1,9),[12,18)} 开始,长度分别8和6,总空闲14块,其余块已保留或分配。两个请求依次为5、7,中间不释放。

政策 分配5后 随后的7块请求
首次适配 交付 [1,6),空闲长度3和6 失败:没有7块连续区域
最佳适配 交付 [12,17),空闲长度8和1 从 [1,9) 交付 [1,8)

首次适配第二步还有9块空闲,却分散在长度3和6的两段中。请求合同要一段连续7块,不接受把两段拼成逻辑列表。若上层允许多extent,它可以改发多个请求;那是换了请求合同,不是原来7块请求突然成功。

请求5、3、6:小零头也可能更有用 ​

重置同样的初态,这次请求5、3、6。首次适配先用 [1,6),第二次恰好用完余下 [6,9),第三次用完整 [12,18),三次全部成功。

最佳适配先用 [12,17),留下1块和8块。请求3只能切走 [1,4),剩下长度5和1;最后请求6失败。初态与总请求量仍是14,改变顺序和长度就改变了谁成功。两组轨迹一起排除了“最佳适配总比首次适配少失败”及其反命题。

释放中间块时,要同时看两边 ​

另开一个释放实验:连续区间 [1,9) 已交付为A=[1,3)、B=[3,6)、C=[6,9),原来的 [12,18) 仍空闲。先释放A和C,此时三段空闲为 [1,3)、[6,9)、[12,18)。

再释放B。地址前驱在3结束,与B起点相等;地址后继从6开始,与B终点相等。应从两个索引中删除这两条邻居记录,再插入合并后的 [1,9),最后仍有 [12,18)。如果只合左边,留下 [1,6) 和 [6,9) 两条相邻空闲记录,就破坏了“最大区间”不变量,后续8块请求会被错误拒绝。

在长度索引中,合并前的键是 (2,1) 和 (3,6),合并后必须替换为 (8,1)。只改地址索引、忘删旧长度键,会让分配器再次交付已被大区间覆盖的旧片段,造成重复分配。两个索引一致属于正确性要求,不只是查询效率要求。

元数据也需要空间和恢复规则 ​

释放空间可能让索引增加新记录,树节点也可能需要分裂。因此“设备已经满了,但释放总会成功,因为释放不占空间”并不成立。实际系统会为元数据维护保留空间或专门的空闲列表;本页把预留元数据作为前提,避免递归地先分配空间才能记录一次释放。

本页原子操作指并发观察下不暴露半更新状态,不自动意味着掉电原子性。把双索引、所有权与文件映射持久地一起更新,可以复用文件系统重做日志;若只把其中一个索引刷盘,恢复后仍会出现两份互相矛盾的空间账。

推论与应用

三个数字给出不同信息 ​

总空闲量 T=∑[a,b)∈F(b−a) 描述容量;最大空闲长度 M=max[a,b)∈F(b−a) 描述本页一次连续请求的可行性;区间数 m 描述碎片形状及索引规模。空集合时约定 M=0。在没有额外资格、对齐和保留预算的当前模型里,请求成功当且仅当 r≤M,而 r≤T 只是必要条件。

Extent映射能使用这里交付的区间,但映射连续性与空闲连续性是两个不同状态。文件删去中间一段后,物理空间是否能立即回收,还要检查快照、未完成I/O与恢复根是否仍引用它;本页只能合并已经真正交回的块。

复算终点除了写出分配地址,还应给出两套排序键、每个所有者的区间以及空闲总数。对任何失败或非法重复释放,逐项比较操作前后的状态应完全一致。这样可以区分“策略选择不理想”和“分配器把同一块给了两个人”这两种本质不同的问题。

参考资料
关系图谱3 个相邻概念 · 2 类关系

拖动节点调整位置。

显示关系

显示:依赖

  1. 前置三跳
  2. 前置二跳
  3. 前置一跳
  4. 当前条目
  5. 后续一跳
  6. 后续二跳
  7. 后续三跳
文字版关系按与当前条目的最短距离分组
类型化关系