【问题标题】:How do I choose a fixed address for mmap?如何为 mmap 选择固定地址?
【发布时间】:2011-06-22 20:23:33
【问题描述】:

mmap() 可以选择提供一个固定位置来放置地图。我想 mmap 一个文件,然后在每个程序中的相同虚拟地址处将它提供给几个不同的程序。我不在乎地址是什么,只要他们都使用相同的地址即可。如果需要,地址可以由其中一个在运行时选择(并通过其他方式与其他人通信)。

是否有一个内存区域我可以映射到 Linux 保证不被使用(由应用程序和内核)?如何找到可在多个正在运行的应用程序中使用的地址?

【问题讨论】:

  • 将每个程序的地址空间布局传达给某个主节点,该主节点合并分配然后选择一个空闲地址,是否存在问题?
  • 不,这可能行得通。您应该将其发布为答案:-P
  • 我觉得用0xcaffe1ne000还是挺安全的。

标签: c linux mmap


【解决方案1】:

不是真的,不。在现代 linux 系统上使用地址空间随机化,很难保证可以使用或不使用哪些地址。

另外,如果您正在考虑使用MAP_FIXED,那么请注意,您需要非常小心,因为它会导致 mmap 取消映射可能已经映射到该地址的任何内容,这通常是一件非常糟糕的事情。

我真的认为您需要为您的问题找到另一种解决方案...

【讨论】:

  • +1 没错,最好从存储共享数据的“基地址偏移”的角度来考虑,这使得固定的绝对地址变得多余。
  • @Damon 用于最初设计为不共享的大型多样数据集,将这些偏移量更改回真实地址可能非常困难。
  • 顺便说一句:MAP_FIXED可以安全使用如果你首先在没有MAP_FIXED的情况下进行单个映射,然后使用MAP_FIXED在相对的固定地址创建各种映射在内核分配位置的原始一次性映射之上相互连接。有了足够大的映射大小,这可能会解决 OP 的问题。
  • 我不认为这个答案是决定性的。如果您编写并运行一个简单的程序来打印mmap 调用的返回地址(它没有指定地址),我们可以看到虽然地址在每次运行时都是随机的,但随机化的范围非常有限(在至少与 64 位地址空间的大小相比)。我强烈怀疑内核可以为某些始终可用的地址范围提供一些保证,但还没有找到任何文档。
【解决方案2】:

两个进程可以使用 shm_open() 和 mmap() 将一个共享内存块映射到同一个虚拟地址;也就是说,从 mmap() 返回的虚拟地址对于两个进程可以是相同的。我发现 Linux 默认会为同一块共享内存的不同进程提供不同的虚拟地址,但是使用带有 MAP_FIXED 的 mmap() 会强制 Linux 为多个进程提供相同的虚拟地址。

创建共享内存块的进程必须将虚拟地址存储在某个地方,无论是在共享内存中、文件中还是使用其他方法,以便另一个进程可以确定原始虚拟地址。然后在 mmap() 调用中使用已知的虚拟地址以及 MAP_FIXED 标志。

我能够使用共享内存来执行此操作。这样做时,“黄金”虚拟地址存储在共享内存块中;我创建了一个包含许多项目的结构,地址是其中之一,并在块的开头对其进行了初始化。

想要映射共享内存的进程必须执行 mmap() 函数两次;一次获取“黄金”虚拟地址,然后使用 MAP_FIXED 标志将该块映射到该地址。

有趣的是,我正在使用运行 2.6 内核的嵌入式系统。默认情况下,它将为所有对给定文件描述符的 mmap() 调用提供相同的虚拟地址。去图吧。

鲍勃·维尔卡

【讨论】:

  • 我还对mmap 为同一块共享内存提供不同的地址这一事实感到恼火。为了解决这个问题,我尝试使用MAP_FIXED。但它不起作用......你是如何让它起作用的?请在您的答案中添加示例代码。另见stackoverflow.com/questions/50093733
【解决方案3】:

您可以考虑使用shmget()shmat() 等来创建共享内存对象。首先让进程获得初始化文件中读取的共享内存对象并将其复制到共享内存对象地址的权限空间。现在任何其他简单地获得返回共享内存 ID 值的进程都可以访问共享内存空间中的数据。因此,例如,您可以采用一些类型初始化方案,如下所示:

#include <sys/shm.h>

#define KEYVALUE 1000 //arbitrary value ... just needs to be shared between your processes

int file_size
//read your file and obtain its size in bytes;

//try to create the shared memory object
int shared_mem_id;
void* shared_mem_ptr = NULL;

if ((shared_mem_id = shmget(KEYVALUE, file_size, S_IRUSR | S_IWUSR IPC_CREAT | IPC_EXCL)) == -1)
{
    if (errno == EEXIST)
    {
        //shared memory segment was already created, so just get its ID value
        shared_mem_id = shmget(KEYVALUE, file_size, S_IRUSR | S_IWUSR);
        shared_mem_ptr = shmat(shared_mem_id, NULL, 0)
    }
    else
    {
        perror("Unable to create shared memory object");
        exit(1);
    }
}
else
{
    shared_mem_ptr = shmat(shared_mem_id, NULL, 0);

    //copy your file into shared memory via the shared_mem_ptr

}

//work with the shared data ...

使用共享内存对象的最后一个进程,将在销毁它之前,将修改后的内容从共享内存复制回实际文件。您可能还想在共享内存对象的开头分配一个可用于同步的结构,即初始化进程将设置某种类型的“幻数”,以便您的其他进程知道数据在访问之前已在共享内存对象中正确初始化。或者,您可以使用命名信号量或 System V 信号量来确保在初始化之前没有进程尝试访问共享内存对象。

【讨论】:

  • SysV IPC (shmget/shmat) 已弃用。
  • @R.. 是什么取代了该功能?是否使用shm_open()ftruncate() 以及mmap()
  • 是的。如果您愿意,甚至可以使用普通文件而不是 shm_open(这使您能够更好地控制权限和所有内容,但有浪费资源将数据刷新到磁盘的风险)。
  • @R.. 当您说使用“普通文件”时,您指的是通过mmap() 对它们进行内存映射,对吧?
猜你喜欢
  • 2011-08-21
  • 1970-01-01
  • 2021-01-20
  • 1970-01-01
  • 2014-11-24
  • 2011-04-08
  • 2011-11-01
  • 2021-03-29
  • 1970-01-01
相关资源
最近更新 更多