mremap(2) изменяет отображение адреса виртуальной памяти

ОБЗОР

#define _GNU_SOURCE /* см. feature_test_macros(7) */
#include <sys/mman.h>


void *mremap(void *old_address, size_t old_size,
size_t new_size, int flags, ... /* void *new_address */);

ОПИСАНИЕ

Вызов mremap() увеличивает (или уменьшает) размер существующего отображения памяти, при необходимости, перемещая его (это контролируется аргументом flags и доступным виртуальным адресным пространством).

В old_address указывается старый адрес блока виртуальной памяти, который вы хотите изменить. Заметим, что old_address должен быть выровнен по границе страницы. В old_size задаётся старый размер блока виртуальной памяти. В new_size задаётся запрашиваемый размер блока виртуальной памяти после изменения. Описание необязательного пятого аргумента new_address смотрите далее в параграфе о MREMAP_FIXED.

В Linux память делится на страницы. Пользовательскому процессу выделяется один или несколько непрерывных сегментов виртуальной памяти. Каждый из этих сегментов имеет одно или несколько отображений в реальных страницах памяти (в таблице страниц). У каждого виртуального сегмента памяти есть своя защита (права доступа), которые можно нарушить, если произвести попытку некорректного доступа (например, записать информацию в сегмент, который доступен только для чтения). Доступ к виртуальной памяти за пределами сегментов также приводит к ошибке сегментации.

Вызов mremap() использует схему табличных страниц Linux. mremap() изменяет отображение между виртуальными адресами и страницами памяти. Это можно использовать при реализации очень эффективной функции realloc(3).

Аргумент битовой маски flags может быть равен 0 или содержать следующие флаги:

MREMAP_MAYMOVE
По умолчанию, если для расширения отображения недостаточно пространства в текущем расположении, то вызов mremap() завершается с ошибкой. Если указан флаг, то, если нужно, ядру разрешается переместить отображение на новый виртуальный адрес. Если отображение перемещено, то абсолютные указатели в старом расположении отображения становятся недействительными (должно быть выполнено смещение относительно начального адреса отображения).
MREMAP_FIXED (начиная с Linux 2.3.31)
Этот флаг играет ту же роль, что и MAP_FIXED для mmap(2). Если указан этот флаг, то mremap() учитывает пятый аргумент void *new_address, в котором задан выровненный на страницу адрес, куда должно быть перемещено отображение. Все предыдущие отображения в адресном диапазоне, задаваемом new_address и new_size, удаляются. При указании MREMAP_FIXED также должен быть указан MREMAP_MAYMOVE.

Если сегмент памяти, указанный old_address и old_size, заблокирован (с помощью mlock(2) или подобного вызова), то эта блокировка останется при изменении/перемещении сегмента. Следовательно, количество заблокированной процессом памяти может измениться.

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

При успешном выполнении mremap() возвращается указатель на новую виртуальную область памяти. При ошибке, возвращается значение MAP_FAILED (то есть (void *) -1), а errno устанавливается в соответствующее значение.

ОШИБКИ

EAGAIN
Вызывающий пытается расширить заблокированный сегмент памяти, но это невозможно без превышения предела ресурса RLIMIT_MEMLOCK.
EFAULT
«Ошибка сегментации (Segmentation fault).» Один из адресов в диапазоне от old_address до old_address+old_size является некорректным адресом виртуальной памяти для этого процесса. Также вы можете получить EFAULT даже если существующие отображения покрывают всё запрошенное адресное пространство, но имеют различные типы.
EINVAL
Задан неправильный параметр. Возможные случаи: old_address не выровнен по размеру страницы; в flags указано значение, отличное от MREMAP_MAYMOVE или MREMAP_FIXED; значение new_size равно нулю; некорректное значение new_size или new_address; новый адресный диапазон, заданный new_address и new_size, перекрывается со старым адресным диапазоном, задаваемым old_address и old_size; указан флаг MREMAP_FIXED без флага MREMAP_MAYMOVE.
ENOMEM
Область памяти не может быть расширена от текущего виртуального адреса и в flags не указано значение MREMAP_MAYMOVE. Или недостаточно свободной (виртуальной) памяти.

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

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

ЗАМЕЧАНИЯ

До версии 2.4, в glibc не был определён флаг MREMAP_FIXED, а прототип mremap() не позволял указывать аргумент new_address.

Если mremap() используется для перемещения или расширения области, заблокированной mlock(2) или эквивалентом, то вызов mremap() постарается заполнить новую область, но не завершится с ошибкой ENOMEM, если область невозможно заполнить.