如何为共享内存映射选择固定地址

时间:2011-05-09 16:10:57

标签: c++ boost shared-memory interprocess

我想在几个进程之间使用共享内存,并希望能够继续使用原始指针(和stl容器)。

为此,我使用映射在固定地址的共享内存

segment = new boost::interprocess::managed_shared_memory(
    boost::interprocess::open_or_create,
    "MySegmentName",
    1048576, // alloc size
    (void *)0x400000000LL // fixed address
);

选择此固定地址的好策略是什么?例如,我应该使用一个非常高的数字来减少我用完堆空间的可能性吗?

4 个答案:

答案 0 :(得分:15)

这是一个难题。如果你要求一个程序来创建子项,并且只有父项和子项将使用内存段,请确保在fork之前映射它。孩子们将自动从父母那里继承映射,不需要使用固定的地址。

如果不是,那么首先要考虑的是你是否真的需要使用原始STL容器而不是boost进程间容器。您已经使用boost进程来分配共享内存段表明您使用boost时没有任何问题,因此我可以想到使用STL容器的唯一优势是您不必移植现有代码。请记住,为了使用固定地址,容器以及它们包含指向的指针(假设您正在使用指针容器)将需要保存在共享内存空间中。 / p>

如果您确定这是您想要的,那么您必须找出一些方法来协商地址。请记住,操作系统可以拒绝您所需的固定内存地址。如果该地址的页面已经映射到内存或已分配,它将拒绝一个地址。因为不同的程序会在不同的时间分配不同数量的内存,哪些页面可用且哪些页面不可用会因程序而异。

因此,您需要程序在内存地址上达成共识。这意味着可能必须尝试和拒绝几个地址。如果有可能在启动后的某个时候新程序会变得有兴趣,寻求共识将不得不重新开始。该算法看起来像这样:

  1. 程序A向所有其他程序提出内存地址X.
  2. 其他程序以true或false响应,以指示地址X处的内存映射是否成功。
  3. 如果程序A收到任何错误回复,请转到#1。
  4. 程序A向其他程序发送消息,让他们知道地址已经过验证并可能被使用。
  5. 如果新应用对数据感兴趣,则必须通知程序A它想要一个地址。
  6. 程序A然后必须告诉所有其他程序停止使用数据并转到#1。
  7. 要想出A应该提出的地址,你可以让A映射成一个非固定内存段,查看它映射到的地址,并提出该地址。如果不满意,请映射另一个细分并提出建议。您需要在某个时刻取消映射段,但是您无法立即取消映射它们,因为如果取消映射然后重新映射相同大小的段,则操作系统将反复给您相同的地址。 请记住,您可能永远无法达成共识;并不能保证在所有流程的公共位置都有足够大的段。如果您的程序全部独立使用几乎所有内存,可能会发生这种情况,例如,如果它们通过大量交换备份(尽管如果您足够关心性能以使用共享内存,希望您可以避免交换)。

    以上所有假设您处于相对受限的地址空间。 如果您使用64位,则可以使用。大多数计算机的RAM +交换将远远小于64位允许的内存,因此您可以将内存映射到一个非常远的固定地址,而所有进程都不可能已映射。我建议至少2 ^ 48,因为目前的64位x86处理器不会超出该范围(尽管指针是64位,你只能插入48位允许的RAM,仍然是一吨写这篇文章的时间)。虽然智能堆分配器没有理由不能利用广阔的地址空间来减少其簿记工作,但要真正强大,您仍需要建立共识。请记住,您至少希望地址可以配置 - 即使我们很快就没有那么多内存,从现在开始,然后其他人可能有相同的想法并选择您的地址。

    要进行双向通信,您可以使用任何套接字,管道或其他共享内存段。您的操作系统可能提供其他形式的IPC。但强烈认为,如果您刚刚使用了boost进程间容器,那么现在可能会引入比您必须处理的更多的复杂性;)

答案 1 :(得分:3)

从配置文件中读取地址。这样可以轻松进行实验,并随着环境的变化轻松更改地址。

答案 2 :(得分:1)

出于安全原因,即使不使用分支或线程,也不要将硬编码的绝对地址用作共享内存区域。这绕过了所有ASLR保护措施。它使任何攻击者都可以在进程的地址空间中预测可预测的位置。在二进制文件中搜索这样的硬编码指针非常容易。

您已经被http://reversingonwindows.blogspot.sg/2013/12/hardcoded-pointers.html选为例如如何使软件不那么安全,绕过ASLR。 第二个不好的例子在boost library

地址空间需要在运行时在通信方之间进行协商。

答案 3 :(得分:1)

我的解决方案:

初始化程序允许系统选择适当的段地址。该地址写入光盘并根据需要检索后续程序使用。

注意事项: 我使用64位fedora 21和Kdevelop 4.7,发现' void *'是64位长。写入段头地址的光盘涉及 sprintf(bu,"%p",指针);并写一个文本文件:

恢复读取此文件并将十六进制数字解码为长整数'值。这将返回给调用者,并将其转换为(void *)

我还发现将所有访问例程分组到一个文件夹中 高于个别流程的水平(每个流程本身就是一个项目)有助于拯救我的理智而牺牲一个异常的“#include”#39;在过程文件中

David N Laine