Пользовательские аналоги функций из подсистемы памяти

Пользователи могут заменить функции подсистемы памяти (функции malloc, calloc, realloc, free, mallopt и mallinfo subroutines ) собственными функциями.

Прим.: Пользовательские подсистемы памяти, написанные на языке C++, не поддерживаются, так как в библиотеке C++ libC.a используется подсистема памяти libc.a.

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

32- и 64-разрядные объекты пользовательской подсистемы памяти должны размещаться в архиве вместе с 32-разрядным общим объектом mem32.o и 64-разрядным общим объектом mem64.o.

Пользовательские общие объекты должны экспортировать следующие идентификаторы:
  • __malloc__
  • __free__
  • __realloc__
  • __calloc__
  • __mallinfo__
  • __mallopt__
  • __malloc_init__
  • __malloc_prefork_lock__
  • __malloc_postfork_unlock__
Дополнительно такие объекты могут экспортировать следующий идентификатор:
  • __malloc_start__
  • __posix_memalign__

Если данные символы не выйдут, выполнение программы продолжится.

Ниже приведены определения пользовательских функций:
void *__malloc__(size_t) :
Пользовательский эквивалент функции malloc.
void __free__(void *) :
Пользовательский эквивалент функции free.
void *__realloc__(void *, size_t) :
Пользовательский эквивалент функции realloc.
void *__calloc__(size_t, size_t) :
Пользовательский эквивалент функции calloc.
int __mallopt__(int, int) :
Пользовательский эквивалент функции mallopt.
struct mallinfo __mallinfo__() :
Пользовательский эквивалент функции mallinfo.
void __malloc_start__()
Эта функция вызывается один раз перед вызовом любой другой пользовательской функции malloc.
void __posix_memalign__()
Пользовательский эквивалент функции posix_memalign. Если данный символ не выйдет, программа будет продолжаться, но вызов процедуры posix_memalign может вызвать ошибку.
Ниже перечислены функции, применяемые подсистемой с нитями для управления пользовательской подсистемой памяти в среде с несколькими нитями. Они вызываются только в том случае, если приложение и/или пользовательский модуль связаны с libpthreads.a. Эти функции должны быть определены и экспортированы даже в том случае, если пользовательская подсистема не поддерживает нити и не связана с libpthreads.a. В противном случае объект не будет загружен.
void __malloc_init__(void)
Вызывается процедурой инициализации API нитей. Данная функция применяется для инициализации пользовательской подсистемы памяти с поддержкой нитей. В большинстве случаев данная функция создает и инициализирует некоторые типы блокировок данных. Даже если модуль пользовательской подсистемы памяти связан с libpthreads.a, пользовательская подсистема памяти должна правильно работать еще до того, как будет вызвана функция __malloc_init__().
void __malloc_prefork_lock__(void)
Вызывается API нитей при обращении к функции fork. Данная функция контролирует, что перед вызовом fork() подсистема находится в допустимом состоянии и остается в нем до завершения функции fork(). В большинстве случаев эта функция устанавливает блокировки подсистемы памяти.
void __malloc_postfork_unlock__(void)
Вызывается API нитей при обращении к функции fork. Эта функция разблокирует подсистему памяти для родительского и дочернего процесса после выполнения функции fork. Она отменяет действие функции __malloc_prefork_lock__. В большинстве случаев, эта функция снимает блокировки подсистемы памяти.
Все эти функции должны быть экспортированы из общего модуля. В архиве должны быть предусмотрены отдельные модули для 32-разрядных и 64-разрядных функций. Например:
  • Модуль mem.exp:
    __malloc__
    __free__
    __realloc__
    __calloc__
    __mallopt__
    __mallinfo__
    __malloc_init__
    __malloc_prefork_lock__
    __malloc_postfork_unlock__
    __malloc_start__
  • Модуль mem_functions32.o:

    Содержит все необходимые 32-разрядные функции

  • Модуль mem_functions64.o:

    Содержит все необходимые 64-разрядные функции

Ниже приведены примеры создания общих объектов. Параметр -lpthreads следует указывать только в том случае, если объект применяет функции библиотеки pthread.
  • Создание 32-разрядного общего объекта:
    ld -b32 -m -o mem32.o mem_functions32.o \
    -bE:mem.exp \
    -bM:SRE -lpthreads -lc
  • Создание 64-разрядного общего объекта:
    ld -b64 -m -o mem64.o mem_functions64.o \
    -bE:mem.exp \
    -bM:SRE -lpthreads -lc
  • Создание архива (32-разрядный общий объект должен называться mem32.o, а 64-разрядный - mem64.o):
     ar -X32_64 -r архив mem32.o mem64.o

