可以在Linux中分配大量的虚拟内存吗?

时间:2019-10-23 13:28:06

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

出于某些目的,分配大量的虚拟空间会很有效,并且仅在被访问的页面中分配页面。分配大量内存是瞬时的,实际上并没有获取页面:

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

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

我希望new调用malloc来调用sbrk,并且当我访问起始位置以外的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);
}

是否有任何方法可以分配大量的虚拟内存并稀疏访问页面,或者这不可行?

2 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:3)

  

是否可以在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从而终止该进程)。由于该值很长,因此在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可能会受益。

相关问题