在Linux上malloc指定地址或页面(指定“最小偏移量”)

在Linux的LuaJIT中,所有VM管理的内存都必须低于2GB的进程内存边界,因为内部指针始终是32位的。因此,我希望自己管理更大的分配(使用FFI和malloc等),例如大纹理、音频、缓冲区等。

现在,我想确保这些被映射到以上2GB的界限,因为它们不会占用任何VM可管理的内存。

有没有办法malloc或者mmap(没有映射文件,或者在SHM中)分配一个指针到特定于该地址之上?甚至不需要占用2G,只需要将我的指针映射到一个更高(=非32位)的地址即可。

点赞
用户646887
用户646887
/* 分配一个 2GB 的内存块。如果该块位于地址的下限之下,再分配另一个 2GB 的内存块(这个必须在 2GB 之上,因为只有一个这么大的块可以放在 2GB 以下。)*/
void *HighMalloc(size_t size, void *address) {
    size_t mysize = (size_t)address;
    void *y, *x;
    if (mysize < size) {
        mysize = size;
    }
    y = x = malloc(mysize);
    if (x < address) {
        /* 内存开始于低地址。
         * mysize 太大,另一个同样大的块不能放在低地址上。
         * 因此,让我们再分配另一个块,
         * 然后释放正在使用低地址内存的块。*/
        x = malloc(mysize);
        free(y);
    }
    return x;
}

注意:

如果 size 小于 address,第二次调用 malloc 时可能在低地址有足够的空间。这就是为什么在这些情况下我增加了分配的大小。因此,不要使用它来分配小内存块。分配一个大块,然后手动将其分成更小的块。

2015-09-16 07:32:40
用户5218277
用户5218277

我所知道的唯一方法(可能不是最佳选择)是在Linux上使用mmap。在有些情况下,我需要分配大块内存并将其对齐到特定值,所以我使用了它(因为你可以指定内存块的地址和长度),但需要实现一些内存管理器单元,因为此时你将要管理分配(和释放)。

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

查看这里获取详细信息:http://man7.org/linux/man-pages/man2/mmap.2.html

如果要使其不映射到任何文件,请将flags设置为MAP_ANONYMOUS

MAP_ANONYMOUS

该映射不由任何文件支持;其内容初始化为零。忽略fdoffset参数;但是,一些实现要求如果指定了MAP_ANONYMOUS(或MAP_ANON),则fd必须为-1,并且可移植的应用程序应确保这一点。仅当内核为2.4以后的Linux支持在MAP_SHARED结合使用MAP_ANONYMOUS时才支持。

如果addr为空,则系统将为您选择可用地址,但由于您想在2G以上分配它,因此您需要管理已分配页面的列表,以了解哪些地址在2G以上使用。请注意,如果指定addr=X,并且mmap无法使用此地址,则不会失败,它只会选择另一个可以使用的地址,而不会有任何失败指示(除了返回的指针不等于addr)。但是,您可以使用MAP_FIXED标志强制执行您提供的地址,如果mmap无法使用它,则会失败(返回MAP_FAILED)。

MAP_FIXED

不要将addr解释为提示:将映射放置在该地址上。addr必须是页面大小的倍数。如果由addrlen指定的内存区域与任何现有映射的页面重叠,则现有映射的重叠部分将被丢弃。如果无法使用指定的地址,则mmap()将失败。由于为映射指定固定地址的要求不太可移植,因此不建议使用此选项。

编辑

请注意,不建议使用MAP_FIXED,因为如描述中所述

如果由addrlen指定的内存区域与任何现有映射的页面重叠,则现有映射的重叠部分将被丢弃。

而您将不会知道。最好检查addr是否等于mmap返回的地址。

2015-09-16 07:34:55