MALLOPT(3) Руководство программиста Linux MALLOPT(3)

ИМЯ

mallopt - задаёт параметры выделения памяти

ОБЗОР

#include <malloc.h>

int mallopt(int param, int value);

ОПИСАНИЕ

Функция mallopt() подстраивает параметры, которые управляют поведением функций выделения памяти (смотрите malloc(3)). В аргументе param указывается изменяемый параметр, а в value — новое значение этого параметра.

В param можно указать следующие значения:

Если этот параметр не равен нулю, то он определяет жёсткое ограничение на максимальное количество площадок (arenas), которое можно создать. Площадка представляет собой общий набор буферов (pool) памяти, который может использовать вызов malloc(3) (и подобные) для обслуживания запросов выделения. Площадки безопасны в нитях и поэтому могут использоваться при одновременных запросах выделения. Нужно соблюдать соотношение между количеством нитей и количеством площадок. Чем больше площадок, тем меньше состязаний между нитями, но большее потребление памяти.
Значение по умолчанию данного параметра равно 0, то есть ограничение на количество площадок будет определяться по M_ARENA_TEST.
Этот параметр доступен начиная с glibc 2.10 при указании --enable-experimental-malloc, и начиная с glibc 2.15 — по умолчанию. В некоторых версиях механизм выделения не имел ограничения на количество создаваемых площадок (например, в CentOS 5, RHEL 5).
При использовании более новых версий glibc приложения могут, в некоторых случаях, встретить высокую состязательность при доступе к площадкам. В этих случаях может быть выгодно увеличить M_ARENA_MAX для соответствия количеству нитей. Такое поведение подобно стратегиям, используемым tcmalloc и jemalloc (например, общие наборы буферов выделения для каждой нити).
Этим параметром определяется значение на количество создаваемых площадок; в соответствии с ним будут исследоваться системные настройки для оценки жёсткого ограничения на количество создаваемых площадок (определение площадки смотрите в описании M_ARENA_MAX).
Вычисленное жёсткое ограничение на площадки определяется реализацией и, обычно, кратно количеству доступных ЦП. После вычисления жёсткого ограничения, результат окончательный и ограничивает общее количество площадок.
Значение по умолчанию для параметра M_ARENA_TEST равно 2 в системах, где sizeof(long) равно 4; в противном случае значение по умолчанию равно 8.
Этот параметр доступен начиная с glibc 2.10 при указании --enable-experimental-malloc, и начиная с glibc 2.15 — по умолчанию.
Значение M_ARENA_TEST не используется, если M_ARENA_MAX не равно нулю.
Значение данного параметра управляет тем, как glibc действует при обнаружении различных программных ошибок (например, освобождение одного указателя дважды). Поведение glibc задаётся тремя битами (2, 1 и 0) указанного значения:
Бит 0
Если этот бит установлен, то печатается сообщение в stderr с подробностями ошибки. Сообщение начинается со строки «*** glibc detected ***», далее идёт имя программы, имя функции выделения памяти, в которой возникла ошибка, краткое описание ошибки и адрес памяти, где обнаружена ошибка.
Бит 1
Если этот бит установлен, то после печати сообщения об ошибке, если указано битом 0, программа завершается вызовом abort(3). В glibc с версии 2.4, если также установлен бит 0, то между печатью сообщения об ошибке и завершением программа также печатает трассировку стека подобную backtrace(3), и печатает отображение памяти процесса в стиле /proc/[pid]/maps (смотрите proc(5)).
Бит 2 (начиная с glibc 2.4)
Этот бит учитывается только если установлен бит 0. Если данный бит установлен, то выдаваемое сообщение об ошибке упрощается до имени функции, где обнаружена ошибка и короткого описания ошибки.
Оставшиеся биты в value игнорируются.
Объединяя выше описанное, получаются следующие числовые значения, влияющие на M_CHECK_ACTION:
0
Игнорировать условия ошибки; продолжить выполнение (с неопределенными результатами).
1
Вывести подробное сообщение об ошибке и продолжить выполнение.
2
Прервать программу.
3
Вывести подробное сообщение об ошибке, трассировку стека и отображения памяти, а затем прервать программу.
5
Вывести простое сообщение об ошибке и продолжить выполнение.
7
Вывести простое сообщение об ошибке, трассировку стека и отображения памяти, и прервать программу.
Начиная с glibc 2.3.4, значение параметра M_CHECK_ACTION по умолчанию равно 3. В glibc версии 2.3.3 и старее значением по умолчанию было 1.
Использование ненулевого значения M_CHECK_ACTION может быть полезно, так как в противном случае падение может случиться позднее и будет сложно отследить реальную причину проблемы.
Данным параметром задаётся максимальное количество запросов выделения, которые может одновременно выполнить mmap(2). Этот параметр существует, так как в некоторых системах ограничено количество внутренних таблиц, используемых mmap(2), и использование больше определённого количество снижает производительность.
По умолчанию значение равно 65536; оно не имеет какого-то специального обоснования и служит только как предохранитель. Присвоение этому параметру 0 отключает использование mmap(2) для обслуживания запросов больших выделений.
Для выделений, которые больше или равны M_MMAP_THRESHOLD (в байтах) и не могут быть получены из списка свободных, то функции выделения памяти вместо mmap(2) используют sbrk(2) для увеличения пространства данных программы.
Выделяемая с помощью mmap(2) память имеет значительное преимущество в том, что выделенные блоки памяти можно всегда независимо освободить и вернуть системе (по сравнению с кучей, которую можно обрезать только, если память свободна в конце). С другой стороны, есть несколько отрицательных моментов при использовании mmap(2): освобождённое пространство не возвращается в список свободного для повторного выделения позже; память может траться впустую, так как выделения mmap(2) должны быть кратны размеру страницы; ядро должно выполнять ресурсоёмкую задачу по обнулению памяти, выделяемой через mmap(2). Баланс этих факторов учтён в значении по умолчанию для параметра M_MMAP_THRESHOLD — 128*1024.
Нижний предел данного параметра равен 0. Верхний предел DEFAULT_MMAP_THRESHOLD_MAX: 512*1024 в 32-битных системах и 4*1024*1024*sizeof(long) в 64-битных системах.
Замечание: В настоящее время по умолчанию в glibc используется динамический порог mmap. Начальное значение порога — 128*1024, но когда освобождаются блоки больше текущего порога и меньше или равны DEFAULT_MMAP_THRESHOLD_MAX, то порог увеличивается до размера свободного блока. Когда срабатывает динамический порог mmap, порог обрезки кучи также динамически корректируется на двукратное значение динамического порога mmap.Динамическая корректировка порога mmap отключается, если задан параметр M_TRIM_THRESHOLD, M_TOP_PAD, M_MMAP_THRESHOLD или M_MMAP_MAX.
Задаёт верхний порог запросов выделения памяти, которые обрабатываются с помощью «fastbins» (значение параметра измеряется в байтах). Fastbins — это области хранилища, которые содержат освобождённые блоки памяти одного размера и не объединяют смежные свободные блоки. Последующее перераспределение блоков одного размера при выделении из fastbin может обрабатываться очень быстро, хотя из-за этого может увеличиться фрагментация памяти и общее количество памяти программы.
По умолчанию значение параметра равно 64*sizeof(size_t)/4 (т. е., 64 на 32-битных архитектурах). Диапазон значений параметра: 0 - 80*sizeof(size_t)/4. Присваивание M_MXFAST значения 0 отключает использование fastbins.
Если этому параметру присвоено ненулевое значение, то байты выделенной памяти (кроме выделенных через calloc(3)) инициализируются дополнением значения в наименее значимом байте value, и при освобождении памяти с помощью free(3) освобождённым байтам присваивается значение наименее значимого байта value. Это может быть полезно для обнаружения ошибок, когда программа некорректно полагается на то, что выделенная памяти инициализируется нулями или повторно использует значения уже освобождённой памяти.
Значение по умолчанию для этого параметра равно 0.
Данным параметром определяется количество заполнения при вызове sbrk(2) для изменения пространства данных программы (измеряется в байтах). Данный параметр полезен в следующих случаях:
  • Когда пространство данных программы увеличивается, то M_TOP_PAD байт добавляется к запросу sbrk(2).
  • Когда обрезается куча в следствии вызова free(3) (смотрите описание M_TRIM_THRESHOLD), то это количество пространства предохраняется вверху кучи.
