用于表示/分配文件中的空闲空间的数据结构和算法

时间:2011-02-01 21:28:25

标签: algorithm data-structures filesystems

我有一个带有“洞”的文件,想要用数据填充它们;我还需要能够释放“二手”空间并腾出空间。

我正在考虑使用映射偏移量和长度的双向贴图。但是,如果文件中存在很小的间隙,我不确定这是否是最好的方法。位图可以工作,但我不知道如何在某些空间区域动态切换到动态。也许某种基数树是要走的路?

对于它的价值,我能够加速现代文件系统设计(ZFS,HFS +,NTFS,XFS,分机......),我发现它们的解决方案严重不足。

我的目标是节省相当多的空间(因此关注小碎片)。如果我不关心那个,我会去找两个展开的树...一个按偏移排序,另一个按长度排序,绑定按偏移量排序。请注意,这为您提供了所有操作的分摊log(n),其工作设置时间为log(m)...相当不错......但是,如前所述,不处理有关高碎片的问题。

4 个答案:

答案 0 :(得分:1)

我已经发布了那样做的商业软件。在最新的迭代中,我们最终将文件块排序为“type”和“index”,因此您可以读取或写入“foo类型的第三个块”。该文件最终结构为:

1)文件头。主类型列表中的点。 2)数据。每个块都有一个包含类型,索引,逻辑大小和填充大小的标头。 3)每种给定类型的(偏移,大小)元组的数组。 4)跟踪类型的(类型,偏移,计数)数组。

我们定义它以使每个块都是原子单元。你开始写一个新的块,并在开始其他任何事情之前写完了。您还可以“设置”块的内容。开始一个新的块总是附加在文件的末尾,因此您可以根据需要追加,而不会破坏块。 “设置”块可以重复使用空块。

当您打开文件时,我们将所有索引加载到RAM中。刷新或关闭文件时,我们在文件末尾重写了每个更改的索引,然后在文件末尾重写索引索引,然后更新前面的标题。这意味着对文件的更改都是原子的 - 要么提交到标题更新的位置,要么不提交。 (有些系统使用两个标题8 kB的副本来保留标题,即使磁盘扇区变坏也是如此;我们没有把它拿走那么远)

其中一个块“类型”是“空闲块”。当重写更改的索引时,并且当替换块的内容时,磁盘上的旧空间被合并到保留在空闲块数组中的空闲列表中。相邻的自由区块合并为一个更大的区块。当您“设置内容”或更新类型块索引时,会重复使用空闲块,但不会使用最后写入的索引索引。

因为索引始终保存在内存中,所以使用打开的文件非常快 - 通常只需一次读取即可获取单个块的数据(或获取流式块的句柄)。打开和关闭有点复杂,因为它需要加载和刷新索引。如果它成为一个问题,我们可以按需加载二级类型索引,而不是预先加载以分摊该成本,但它对我们来说从来都不是问题。

持久性(磁盘上)存储的首要任务:稳健性!即使计算机在使用文件时断电,也不要丢失数据! 磁盘存储的第二优先级:不要做超过必要的I / O!寻求昂贵。在闪存驱动器上,每个单独的I / O都很昂贵,写入也是如此。尝试对齐和批量I / O.使用像malloc()这样的磁盘存储通常不是很好,因为它做了太多的搜索。这也是我不喜欢内存映射文件的原因 - 人们倾向于将它们视为RAM,然后I / O模式变得非常昂贵。

答案 1 :(得分:1)

对于内存管理,我是BiBOP *方法的粉丝,它通常可以有效地管理碎片。

这个想法是根据数据的大小来分离数据。这样,在“包”中你只有“相同大小的小块”页面:

  • 无需明确存储尺寸,这取决于您所在的行李
  • 包内没有“真正的”碎片

该包保留了可用页面的简单免费列表。每个页面都会在这些单元的叠加层中保留一个可用存储单元的空闲列表。

您需要一个索引来将尺寸映射到相应的包。

您还需要对“超出规范”的请求(即要求分配大于页面大小的请求)进行特殊处理。


这个存储空间效率非常高,特别是对于小型对象,因为开销不是每个对象,但是有一个缺点:你可以最终得到仍然包含一个或两个占用存储单元的“几乎空”的页面

如果您能够“移动”现有对象,则可以减轻这种情况。这有效地允许合并页面。

(*)BiBOP:Big Bag Of Pages

答案 2 :(得分:1)

我建议根据FUSE制作自定义文件系统(当然可能包含一个文件)。您可以a lot of available solutions for FUSE - 我建议您选择不相关但最简单的项目,以便轻松学习。

选择何种算法和数据结构,它会根据您的需求进行高度深化。它可以是:使用动态压缩/解压缩将地图,列表或文件拆分为块。

您提出的数据结构是好主意。正如你清楚地看到有一个权衡:碎片与压缩。

一方面 - 最佳压实,最高碎片 - 展开和许多其他类型的树木。

另一方面 - 最低碎片,最差压缩 - 链表。

中间有B树和其他人。

据我了解,您表示优先考虑:节省空间 - 同时注重性能。

我建议您使用混合数据结构以实现所有要求。

  • 一种连续的数据块列表
  • 当前“添加/删除”操作的一种树
  • 根据需要提供数据时,从树中分配。删除后,使用树跟踪“已删除”的内容。
  • 混合 - >在每个操作期间(或在空闲时刻)执行“逐步”去碎片化,并将保留在树中的更改应用于连续块,同时缓慢移动它们。

此解决方案可以根据需要快速响应,同时“优化”使用时的内容(例如“每次读取10MB数据 - > 1MB的碎片整理”或空闲时刻。

答案 3 :(得分:0)

最简单的解决方案是free list:保留空闲块的链接列表,重用可用空间来存储列表中下一个块的地址。