【问题标题】:Growing Boost.Interprocess memory mapped file with single writer使用单个写入器增长 Boost.Interprocess 内存映射文件
【发布时间】:2017-08-28 22:58:46
【问题描述】:

我的问题是关于在单个写入进程和多个读取进程的上下文中使用 Boost.Interprocess 增长内存映射区域。 是否可以使用作者的managed_mapped_file::grow,假设读者不更新地图大小的变化是可以接受的?我的假设是读者的地图将保持有效,然后当我需要阅读器从作者那里获取最新更改时,我可以使用更新后的尺寸重新映射阅读器。这是正确的吗?

文档的Growing managed segments 部分说:

一旦创建了托管段,就不能再增加托管段了。这个限制不容易解决:每个附加到托管段的进程都需要停止,通知新的大小,它们需要重新映射托管段并继续工作。 [...]

这让我觉得我可以grow,只要我对不立即更新读者的情况感到满意。但是,文档继续说:

另一方面,Boost.Interprocess 提供离线段增长。这是什么意思?如果没有进程映射托管段,则该段可以增长。如果应用程序可以找到没有附加进程的时刻,它可以增长或缩小以适应托管段。 [...]managed_mapped_file 也提供了类似的功能来增加或shrink_to_fit 托管文件。请记住,在执行增长/收缩过程时,不应有任何进程修改文件/共享内存。否则,托管段将被损坏。

这让我觉得我不能做我想做的事,但我不明白为什么它不起作用。

【问题讨论】:

  • 您想谈谈更多关于段管理器与共享内存的烦恼吗?关于做出设计决策,我还有一些您可能想知道的警告:)
  • 是的,我非常想知道这些警告。
  • Come over 如果你有时间

标签: c++ boost mmap memory-mapped-files boost-interprocess


【解决方案1】:

让我们仔细区分shared_memory_object(技术含量低且不引人注目)和managed_shared_memory/managed_mapped_file(两者都使用segment_manager)。

你使用的是后者。


“假设读者不知道地图大小的变化是可以接受的”:

我不明白这个假设如何成立。

托管段实质上是一个辅助内存堆及其相关的控制结构。

控制结构包含诸如“堆”的实际范围等细节是有意义的。由于控制结构在共享内存中,它们也必须在已经映射到读取进程的部分中。

更改内存段的大小将更改该控制结构中的值,该控制结构在映射相同共享内存的所有进程中都是可见的。这显然会对实际上没有足够的内存映射来满足新范围的进程造成严重破坏(最多导致页面错误)。

现在,我可以想象一个非常聪明的实现,它可以规避对此类控制信息的需求(例如具有标记值的经典空闲阻止列表)。但我不认为 Boost Interprocess 会走这条路¹,而您引用的文字强烈表明该设计没有满足这种灵活性。

是否可以使用作者的 managed_mapped_file::grow,假设读者可以接受不更新地图大小的变化?

不,不行。

我的假设是阅读器的地图将保持有效,然后当我需要阅读器从作者那里获取最新更改时,我可以使用更新后的大小重新映射阅读器。这是正确的吗?

毫无疑问,/maps/ 会很好(取决于grow 的实现方式,但我想我之前已经通过实验证实了这一点)。问题在于在顶部运行的segment_manager。这是会被混淆的那个。

¹纯Fingerspitzengefühl

【讨论】:

  • @Iman 是的,基本上,您应该始终考虑围绕 managed_mapped_* 段构建一个包装器以实现版本控制/兼容性。但是,仅设置字节不会发出任何信号(需要explicit synchronization)。
  • 对于生产用途(除了一次性缓存数据),我可能会使用映射文件来包含元数据(如您所建议的),并可能在开始时在 mapped_region 中包含同步原语,然后是 managed_external_buffer放置在随后的 mapped_region 中。这听起来有点乏味,但它消除了许多有问题的边缘情况。
  • 这里的演示代码胜于雄辩:coliru.stacked-crooked.com/a/a7935f229bca5ba7
  • @Iman 用 managed_external_buffer 在 mapped_regions 内包裹在 file_mappings 内变得...更加乏味:godbolt.org/z/sfbjWG15P(Coliru 似乎不喜欢这样,但可能是沙盒限制)跨度>
  • 你不能在 Boost Interprocess 中使用它。他们的分配器返回 offset_ptr,但无论如何,即使你例如在共享缓冲区中使用 Boost Pool,您应该将这些指针存储在 offset_ptr 中。然后它们将保持有效。这里的命名变量不是 offset_ptrs。 Index 实例中的元素是 (StringPtr = offset_ptr<string>)。事实上,托管段中的命名对象是另一种选择,一种在重新映射后方便地获取共享对象图的根对象的方法。
猜你喜欢
  • 1970-01-01
  • 2016-02-23
  • 2011-04-15
  • 2016-01-08
  • 1970-01-01
  • 1970-01-01
  • 2018-05-18
  • 2012-10-11
  • 1970-01-01
相关资源
最近更新 更多