В обоих случаях, количество заполнения всегда округляется до границы системной страницы.
Изменение M_TOP_PAD — компромисс между увеличением количества системных вызовов (если значение параметра занижено) и тратой неиспользуемой памяти сверху кучи (если значение параметра завышено).
Значение по умолчанию этого параметра равно 128*1024.
Когда количество непрерывной свободной памяти сверху кучи вырастает до значительных размеров функция free(3) вызывает sbrk(2) для освобождения этой памяти обратно в систему (это может быть полезно в программах, которые работают длительное время после освобождения значительного количества памяти). Параметром M_TRIM_THRESHOLD задаётся минимальный размер (в байтах), которого должен достигнуть блок памяти, чтобы вызвался sbrk(2) для обрезания кучи.
Значение по умолчанию этого параметра равно 128*1024. Установка M_TRIM_THRESHOLD равным -1 отключает обрезку.
Изменение M_TRIM_THRESHOLD — компромисс между увеличением количества системных вызовов (если значение параметра занижено) и тратой неиспользуемой памяти сверху кучи (если значение параметра завышено).

Переменные окружения

Параметры, управляющие mallopt(), можно изменить и через переменные окружения. Использование этих переменных позволяет изменять работу программы без пересборки из исходного кода. В целях эффективной работы эти переменные должны быть определены до первого вызова функции выделения памяти (если этот же параметр изменяется через mallopt(), то вызов mallopt() имеет приоритет). В целях безопасности, эти переменные игнорируются для программ с установленными битами set-user-ID и set-group-ID.

