【问题标题】:Is it possible to allocate large amount of virtual memory in linux?是否可以在linux中分配大量虚拟内存?
【发布时间】:2020-02-19 18:24:51
【问题描述】:

为了某些目的,分配大量虚拟空间并仅在访问的页面中分页会很有效。分配大量内存是瞬时的,实际上并不抢页:

char* p = new char[1024*1024*1024*256];

好的,上面指出的错误,因为它是一个 32 位数字。

我希望 new 正在调用调用 sbrk 的 malloc,并且当我访问开始后 4Gb 的位置时,它会尝试将任务内存扩展那么多?

这是完整的程序:

#include <cstdint>
int main() {
  constexpr uint64_t GB = 1ULL << 30;
  char* p = new char[256*GB]; // allocate large block of virtual space
  p[0] = 1;
  p[1000000000] = 1;
  p[2000000000] = 1;
}

现在,当我尝试分配大量时,我得到了 bad_alloc,所以很明显 malloc 不起作用。

我的印象是 mmap 会映射到文件,但由于有人建议我正在研究它。

好的,所以 mmap 似乎支持分配大面积的虚拟内存,但它需要一个文件描述符。创建巨大的内存数据结构可能是一种胜利,但如果它们必须由文件支持,则不是:

以下代码使用 mmap,即使我不喜欢附加到文件的想法。我不知道要在虚拟内存中输入什么数字来请求,所以选择了 0x800000000。 mmap 返回 -1,所以很明显我做错了什么:

#include <cstdint>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

int main() {
  constexpr uint64_t GB = 1ULL << 30;
  void *addr = (void*)0x8000000000ULL;
  int fd = creat("garbagefile.dat", 0660);
  char* p = (char*)mmap(addr, 256*GB, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
  p[0] = 1;
  p[1000000000] = 1;
  p[2000000000] = 1;
  close(fd);
}

有没有办法分配一大块虚拟内存并稀疏地访问页面,或者这不可行?

【问题讨论】:

  • 我对这些东西不是很熟悉或不确定,但我认为mmap 系统调用可以用于此。
  • 正如@aschepler 所说,可能使用了mmap。检查strace
  • 请遵守国际公认的 SI 单位制 - en.wikipedia.org/wiki/International_System_of_Units 请使用 B 表示 bytesb 表示,所以一兆字节是 1MB,一千兆字节是 1GB (en.wikipedia.org/wiki/Gigabyte),一千兆字节是 1Gb (en.wikipedia.org/wiki/Gigabit)。谢谢。
  • @DanielLangr:这是第一个问题。调查 mmap
  • @MarkSetchell 根据 SI 单位制,G 代表 10^9。如果您需要精确,请在此处使用 GiB。

标签: c++ linux memory-management virtual-memory


【解决方案1】:

linux可以分配大量的虚拟内存吗?

可能。但您可能需要将其配置为允许:

Linux 内核支持以下过量使用处理模式

0 - 启发式过度使用处理。明显的地址过度使用 空间被拒绝。用于典型系统。它确保了一个认真 野分配失败,同时允许过度使用以减少交换 用法。允许 root 在此分配稍微多一点的内存 模式。这是默认设置。

1 - 总是过度使用。适用于一些科学应用。 经典示例是使用稀疏数组的代码并且仅依赖于 虚拟内存几乎完全由零页组成。

2 - 不要过度使用。系统的总地址空间提交 不允许超过交换 + 可配置的数量(默认为 50%) 的物理 RAM。根据您使用的数量,在大多数情况下 这意味着进程在访问时不会被杀死 页面,但会收到适当的内存分配错误。

对于想要保证其内存的应用程序很有用 分配将在未来可用,而无需 初始化每一页。

过量使用策略是通过 sysctl `vm.overcommit_memory' 设置的。

所以,如果你想分配比物理内存更多的虚拟内存,那么你会想要:

# in shell
sysctl -w vm.overcommit_memory=1

RLIMIT_AS 进程的虚拟内存(地址空间)的最大大小,以字节为单位。此限制会影响对 brk(2)、mmap(2) 和 mremap(2) 的调用,这些调用在超过此限制时会失败并返回错误 ENOMEM。自动堆栈扩展也会失败(如果没有通过 sigaltstack(2) 提供可用的备用堆栈,则会生成一个终止进程的 SIGSEGV)。由于该值为 long,因此在具有 32 位长度的机器上,此限制最多为 2 GiB,或者此资源是无限的。

所以,你会想要:

setrlimit(RLIMIT_AS, {
    .rlim_cur = RLIM_INFINITY,
    .rlim_max = RLIM_INFINITY,
});

或者,如果您不能授予进程执行此操作的权限,那么您可以在 /etc/security/limits.conf 中持久配置它,这将影响(用户/组的)所有进程。


好的,所以 mmap 似乎支持......但它需要一个文件描述符。 ...可能是一个胜利,但如果它们必须有文件支持,则不会......我不喜欢附加到文件的想法

您不需要使用文件支持的 mmap。这有 MAP_ANONYMOUS。

我不知道要输入什么号码来请求

然后使用null。示例:

mmap(nullptr, 256*GB, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)

也就是说,如果您按照描述配置了系统,那么new 应该和mmap 一样好用。它可能会使用malloc,而mmap 可能会用于像这样的大型分配。


额外提示:您可能会受益于使用HugeTLB Pages

【讨论】:

  • 很好的答案! new/malloc 实际上确实有效,但我宁愿不必设置系统范围内的过度使用内存策略。有什么方法可以仅针对我的流程覆盖它吗?
  • @Dov 据我所知,该设置只能在内核范围内更改。
  • 也许任务设置了参数,分配,然后设置回来?有点笨拙
【解决方案2】:

256*GB 的值不适合 32 位整数类型的范围。尝试将uint64_t 作为GB 的一种类型:

constexpr uint64_t GB = 1024*1024*1024;

或者,强制 64 位乘法:

char* p = new char[256ULL * GB];

OT:我更喜欢GB的这个定义:

constexpr uint64_t GB = 1ULL << 30;

关于虚拟内存限制,见this answer

【讨论】:

  • @Dov 我添加了一个链接到一个解释虚拟地址空间如何受到限制的答案。基本上,RAM 大小、交换大小和ulimit 都在这里发挥作用。我尝试了一个具有 1.5 TB RAM 的系统,我能够分配 1 TB,但不能分配 2 TB。 ulimit 设置为无限制。
  • @DanielLangr Point 是可以的,因为ULL 将始终至少为 64 位。毕竟,您的答案就是将数字拟合到正确的位数中。
  • 必须同意 Paul 的观点,即 ULL 是正确的选择:eel.is/c++draft/basic.fundamental#tab:basic.fundamental.width(尽管“UL 通常只是 unsigned int”是一个可疑的声明)。
  • @DanielLangr,鉴于我想访问更少的物理内存,这似乎是一个荒谬的限制。 mmap 不是这样工作的吗?我正在寻找像 mmap 这样的电话,但不是文件。当然,我可以使用 mmap 对比交换空间更大的文件进行内存映射吗?
猜你喜欢
  • 2023-03-06
  • 2015-09-16
  • 1970-01-01
  • 1970-01-01
  • 2019-02-19
  • 1970-01-01
  • 2011-03-08
  • 2023-03-28
  • 1970-01-01
相关资源
最近更新 更多