Применение пользовательской подсистемы памяти

Пользовательскую подсистему памяти можно подключить различными способами:
  • С помощью переменной среды MALLOCTYPE
  • С помощью глобальной переменной _malloc_user_defined_name в пользовательском приложении

Для применения переменной среды MALLOCTYPE нужно задать архив, содержащий пользовательскую подсистему памяти, присвоив переменной MALLOCTYPE значение user:имя-архива. При этом имя-архива должно быть указано в параметре libpath приложения или в переменной среды LIBPATH.

Для применения глобальной переменной _malloc_user_defined_name она должна быть объявлена в программе следующим образом:
char *_malloc_user_defined_name="архив"

архив должен быть указан в переменной libpath программы или в переменной среды LIBPATH.

Прим.:
  1. При выполнении приложения setuid переменная среды LIBPATH игнорируется, поэтому имя архива должно быть определено в параметре libpath приложения.
  2. Имя-архива не должно содержать информацию о пути.
  3. Если архив указан и в переменной среды MALLOCTYPE, и в глобальной переменной _malloc_user_defined_name, то будет применяться архив, указанный в переменной MALLOCTYPE.

Информация о 32-разрядных и 64-разрядных функциях

Если архив не содержит и 32-разрядный, и 64-разрядный общий объект, и пользовательская подсистема памяти была подключена с помощью переменной среды MALLOCTYPE, то при выполнении 64-разрядных процессов в 32-разрядных приложениях или 32-разрядных процессов в 64-разрядных приложениях может возникнуть сбой. При вызове функции exec создается новый процесс, который наследует среду родительского приложения. Это означает, что будет унаследована переменная среды MALLOCTYPE, и новый процесс попытается загрузить пользовательскую подсистему памяти. Если в архиве нет объекта для программы данного типа, то подсистема не будет загружена, и работа процесса будет завершена.

Рекомендации по работе с нитями

Все пользовательские функции должны правильно работать в среде с несколькими нитями. Даже если модуль подключен с помощью libpthreads.a, функция __malloc__() должна правильно работать до того, как будет вызвана функция __malloc_init__() и выполнена инициализация библиотеки pthread. Это требование связано с тем, что при инициализации API нитей функция malloc() вызывается раньше, чем функция __malloc_init__().

Все подключаемые функции управления памятью должны поддерживать как приложения с нитями, так и приложения без нитей. Функция __malloc__() не должна зависеть от функции __malloc_init__() (т. е. при выполнении __malloc__() должно предполагаться, что функция __malloc_init__() еще не выполнена.) После выполнения функции __malloc_init__() ее результаты могут использоваться функцией __malloc__(). Это связано с тем, что при инициализации API нитей функция malloc() вызывается перед обращением к функции __malloc_init__().

Следующие переменные позволяют избежать лишних вызовов функций, связанных с нитями:
  • Значение переменной __multi_threaded равно нулю до тех пор, пока не будет создана первая нить. После создания нити значение становится ненулевым и не обнуляется до конца выполнения процесса.
  • Значение переменной __n_pthreads, равно -1 до тех пор, пока не будут инициализированы API нитей; после инициализации значение становится равным 1. С этого момента данная переменная используется в качестве счетчика активных нитей.

Пример:

Ниже приведен фрагмент кода для случая, когда функция __malloc__() применяет функцию pthread_mutex_lock():

if (__multi_threaded)
pthread_mutex_lock(mutexptr);

/* ..... работа ....... */

if (__multi_threaded)
pthread_mutex_unlock(mutexptr);

В этом примере функция __malloc__() не применяет API нитей до тех пор, пока не будет завершена их инициализация. Производительность однонитевых приложений также повышается, так как блокировка устанавливается только при запуске второй нити.

Ограничения

Подсистемы памяти, написанные на языке C++, не поддерживаются, так как в них применяются библиотека libC.a и подсистема памяти libc.a.

Сообщения об ошибках не переводятся, поскольку при инициализации локалей с помощью setlocale применяется функция malloc(). Если при вызове malloc() произойдет сбой, то функция setlocale не будет выполнена, и приложение по-прежнему будет применять локаль POSIX. Это означает, что будут выводиться только сообщения на английском языке.

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

Сообщения об ошибках

При первом обращении к функции malloc загружается 32- или 64-разрядный объект из архива, заданного в переменной среды MALLOCTYPE. Если во время загрузки возникает ошибка, то выводится соответствующее сообщение, и работа приложения завершается. В противном случае проверяется наличие всех необходимых идентификаторов. Если какие-либо идентификаторы отсутствуют, работа приложения завершается, и выводится сообщение со списком недостающих идентификаторов.