Используются следующие переменные окружения (обратите внимание на подчёркивание в конце некоторых переменных):

Управляет параметром M_ARENA_MAX, аналогично вызову mallopt().
Управляет параметром M_ARENA_TEST, аналогично вызову mallopt().
Эта переменная окружения управляет тем же параметром что и mallopt() M_CHECK_ACTION. Если эта переменная установлена в ненулевое значение, то будет использоваться особенная реализация функций выделения памяти (это достигается использованием функции malloc_hook(3)). Эта реализация выполняет дополнительные проверки ошибок, но она медленнее чем стандартный набор функций выделения памяти (эта реализация не способна обнаружить все возможные ошибки; утечки памяти всё ещё возможны).
Значение, назначенное этой переменной окружения должно быть одиночной цифрой; их значения описаны в M_CHECK_ACTION. Все символы кроме первой цифры игнорируются.
В целях безопасности по умолчанию переменная MALLOC_CHECK_ игнорируется для программ с установленными битами set-user-ID и set-group-ID. Однако, если существует файл /etc/suid-debug (содержимое файла не важно), то MALLOC_CHECK_ учитывается и для программ с установленными битами set-user-ID и set-group-ID.
Управляет параметром M_MMAP_MAX, аналогично вызову mallopt().
Управляет параметром M_MMAP_THRESHOLD, аналогично вызову mallopt().
Управляет параметром M_PERTURB, аналогично вызову mallopt().
Управляет параметром M_TRIM_THRESHOLD, аналогично вызову mallopt().
Управляет параметром M_TOP_PAD, аналогично вызову mallopt().

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

При успешном выполнении mallopt() возвращается 1. При ошибке возвращается 0.

ОШИБКИ

При ошибке значение errno не изменяется.

СООТВЕТСТВИЕ СТАНДАРТАМ

