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.gif
Рисунок 1. Маппирование области памяти с помощью функции mmap()

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

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

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

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

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

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 MF|SHM|TYM; mmap64() — Поддержка больших файлов

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

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

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




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