mmap(), mmap64()

Мапировать область памяти в адресное пространство процесса

Прототип:

#include <sys/mman.h>
void * mmap( void *addr,
size_t len,
int prot,
int flags,
int fildes,
off_t off );
void * mmap64( void *addr,
size_t len,
int prot,
int flags,
int fildes,
off64_t off );

Аргументы:

addr
NULL или указатель на виртуальный адрес, по которому должна быть смапирована память в вызывающем процессе.
len
0 или количество байт данных, подлежащих мапированию.
prot
Возможности доступа, которые необходимо использовать для мапируемой области памяти. Может быть использована комбинация следующих битов, определённых в <sys/mman.h>:
PROT_EXEC
область помечается в качестве исполняемой.
PROT_NOCACHE
отключение кэширования области памяти (может использоваться, например, для доступа к двухпортовой памяти).
PROT_NONE
доступ к области памяти не может быть осуществлен.
PROT_READ
область помечается в качестве доступной для чтения.
PROT_WRITE
область помечается в качестве доступной для записи.
flags
Флаги, определяющие дополнительную информацию для обработки области памяти. POSIX определяет следующие флаги:
MAP_PRIVATE
MAP_SHARED
MAP_FIXED
Следующие флаги являются расширениями Unix или ЗОСРВ «Нейтрино»:
MAP_ANON
MAP_BELOW
MAP_BELOW16M
MAP_BELOW4G
Флаг MAP_BELOW4G добавлен в ЗОСРВ «Нейтрино» редакции 2018
MAP_ELF
MAP_FILE
определен для совместимости, не имеет эффекта.
MAP_LAZY
MAP_NOINIT
MAP_RENAME
определен для совместимости, не имеет эффекта.
MAP_NORESERVE
определен для совместимости, не имеет эффекта.
MAP_NOSYNCFILE
MAP_NOX64K
MAP_PHYS
MAP_STACK
Более подробная информация дана ниже по тексту.
fildes
Файловый дескриптор, указывающий на файл, объект разделяемой памяти или объект типизированной памяти. Если мапируется анонимная или физическая память, аргумент должен принимать значение NOFD.
off
Смещение в файле или объекте памяти на начало мапируемой области памяти или физический адрес (например, при мапировании регистров устройства при разработке менеджера ресурсов).


Note: Если требуется мапирование физической памяти устройства, используйте функцию mmap_device_memory() вместо mmap().

Для архитекруры x86 если регистры устройства доступны в виде ресурсов памяти может использоваться функция mmap_device_memory(). В противном случае должна применяться функция mmap_device_io() для предоставления доступа к устройству, а также функции in8() и out8() для считывания и изменения значений регистров. В других архитектурах обе функции эквивалентны.


Библиотека:

libc

Описание:

Функция mmap() производит мапирование в адресное пространство вызывающего процесса области памяти, ассоциированной с объектом filedes, начиная со смещения off и длиной len. Функция возвращает виртуальный адрес смапированной памяти. В качестве используемого объекта может быть выбран:

Если аргумент fildes не соответствует NOFD должен использоваться открытый для чтения файловый дескриптор независимо от того, что указано в аргументе prot; доступ на запись также требуется в случае, если задано разрешение PROT_WRITE без MAP_PRIVATE.

Процедура мапирования проиллюстрирована ниже.

memory_mapping.png
Рисунок 1. Мапирование области памяти с помощью функции mmap()

В большинстве случаев использование аргумента addr не требуется, достаточно передать NULL. В противном случае поведение функции определяется наличием или отсутствием флага MAP_FIXED:

MAP_FIXED установлен
Объект мапируется по адресу addr или функция завершается с ошибкой.
MAP_FIXED не установлен
Значение addr используется для определения адреса мапирования объекта в адресном пространстве вызывающего процесса. Мапируемая область не должна перекрывать уже существующие в адресном пространстве объекты.

Существует две группы флагов, передаваемых через аргумент flags. Первая группа определяет тип памяти (флаги соответствуют маске MAP_TYPE), который поддерживается функцией fork(). Должен быть выбран один из следующих типов:

MAP_PRIVATE
Мапирование эксклюзивно для вызывающего процесса, все изменения не распространяются на исходный объект. Для этого типа мапирования функция mmap() резервирует часть RAM и копирует туда исходный объект.
MAP_SHARED
Мапирование может быть использовано в разных процессах, все изменения проецируются на исходный объект.

В дополнение к основным типам мапирования могут переданы дополнительные флаги (через битовую операцию ИЛИ):

