Можно ли выделить большой объем виртуальной памяти в Linux? - PullRequest
5 голосов
/ 23 октября 2019

В некоторых целях было бы целесообразно выделять огромное количество виртуального пространства и страницы только на тех страницах, к которым осуществляется доступ. Выделение большого объема памяти происходит мгновенно и фактически не захватывает страницы:

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

Хорошо, вышеприведенное неверно, как указано, поскольку это 32-разрядное число.

Я ожидаю, что новыйвызывает 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 ]

3 голосов
/ 23 октября 2019

Можно ли выделить в Linux большой объем виртуальной памяти?

Возможно. Но вам может потребоваться настроить его так, чтобы он был разрешен:

Ядро Linux поддерживает следующие режимы обработки overcommit

0 - Эвристическая обработка overcommit. Очевидные нарушения адресного пространства отклоняются. Используется для типичной системы. Это обеспечивает серьезное сбои при распределении, позволяя при избыточном коммите сократить использование подкачки. В этом режиме root может выделить немного больше памяти. Это значение по умолчанию.

1 - Всегда перегрузка. Подходит для некоторых научных приложений. Классическим примером является код, использующий разреженные массивы и просто полагающийся на виртуальную память, состоящую почти полностью из нулевых страниц.

2 - Не перегружайте. Общая фиксация адресного пространства для системы не может превышать swap + настраиваемый объем (по умолчанию 50%) физической памяти. В зависимости от количества, которое вы используете, в большинстве случаев это означает, что процесс не будет остановлен при доступе к страницам, но при необходимости получит ошибки при выделении памяти.

Полезно для приложений, которые хотят гарантировать, что их выделение памяти будет доступнов будущем без необходимости инициализации каждой страницы.

Политика избыточной фиксации устанавливается с помощью sysctl `vm.overcommit_memory '.

Итак, если вы хотите выделить больше виртуальной памяти, чему вас есть физическая память, тогда вам нужно:

# in shell
sysctl -w vm.overcommit_memory=1

RLIMIT_AS Максимальный размер виртуальной памяти процесса (адресного пространства) в байтах. Это ограничение влияет на вызовы brk (2), mmap (2) и mremap (2), которые завершаются ошибкой ENOMEM при превышении этого предела. Также автоматическое расширение стека завершится неудачно (и сгенерирует SIGSEGV, который убивает процесс, если через sigaltstack (2) не было сделано доступного альтернативного стека). Поскольку значение является длинным, на компьютерах с длиной 32-битной версии это ограничение не должно превышать 2 ГиБ, или этот ресурс не ограничен.

Итак, вам нужно:

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

Или, если вы не можете дать процессу разрешение на это, то вы можете настроить его постоянно в /etc/security/limits.conf, что повлияет на все процессы (пользователя / группы).


Хорошо, так что, кажется, mmap поддерживает ... но для этого требуется дескриптор файла. ... может быть победой, но не в том случае, если они должны быть подкреплены файлом ... Мне не нравится идея прикрепления к файлу

Вам не нужно использоватьфайл поддержал mmap. Для этого есть MAP_ANONYMOUS.

Я не знал, какой номер вставить в запрос

Затем используйте ноль. Пример:

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

Тем не менее, если вы настроили систему, как описано, то new должен работать так же, как и mmap. Вероятно, он будет использовать malloc, который, вероятно, будет использовать mmap для таких больших выделений, как этот.


Бонус-подсказка: вы можете воспользоваться преимуществом использования HugeTLB Pages .

3 голосов
/ 23 октября 2019

Значение 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;

Что касается ограничения виртуальной памяти, см. этот ответ .

...