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

ИМЯ

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

ОБЗОР

#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.

Если значение old_size равно нулю и old_address указывает на общее отображение (смотрите mmap(2) MAP_SHARED), то mremap() создаст новое отображение тех же страниц.Размер нового отображения будет равен значению new_size, а расположение можно указать в new_address; смотрите описание MREMAP_FIXED далее. Если новое отображение запрашивается через этот метод, то также должен быть указан флаг MREMAP_MAYMOVE.

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

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

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

По умолчанию, если для расширения отображения недостаточно пространства в текущем расположении, то вызов mremap() завершается с ошибкой. Если указан флаг, то, если нужно, ядру разрешается переместить отображение на новый виртуальный адрес. Если отображение перемещено, то абсолютные указатели в старом расположении отображения становятся недействительными (должно быть выполнено смещение относительно начального адреса отображения).
Этот флаг играет ту же роль, что и 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 устанавливается в соответствующее значение.

ОШИБКИ

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

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

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

ЗАМЕЧАНИЯ

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

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

ДЕФЕКТЫ

До Linux 4.14, если old_size равно нулю и отображение, на которое указывает old_address — частное отображение (mmap(2) MAP_PRIVATE), то вызов mremap() создавал новое частное отображение, не относящееся к первоначальному отображению. Такое поведение не предусматривалось и, вероятно, не ожидается в приложениях пользовательского пространства (так предназначение mremap() — создание нового отображения на основе первоначального). Начиная с Linux 4.14, в этом случае mremap() завершается ошибкой EINVAL.

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

brk(2), getpagesize(2), getrlimit(2), mlock(2), mmap(2), sbrk(2), malloc(3), realloc(3)

Описание страничной памяти в вашей любимой книге по операционным системам (например, Современные операционные системы Эндрю С. Таненбаума, Внутри Linux Рэндольфа Бентсона, Архитектура операционной системы UNIX Мориса Дж. Баха)

2019-03-06 Linux