MAP_ANON
Мапирование анонимной памяти, которая не ассоциирована с файловым дескриптором (в качестве fildes должно быть указано значение NOFD). Функция mmap() выделяет память и зануляет ее (если не указан флаг MAP_NOINIT). Это действие эквивалентно открытию /dev/zero. MAP_ANON часто используется в сочетании MAP_PRIVATE, однако при создании разделяемой памяти с дочерними процессами, порождаемыми с помощью fork(), MAP_ANON должен использоваться в сочетании с MAP_SHARED.
MAP_BELOW
Если возможно, мапирование должно осуществляться до виртуального адреса, определнного в аргументе addr.
MAP_BELOW16M
Используется в сочетании с MAP_PHYS | MAP_ANON. Выделенная память будет размещена физически ниже порога в 16 Мб. Флаг важен для использовании DMA устройствами на шине ISA.
MAP_BELOW4G
Используется в сочетании с MAP_PHYS | MAP_ANON. Выделенная память будет размещена физически ниже порога в 4 Гб. Флаг важен для использовании DMA устройствами, которые не поддерживают 64-битную адресацию.
Флаг MAP_BELOW4G доступен лишь на 64-битных платформах.
MAP_ELF
Мапируемая память соответствует ELF объекту. Флаг используется системным загрузчиком программ.
MAP_FILE
Мапирование регулярного файла. Флаг требуется в некоторых операционных системах, но для ЗОСРВ «Нейтрино» он является опциональным.

Note: Вы можете иметь несколько доступных на запись мапирований файла. Если осуществляется доступ к файлу через файловый дескриптор одновременно с мапированием его содержимого в адресное пространство, необходимо отдельно озаботиться вопросом обеспечения синхронизации доступа. Например, имеется возможность указать O_SYNC при использовании open().

MAP_FIXED
объект мапируется по адресу, заданному параметром addr. Если эта область уже смапирована, вызов заменяет существующее мапирование для этой области.

Note: Используйте флаг MAP_FIXED с осторожностью. Не все модели памяти поддерживают его. В общем случае следует исходить из того, что MAP_FIXED можно использовать только с адресами (и размерами), возвращаемыми mmap() без MAP_FIXED.

Область памяти, мапируемая с помощью MAP_FIXED, сначала размапируется системой, используя ту же область памяти. Подробности см. в munmap().
MAP_LAZY
Осуществляет задержку выделения системной памяти, а также копирования и зануления MAP_PRIVATE | MAP_ANON страниц, до тех пор, пока к ним не будет осуществлет первый доступ по записи. Если флаг установлен, то в момент первого доступа по записи к невыделенной памяти поток получит сигнал SIGBUS с кодом BUS_ADRERR, что является триггером для менеджера памяти.

Для объектов анонимной разделяемой памяти (создаваемых mmap() с MAP_ANON | MAP_SHARED и файловым дескриптором -1) флаг MAP_LAZY приводит к нефвной установке SHMCTL_LAZY для объекта (подробнее см. shm_ctl()).
MAP_NOINIT
При указании данного флага требование POSIX по занулению памяти игнорируется. Физическая память, используемая для подобного рода мапирований, должна быть предварительно освобождена с UNMAP_INIT_OPTIONAL, в противном случае данный флаг не будет иметь силы.
MAP_RENAME
определен для совместимости, не имеет эффекта.
MAP_NORESERVE
определен для совместимости, не имеет эффекта.
MAP_NOSYNCFILE
Запрещает модификацию исходного файла.

Note: Если одна и таже область памяти, принадлежащая файлу, смапирована дважды (с флагом MAP_NOSYNCFILE и без), менеджер памяти может некорректно определять сделаны ли изменения в файле с использованием MAP_NOSYNCFILE или без, что может привести к изменению файла даже в том случае, если это не предполагалось.

MAP_NOX64K
(Применимо только на платформе x86). Используется в сочетании с MAP_PHYS | MAP_ANON. Предотвращает пересечение границы в 64 Кб при выделении памяти. Это может быть важно для некоторых DMA устройств. Если выделяется более 64 Кб, область памяти будет выровнена по границе 64 Кб.
MAP_PHYS
Запрашивается физическая память. Аргумент fildes должен быть равен NOFD. Если флаг используется без MAP_ANON аргумент off определяет точный физический адрес ресурса (например, адрес буфера кадров видео памяти), что эквивалентно открытию /dev/mem.

При использовании в сочетании MAP_ANON функция mmap() выделяет физически непрерывную память, при этом аргумент off игнорируется. Могут использоваться дополнительные флаги, такие как MAP_NOX64K и MAP_BELOW16M, для включения специальных характеристик мапируемых областей памяти.

Note: Используйте mmap_device_memory() вместо MAP_PHYS если вы не выделяете физически непрерывную память.

MAP_STACK
Флаг используется в сочетании с MAP_ANON для определения роли аллоцируемой памяти. Флаг носит информационный характер.

Следующие флаги таже определены в <sys/mman.h>, но использование их не рекомендовано:

MAP_PRIVATEANON
Флаг устарел; используйте MAP_PRIVATE | MAP_ANON вместо него.
MAP_SYSRAM
Константа используется в качестве выходного статус флага для операций DCMD_PROC_MAPINFO и DCMD_PROC_PAGEDATA devctl().

Note: Установка константы MAP_SYSRAM в качестве флага в функции mmap() приводит к ошибке EINVAL.

