Конкретный адрес или страница malloc (укажите минимальное смещение) в linux

В LuaJIT на Linux вся оперативная память виртуальной машины должна быть ниже границы памяти процесса 2 ГБ, потому что внутренние указатели всегда 32-битные. поэтому я хочу сам управлять большими аллоками (используя FFI и malloc и т. д.), например. для больших текстур, аудио, буферов и т. д.

Теперь я хочу убедиться, что они сопоставлены выше границы 2 ГБ, потому что тогда они не отнимают память, управляемую виртуальной машиной.

есть ли способ malloc или, может быть, mmap (без сопоставленного файла или, может быть, в SHM), чтобы выделить указатель конкретно над этим адресом? даже не нужно занимать 2 гигабайта, просто сопоставьте мой указатель с более высоким (= не 32-битным) адресом


person nonchip    schedule 16.09.2015    source источник


Ответы (2)


Выделите блок размером 2 ГБ. Если он расположен ниже предела, выделите еще 2 ГБ (этот должен быть больше 2 ГБ, поскольку только один блок такого размера может уместиться ниже 2 ГБ).

/* Allocates size bytes at an address higher than address */
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) {
        /* The memory starts at a low address. 
         * mysize is so big that another block this size cannot fit 
         * at a low address. So let's allocate another block and 
         * then free the block that is using low memory. */
        x = malloc(mysize);
        free(y);
    }
    return x;
}

Примечание.

Если size меньше, чем address, может быть достаточно места по младшему адресу во втором malloc. Вот почему я увеличиваю выделенный размер в этих случаях. Поэтому не используйте это для выделения небольших фрагментов памяти. Выделите большой кусок, а затем разделите его на более мелкие части вручную.

person Klas Lindbäck    schedule 16.09.2015
comment
Вы должны выделить достаточно большой блок, чтобы он был вынужден находиться в верхнем ОЗУ, а затем использовать там свой собственный распределитель. - person Antti Haapala; 16.09.2015
comment
Не могли бы вы объяснить, как именно это будет работать? Собираетесь ли вы каждый раз выделять куски по 2 ГБ? Я думаю, что malloc очень скоро начнет возвращать NULL... Что мне здесь не хватает? - person Alex Lop.; 16.09.2015
comment
Это не так. Для теста я выделил 64k блоков по 2 ГБ. (Возможно, даже лучше все еще использовать mmap для 2 ГБ, хотя это будет не так переносимо.) - person Antti Haapala; 16.09.2015
comment
@АлексЛоп. Я предполагаю, что пользователь выделит большой блок, а затем использует свой собственный распределитель для получения меньших частей из этого большого блока. - person Klas Lindbäck; 16.09.2015
comment
@KlasLindbäck О, понятно. Спасибо. Теперь это имеет больше смысла. - person Alex Lop.; 16.09.2015
comment
@KlasLindbäck это то, что я все равно хотел сделать (на самом деле, используя realloc для увеличения/уменьшения блока верхней памяти по мере необходимости, а затем просто сохраняя в нем структуру. Поэтому я запускаю ваш код, например, (1GB,2GB) и где-то получаю (как минимум) блок 1G выше 2G? (при расчете правильных указателей для этих размеров, конечно) - person nonchip; 16.09.2015
comment
@nonchip Если вы сделаете realloc и получите новый адрес, нет гарантии, что новый адрес будет выше отметки 2 ГБ. - person Klas Lindbäck; 17.09.2015
comment
@KlasLindbäck, так что я должен просто вручную перераспределить большие куски, используя ваш код, вместо reallocing? - person nonchip; 17.09.2015
comment
Копирование 2+ ГБ памяти обходится дорого, поэтому не стоит делать это без необходимости. Я нутром чувствую, что вероятность перераспределения данных для перемещения данных на низкий адрес довольно низка. Поэтому я бы создал функцию HighRealloc, которая сначала пробует обычный realloc, и только если результирующий указатель слишком мал, вызывает HighMalloc и копирует данные вручную. - person Klas Lindbäck; 17.09.2015

Единственное, что я знаю (так что, возможно, это не лучший выбор), это использовать mmap для Linux. Были некоторые ситуации, когда мне приходилось выделять огромные куски памяти, выровненные по определенным значениям, поэтому я использовал его (потому что здесь вы можете указать адрес и длину куска памяти), но это требует реализации некоторого модуля управления памятью, так как теперь вы собирается управлять распределениями (и выпусками).

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:

КАРТА_АНОНИМНАЯ

Отображение не поддерживается никаким файлом; его содержимое инициализируется нулем. Аргументы fd и offset игнорируются; однако в некоторых реализациях требуется, чтобы fd был равен -1, если указано MAP_ANONYMOUS (или MAP_ANON), и переносимые приложения должны это обеспечивать. Использование MAP_ANONYMOUS в сочетании с MAP_SHARED поддерживается в Linux только начиная с ядра 2.4.

Если addr равно NULL, тогда система выберет для вас доступный адрес, но, поскольку вы хотите выделить его выше 2G, вам нужно будет управлять списком выделенных страниц, чтобы узнать, какие адреса используются выше 2G. Также обратите внимание, что если вы укажете, что addr=X, а mmap не смогут использовать этот адрес, это не приведет к сбою, он просто выберет другой адрес, который можно использовать без какой-либо индикации сбоя (за исключением того факта, что возвращаемый указатель не будет быть равным addr). Однако вы можете использовать флаг MAP_FIXED для принудительного применения указанного вами адреса, и если mmap не сможет его использовать, произойдет сбой (возврат MAP_FAILED).

КАРТА_ИСПРАВЛЕНА

Не воспринимайте адрес как подсказку: поместите сопоставление именно по этому адресу. адрес должен быть кратен размеру страницы. Если область памяти, указанная с помощью addr и len, перекрывает страницы любого существующего сопоставления(ий), то перекрывающаяся часть существующего сопоставления(й) будет отброшена. Если указанный адрес не может быть использован, mmap() завершится ошибкой. Поскольку требование фиксированного адреса для сопоставления менее переносимо, использование этого параметра не рекомендуется.

ИЗМЕНИТЬ

ВНИМАНИЕ, что использование MAP_FIXED не рекомендуется, поскольку, как сказано в описании

Если область памяти, указанная с помощью addr и len, перекрывает страницы любых существующих сопоставлений, то перекрывающаяся часть существующих сопоставлений будет отброшена.

и вы даже не узнаете об этом. Безопаснее проверить, что addr равно возвращенному адресу mmap.

person Alex Lop.    schedule 16.09.2015
comment
Не используйте MAP_FIXED. Он с радостью удалит все, что существовало в области памяти раньше, о чем вы никогда не знали. - person Antti Haapala; 16.09.2015
comment
@AnttiHaapala Полностью с вами согласен. Я добавлю это к своему ответу. - person Alex Lop.; 16.09.2015
comment
Вы всегда можете попытаться mmap увеличить объем памяти, а затем отключить оставшуюся часть, если вам нужно меньше; если ядро ​​​​откажется давать сопоставления такого размера, будет возвращено значение NULL. - person Antti Haapala; 16.09.2015