Данная функция не описана в POSIX или C. Подобная функция существует во многих ответвлениях System V, но значения param не совпадают. В SVID определены параметры M_MXFAST, M_NLBLKS, M_GRAIN и M_KEEP, но только первый из них реализован в glibc.

ДЕФЕКТЫ

Неправильное значение param не вызывает ошибки.

Ошибка вычисления внутри реализации glibc означает, что вызов:

mallopt(M_MXFAST, n)

не приведёт к тому, что из fastbins будет выделяться память до размера n. Чтобы это сработало, n должно быть округлено до следующего множителя большего или равного (2k+1)*sizeof(size_t), где k — целое число.

Если mallopt() используется для установки M_PERTURB, то, как и ожидалось, байты свободной памяти инициализируются дополнением байта из value, и когда эта память освобождается, байты области инициализируются байтом, указанным в value. Однако, в реализации есть ошибка выхода за sizeof(size_t): вместо инициализации точного блока памяти освобождаемом вызовом free(p), блок начинает инициализироваться с p+sizeof(size_t).

ПРИМЕР

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

Следующий сеанс в оболочке показывает работу программы с glibc и значением по умолчанию для M_CHECK_ACTION:

$ ./a.out
main(): возвращение из первого вызова free()
*** glibc detected *** ./a.out: double free or corruption (top): 0x09d30008 ***
======= Backtrace: =========
/lib/libc.so.6(+0x6c501)[0x523501]
/lib/libc.so.6(+0x6dd70)[0x524d70]
/lib/libc.so.6(cfree+0x6d)[0x527e5d]
./a.out[0x80485db]
/lib/libc.so.6(__libc_start_main+0xe7)[0x4cdce7]
./a.out[0x8048471]
======= Memory map: ========
001e4000-001fe000 r-xp 00000000 08:06 1083555    /lib/libgcc_s.so.1
001fe000-001ff000 r--p 00019000 08:06 1083555    /lib/libgcc_s.so.1
[some lines omitted]
b7814000-b7817000 rw-p 00000000 00:00 0
bff53000-bff74000 rw-p 00000000 00:00 0          [stack]
Aborted (core dumped)

В этом запуске показаны результаты при других значениях M_CHECK_ACTION:

$ ./a.out 1             # показ ошибки и продолжение
main(): возвращение из первого вызова free()
*** glibc detected *** ./a.out: double free or corruption (top): 0x09cbe008 ***
main(): возвращение из второго вызова free()
$ ./a.out 2             # прерывание без показа ошибки
main(): возвращение из первого вызова free()
Aborted (core dumped)
$ ./a.out 0             # игнорирование ошибки и продолжение
main(): возвращение из первого вызова free()
main(): возвращение из второго вызова free()

При этом запуске показано как изменить тот же параметр с помощью переменной окружения MALLOC_CHECK_:

$ MALLOC_CHECK_=1 ./a.out
main(): возвращение из первого вызова free()
*** glibc detected *** ./a.out: free(): invalid pointer: 0x092c2008 ***
main(): возвращение из второго вызова free()

Исходный код программы

#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{

    char *p;

    if (argc > 1) {

        if (mallopt(M_CHECK_ACTION, atoi(argv[1])) != 1) {

            fprintf(stderr, "mallopt() завершилась с ошибкой");

            exit(EXIT_FAILURE);

        }

    }

    p = malloc(1000);

    if (p == NULL) {

        fprintf(stderr, "malloc() завершилась с ошибкой");

        exit(EXIT_FAILURE);

    }

    free(p);

    printf("main(): возвращение из первого вызова free()\n");

    free(p);

    printf("main(): возвращение из второго вызова free()\n");

    exit(EXIT_SUCCESS);
}

СМОТРИТЕ ТАКЖЕ

mmap(2), sbrk(2), mallinfo(3), malloc(3), malloc_hook(3), malloc_info(3), malloc_stats(3), malloc_trim(3), mcheck(3), mtrace(3), posix_memalign(3)

2019-03-06 Linux