Если аргумент fildes представляет объект типизированной памяти, открытый с использованием POSIX_TYPED_MEM_ALLOCATE или POSIX_TYPED_MEM_ALLOCATE_CONTIG флагов (см posix_typed_mem_open()), и при этом доступных ресурсов достаточно, функция mmap() производит мапирование len байт данных объекта, которые не были ранее аллоцированы любым процессом в системе, которым разрешен доступ к типизированной памяти. Если типизированная память не содержит требуемое количество ресурсов, вызов mmap() завершается с ошибкой.

Если аргумент fildes представляет объект типизированной памяти, открытый с использованием POSIX_TYPED_MEM_ALLOCATE_CONTIG флага, мапируемая память является непрерывной в пределах заданного объекта. Если объект типизированной памяти был открыт с использованием POSIX_TYPED_MEM_ALLOCATE, непрерывность не гарантируется. Если ни один из этих флаго не был использован, len байт данных, начиная со смещения off внутри объекта типизированной памяти, мапируется по аналогии с файлами или объектами разделяемой памяти. В этом случае, если два процесса мапируют типизированную память с одним и тем же смещением и длиной в рамках одного объекта, то они будут адресоваться к одной и той же области памяти.

Возвращаемое значение:

Виртуальный адрес смапированного объекта или MAP_FAILED в случае ошибки (при этом устанавливается errno).

Коды ошибок:

EACCES
Файловый дескриптор fildes не был открыт для чтения, либо были указаны PROT_WRITE и MAP_SHARED для файлового дескриптора fildes не доступного для записи.
EAGAIN
Мапирование не может заблокировано в памяти из-за нехватки ресурсов, если это определено в mlockall().
EBADF
Файловый дескриптор fildes недоступен.
EBUSY
Ресурс, который Вы пытаетесь смапировать недоступен.
EINVAL
Некорректно задана маска flags, len равно 0 или для флага MAP_FIXED задан некорректный addr.
EMFILE
Число мапируемых областей памяти достигло максимума; см. RLIMIT_AS и RLIMIT_DATA параметры для setrlimit().
ENODEV
Аргумент fildes указывает на объект, для которого выполнение mmap() бессмысленно (например, на терминал).
ENOMEM
Выполнено одно из следующих условий:
ENXIO
Выполнено одно из следующих условий:
EOVERFLOW
При доступе к регулярному файлу смещение и длина мапируемой области памяти выходят за его границы.

Примеры:

Открытие объекта разделяемой памяти и разделение доступа к нему между процессами:

fd = shm_open( "/datapoints", O_RDWR, 0777 );
addr = mmap( 0, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );

Выделение физически непрерывного DMA буфера:

addr = mmap( 0, 262144, PROT_READ | PROT_WRITE | PROT_NOCACHE, MAP_PHYS | MAP_ANON, NOFD, 0 );

Мапирование файла в память, его модификация и последующая верификация изменений:

#include <stdlib.h>
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#define TESTSTRING "AAAAAAAAAA"
int main( int argc, char *argv[] )
{
char buffer[80],
filename[200] = "/tmp/try_it";
int fd, file_size, ret, size_written, size_read;
void *addr;
/* Write the test string into the file. */
unlink( filename );
fd = open( filename, O_CREAT | O_RDWR, 0777 );
if ( fd == -1 )
{
perror( "open" );
exit( EXIT_FAILURE );
}
size_written = write( fd, TESTSTRING, sizeof( TESTSTRING ) );
if ( size_written == -1 )
{
perror( "write" );
exit( 0 );
}
printf( "Wrote %d bytes into file %s\n", size_written, filename );
lseek( fd, 0L, SEEK_SET );
file_size = lseek( fd, 0L, SEEK_END );
printf( "Size of file = %d bytes\n", file_size );
/* Map the file into memory. */
addr = mmap( 0, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
if ( addr == MAP_FAILED )
{
perror( "mmap" );
exit( EXIT_FAILURE );
}
/* Change the memory and synchronize it with the disk. */
memset( addr, 'B', 5 );
ret = msync( addr, file_size, MS_SYNC );
if ( ret == -1 )
{
perror( "msync" );
exit( 0 );
}
/* Close and reopen the file and then read its contents. */
close( fd );
fd = open( filename, O_RDONLY );
if ( fd == -1 )
{
perror( "open" );
exit( EXIT_FAILURE );
}
size_read = read( fd, buffer, sizeof( buffer ) );
printf( "File content = %s\n", buffer );
close( fd );
return (EXIT_SUCCESS);
}

Классификация:

mmap() — POSIX 1003.1 Memory Mapped Files, Shared Memory Objects, Typed Memory Objects; mmap64() — Поддержка больших файлов

Безопасность использования
Точка остановки потока
Нет
Обработчик прерываний
Нет
Обработчик сигналов
Да
В потоке
Да

Тематические ссылки:

mmap_device_io(), mmap_device_memory(), munmap(), msync(), posix_typed_mem_open(), setrlimit(), shm_open()

Разделяемая память




Предыдущий раздел: Описание API системной библиотеки