2

前提是:

  • 我要求的大小是页面大小的倍数
  • 我请求的起始地址是最后一次分配的大小+起始地址

如果我在使用 mmap 在堆上分配内存时总是遵循这些规则,那么返回的地址会是连续的吗?或者他们之间可能有差距?

4

3 回答 3

3

您可以使用 MAP_FIXED 标志获得所需的行为。不幸的是,对于您的目标,它并没有得到普遍支持,因此您需要检查返回值以确保它为您提供了您请求的分配。为了获得良好的可移植性,您需要在调用返回 0 时制定备份计划。

于 2012-11-05T15:02:11.633 回答
2

快速回答:不一定。在各种机器上进行有限的广泛测试时,它很有可能“几乎总是有效”,但这绝对不是一个好的做法。大多数 Linux 版本都支持 MAP_FIXED 标志,但根据我的经验,它也有问题。避免。

在您的情况下,更好的是一次简单地分配您需要的所有内容,然后手动将指针分配给映射的每个子部分:

int LengthOf_FirstThing = 0x18000;
int LengthOf_SecondThing = 0x10100;
int LengthOf_ThirdThing = 0x20000;

int _pagesize = getpagesize();
int _pagemask = _pagesize - 1;

size_t sizeOfEverything = LengthOf_FirstThing + LengthOf_SecondThing + LengthOf_ThirdThing;
sizeOfEverything = (sizeOfEverything + _pagemask) & ~(_pagemask);

int8_t* result = (int8_t*)mmap(nullptr, sizeOfEverything, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
int8_t* myFirstThing = result;
int8_t* mySecondThing = myFirstThing + LengthOf_FirstThing;
int8_t* myThirdThing = mySecondThing + LengthOf_SecondThing;

这种方法的一个优点还在于,您映射的每一件事都不必与页面大小严格对齐。最重要的是,它确保了完全连续的内存。

更长的答案: mmap() 的实现可以完全无视“提示”地址,因此您永远不应该期望该地址得到尊重。这可能比预期的更常见,因为某些实现实际上可能不支持新 mmap() 的页面大小粒度。它们可能会将有效的起始映射限制为 16k 或 64k 边界,以帮助减少管理非常大的虚拟地址空间所需的开销。这样的实现将始终忽略未与此类边界对齐的 mmap() 提示。

此外, mmap() 根本不从堆中分配内存。堆是创建进程时由 C 运行时库(*nix 上的 glibc)创建/保留的内存区域。malloc() 和 new/delete 通常是从堆中提取的唯一函数,以及可能在内部使用 malloc/new 的任何库。堆本身通常由内部调用 mmap() 创建和管理。

于 2012-12-23T22:34:35.650 回答
1

我认为这没有具体说明,而是所谓的“实施细节”。也就是说,您不应该依赖一种行为或另一种行为,而是假设指针是不透明的并且不关心它的确切值。

(也就是说,可能会有黑客攻击的地点和时间。在这种情况下,您需要准确了解您的操作系统的行为方式。)

于 2012-11-05T15:00:12.110 回答