3

我有一个场景,我需要在进程空间中线性映射文件的非线性部分。

例如,如果文件是10页,我可能需要映射前3页,跳过4页,最后3页。映射应该是线性的,进程空间中的st增量访问允许在第3页之后转到文件的第8页,作为页4、5、6 和 7 未映射。

我想知道这在 Linux 中是否可行。

谢谢。

4

2 回答 2

4

mmap()多次调用MAP_FIXED以指定第二个和后续映射的固定地址的策略应该可以工作,但问题是如果在第一次映射之后立即有任何东西已经映射到内存中,它将被破坏,因为MAP_FIXED自动取消映射任何东西在制作新映射之前曾经在那里。

我在这里查看了 Linux 系统上地址空间中一些映射的布局,我观察到,至少在某些时候,内核为内存映射选择的地址从高地址向下增长到低地址地址。即,在最近的现有映射所使用的地址空间之下的地址空间被赋予一个新的映射。在该策略下,当您进行第一次映射时,几乎可以保证紧随该映射的地址空间已经被其他东西占用(并且它可能也很重要,例如系统库)。其他系统(不同的内核版本、不同的体系结构或非 Linux 等)可能使用不同的地址空间分配策略,这不会使这个问题不太可能发生,

  1. 首先创建一个虚拟映射,它是您要构建的所有映射的大小之和。因此,如果要映射文件的前 3 页,则跳过 4 页,然后再映射 3 页,进行 6 页的虚拟映射。

    对于这个虚拟映射,您可以只映射匿名内存 ( MAP_ANONYMOUS)。感谢Basile Starynkevitch提出也MAP_NORESERVE用于此映射的建议。

  2. 用您实际需要的文件的映射逐个替换此虚拟映射,MAP_FIXED用于指定您希望每个映射出现的精确地址。

    编辑:我最初建议在重用地址空间进行新映射之前破坏虚拟munmap()映射,但感谢jstine指出这是不必要的(如果您的程序是多线程的,它会引入竞争条件)。

    对于第一个映射,使用虚拟映射的起始地址。计算第二个映射的地址为虚拟映射的起始地址加上第一个映射的大小。这应该将第二个映射放在第一个映射结束之后。第三个和第四个映射依此类推。在您的方案中,所有内容都是页面大小和页面对齐的,因此不会因对齐而出现间隙。

在步骤 2 中完成所有映射后,原始虚拟映射应该没有任何剩余。

于 2013-01-15T22:03:19.710 回答
4

In addition of previous answer by Celada, you might also be interested by the Linux specific remap_file_pages(2) syscall after your mmap(2).

And the first mmap might use MAP_NORESERVE to avoid spending swap space (and just reserve the address space, not the data).

于 2013-01-16T05:53:14.733 回答