一尘不染

快速调整mmap文件的大小

linux

我需要一个非常大的mmap文件的无拷贝调整大小,同时仍然允许并发访问读取器线程。

一种简单的方法是在同一进程中在同一文件上使用两个MAP_SHARED映射(增长文件,然后创建第二个映射,其中包括增长的区域),然后在所有可访问该文件的读取器完成操作后取消映射旧映射。但是,我很好奇下面的方案是否可行,如果可行,是否有任何好处。

  1. 使用MAP_PRIVATE映射文件
  2. 对多个线程进行只读访问
  3. 要么为文件获取一个互斥锁,然后将其写入内存(假设这样做是为了使可能正在读取该内存的读者不会被它弄乱)
  4. 或获取互斥锁,但增加文件的大小并使用mremap将其移动到新地址(调整映射的大小,而无需复制或不必要的文件IO。)

疯狂的部分出现在(4)。如果您移动内存,旧地址将变为无效,并且仍在读取的读取器可能会突然出现访问冲突。如果我们修改读取器以捕获此访问冲突,然后重新启动操作(即,不要重新读取错误的地址,请给定偏移量的地址和mremap的新基址来重新计算),该怎么办?是的,我知道这很邪恶。
,但在我看来,读者只能成功读取旧地址上的数据,否则会因访问冲突而失败并重试。如果采取足够的措施,那 应该
是安全的。由于调整大小不会经常发生,因此读者最终会成功,并且不会陷入重试循环中。

如果在阅读器仍具有指向旧地址空间的指针的情况下重新使用该旧地址空间,则可能会发生问题。这样就不会出现访问冲突,但是数据将是不正确的,程序将进入未定义行为的独角兽和糖果填充区(其中通常既没有独角兽也没有糖果)。

但是,如果您完全控制了分配并且可以确定在此期间发生的任何分配都不会再使用旧的地址空间,那么这应该不成问题,并且行为也不应不确定。

我对吗?能行吗?与使用两个MAP_SHARED映射相比,这有什么好处吗?


阅读 1213

收藏
2020-06-07

共1个答案

一尘不染

对于我来说,很难想象您不知道文件可以多大的上限。假设是这样,您可以通过在首次使用mmap()映射文件时提供该大小来“保留”文件的最大大小。当然,超出文件实际大小的任何访问都将导致访问冲突,但这就是您无论如何都希望其工作的方式-
您可能会争辩说,保留额外的地址空间 可确保 访问冲突,而不是使该地址范围不开放被诸如mmap()或malloc()之类的其他调用所使用。

无论如何,关键是我的解决方案,您永远不会 移动 地址范围,只更改其大小,现在锁定就在为每个线程提供当前有效大小的数据结构周围。

如果文件太多,则每个文件的最大映射都使您用尽了地址空间,那么我的解决方案将不起作用,但是这是64位地址空间的使用期限,因此希望最大映射大小没有问题。

(只是为了确保我不会忘记一些愚蠢的东西,我确实写了一个小程序说服自己,当您尝试访问超出文件大小的文件时,创建大于文件大小的映射会导致访问冲突,然后可以正常工作一旦将文件ftruncate()变大,所有文件都将从第一次mmap()调用返回的地址相同。)

2020-06-07