mmap(2) | System Calls Manual | mmap(2) |
mmap, munmap - отображает файлы или устройства в памяти, или удаляет их отображение
Standard C library (libc, -lc)
#include <sys/mman.h>
void *mmap(void addr[.length], size_t length, int prot, int flags, int fd, off_t offset); int munmap(void addr[.length], size_t length);
Информацию по требованиям макроса тестирования свойств смотрите в разделе ЗАМЕЧАНИЯ.
Вызов mmap() создаёт новое отображение в виртуальном адресном пространстве вызывающего процесса. Адрес начала нового отображения указывается в addr. В аргументе length задаётся длина отображения (должна быть больше 0).
Если значение addr равно NULL, то ядро само выбирает адрес (выровненный по странице), по которому создаётся отображение; это наиболее переносимый метод создания нового отображения. Если значение addr не равно NULL, то ядро учитывает это при размещении отображения; в Linux ядро выберет ближайшую к границе страницу (но всегда выше или равною значению, заданному в /proc/sys/vm/mmap_min_addr) и попытается создать отображение. Если по этому адресу уже есть отображение, то ядро выберет новый адрес, который может и не зависеть от подсказки. Адрес нового отображения возвращается как результат вызова.
Содержимое файлового отображения (в отличие от анонимного отображения; смотрите MAP_ANONYMOUS далее) инициализируется данными из файла (или объекта), на который указывает файловый дескриптор fd, длиной length байт, начиная со смещения offset. Значение offset должно быть кратно размеру (возвращается sysconf(_SC_PAGE_SIZE)) страницы.
После возврата из вызова mmap() файловый дескриптор fd может быть немедленно закрыт без признания отображения недействительным.
В аргументе prot указывается желаемая защита памяти отображения (не должна конфликтовать с режимом открытого файла). Значением может быть PROT_NONE или побитово сложенные (OR) следующие флаги:
В аргументе flags задаётся будут ли изменения отображения видимы другим процессам, отображающим ту же область, и будут ли изменения перенесены в отображённый файл. Данное поведение определяется в flags одним из следующих значений:
Флаги MAP_SHARED и MAP_PRIVATE описаны в POSIX.1-2001 и POSIX.1-2008. Флаг MAP_SHARED_VALIDATE является расширением Linux.
Кроме этого в flags могут быть указаны (побитовым сложением):
#define MAP_HUGE_2MB (21 << MAP_HUGE_SHIFT) #define MAP_HUGE_1GB (30 << MAP_HUGE_SHIFT)
Из флагов, перечисленных выше, в POSIX.1-2001 и POSIX.1-2008 определён только MAP_FIXED. Однако, большинство систем также поддерживают MAP_ANONYMOUS (или его синоним MAP_ANON).
Системный вызов munmap() удаляет отображение для указанного адресного диапазона и это приводит к тому, что дальнейшее обращение по адресам внутри диапазона приводит к генерации неправильных ссылок на память. Также для диапазона отображение автоматически удаляется при завершении работы процесса. С другой стороны, закрытие файлового дескриптора не приводит к удалению отображения диапазона.
Адрес addr должен быть кратен размеру страницы (но значения length это не касается). Все страницы, содержащие часть указанного диапазона, удаляются из отображения и последующие ссылки на эти страницы приводят к генерации сигнала SIGSEGV. Это не ошибка, если указанный диапазон не содержит каких-либо отображённых страниц.
On success, mmap() returns a pointer to the mapped area. On error, the value MAP_FAILED (that is, (void *) -1) is returned, and errno is set to indicate the error.
On success, munmap() returns 0. On failure, it returns -1, and errno is set to indicate the error (probably to EINVAL).
При использовании отображаемой области памяти могут возникать следующие сигналы:
Описание терминов данного раздела смотрите в attributes(7).
Интерфейс | Атрибут | Значение |
mmap(), munmap() | Безвредность в нитях | MT-Safe |
POSIX.1-2001, POSIX.1-2008, SVr4, 4.4BSD.
В системах POSIX, в которых есть вызовы mmap(), msync(2) и munmap(), значение _POSIX_MAPPED_FILES, определённое в <unistd.h>, больше 0 (смотрите также sysconf(3)).
Память, отображённая с помощью mmap(), сохраняется при fork(2) с теми же атрибутами.
A file is mapped in multiples of the page size. For a file that is not a multiple of the page size, the remaining bytes in the partial page at the end of the mapping are zeroed when mapped, and modifications to that region are not written out to the file. The effect of changing the size of the underlying file of a mapping on the pages that correspond to added or removed regions of the file is unspecified.
На некоторых архитектурах (например, i386), флаг PROT_WRITE подразумевает флаг PROT_READ. Также от архитектуры зависит подразумевает ли PROT_READ флаг PROT_EXEC или нет. Переносимые программы должны всегда устанавливать PROT_EXEC, если они собираются выполнять код, находящийся в отображении.
Переносимый способ создания отображения: указать в addr значение 0 (NULL) и убрать MAP_FIXED из flags. В этом случае, система сама выберет адрес для отображения; адрес, выбранный таким образом, не будет будет конфликтовать с существующими отображениями и не будет равен 0. Если указан флаг MAP_FIXED и значение addr равно 0 (NULL), то адрес отображения будет равен 0 (NULL).
Некоторые константы flags определены только, если определён подходящий макрос тестирования свойств (возможно, по умолчанию): _DEFAULT_SOURCE в glibc 2.19 и новее; _BSD_SOURCE или _SVID_SOURCE в glibc 2.19 и старее (также достаточно использовать _GNU_SOURCE и требовать, этот макрос логично, так как данные флаги есть только в Linux). Соответственно, флаги: MAP_32BIT, MAP_ANONYMOUS (и синоним MAP_ANON), MAP_DENYWRITE, MAP_EXECUTABLE, MAP_FILE, MAP_GROWSDOWN, MAP_HUGETLB, MAP_LOCKED, MAP_NONBLOCK, MAP_NORESERVE, MAP_POPULATE и MAP_STACK.
Приложение может определить какие страницы отображены в данный момент в буфере/страничном кэше с помощью mincore(2).
Единственным вариантом безопасного использования MAP_FIXED является предварительное резервирование адресного пространства, указываемого в addr и length, другим отображением; в остальных случаях использование MAP_FIXED опасно, так как оно выполняет принудительное удаление существующих отображений, что позволяет легко повредить собственное адресное пространство многонитевого процесса.
Предположим, например, что нить A просматривает /proc/<pid>/mapsв поиске неиспользуемого адресного диапазона, который она сможет отобразить используя MAP_FIXED, но одновременно с этим нить B захватывает часть или весь этот же адресный диапазон. Когда после этого нить A запустит mmap(MAP_FIXED), это, фактически, разобьёт отображение, созданное нитью B. В этом сценарии нити B не нужно создавать отображение явным образом; будет достаточно просто сделать библиотечный вызов, например, dlopen(3) для загрузки какой-то другой общей библиотеки. Вызов dlopen(3) отобразит библиотеку в адресное пространство процесса. Более того, почти каждый библиотечный вызов можно реализовать так, чтобы он добавлял отображения памяти в адресное пространство с помощью этого метода или просто выделяя память. Например, такими вызовами являются brk(2), malloc(3), pthread_create(3) и библиотеки PAM http://www.linux-pam.org.
Начиная с Linux 4.17, в многонитевых программах можно использовать флаг MAP_FIXED_NOREPLACE и, тем самым, избежать опасности, описанной выше, когда выполняется попытка создать отображение по фиксированному адресу, который не был зарезервирован существующим отображением.
У отображённых файлов поле st_atime может измениться в любой момент между вызовом mmap() и соответствующим удалением отображения; первое обращение к отображённой странице приведёт к обновлению поля, если это ещё не было сделано.
Поля st_ctime и st_mtime у отображённого с помощью флагов PROT_WRITE и MAP_SHARED файла будут обновлены после записи отображённой области и перед последующим вызовом msync(2) с флагом MS_SYNC или MS_ASYNC, если он будет вызван.
Для отображений, работающих с огромными страницами, требования к аргументам mmap() и munmap() несколько отличаются от требований к отображениям, в которых используются страницы с системным размером.
Для mmap(), offset должно быть кратно размеру нижележащей огромной страницы. Система автоматически выравнивает length до кратного значения размера нижележащей огромной страницы.
Для munmap(), addr и length должны быть кратны размеру нижележащей огромной страницы.
This page describes the interface provided by the glibc mmap() wrapper function. Originally, this function invoked a system call of the same name. Since Linux 2.4, that system call has been superseded by mmap2(2), and nowadays the glibc mmap() wrapper function invokes mmap2(2) with a suitably adjusted value for offset.
В Linux не гарантируется результат флага MAP_NORESERVE, описанный выше. По умолчанию, любой процесс может быть принудительно завершён в любой момент, если в системе закончилась память.
Before Linux 2.6.7, the MAP_POPULATE flag has effect only if prot is specified as PROT_NONE.
SUSv3 specifies that mmap() should fail if length is 0. However, before Linux 2.6.12, mmap() succeeded in this case: no mapping was created and the call returned addr. Since Linux 2.6.12, mmap() fails with the error EINVAL for this case.
В POSIX сказано, что система всегда должна заполнять нулями любую частичную страницу у конца объекта и что система никогда не должна вносить любые изменения вне пределов объекта. В Linux, если вы пишите данные в такую частичную страницу за концом объекта, то данные остаются в страничном кэше даже после закрытия и выключения отображения файла и хотя данные никогда не пишутся в сам файл, последующие отображения могут увидеть изменённое содержимое. В некоторых случаях это можно исправить вызвав msync(2) перед выключением отображения; однако это не работает на tmpfs(5) (например, когда используется интерфейс общей памяти POSIX, описанный в shm_overview(7)).
Следующая программа выводит часть файла, указанного в первом аргументе командной строки, в стандартный вывод. Диапазон выдаваемых байт задаётся смещением и длиной во втором и третьем аргументах командной строки. Программа создаёт отображение требуемых страниц файла и затем использует write(2) для вывода запрошенных байт.
#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <sys/stat.h> #include <unistd.h> #define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0) int main(int argc, char *argv[]) {
int fd;
char *addr;
off_t offset, pa_offset;
size_t length;
ssize_t s;
struct stat sb;
if (argc < 3 || argc > 4) {
fprintf(stderr, "%s файл смещение [длина]\n", argv[0]);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_RDONLY);
if (fd == -1)
handle_error("open");
if (fstat(fd, &sb) == -1) /* получение размера файла */
handle_error("fstat");
offset = atoi(argv[2]);
pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1);
/* offset for mmap() must be page aligned */
if (offset >= sb.st_size) {
fprintf(stderr, "указанное смещение находится за концом файла\n");
exit(EXIT_FAILURE);
}
if (argc == 4) {
length = atoi(argv[3]);
if (offset + length > sb.st_size)
length = sb.st_size - offset;
/* Can't display bytes past end of file */
} else { /* Не указана длина ==> показать всё до конца файла */
length = sb.st_size - offset;
}
addr = mmap(NULL, length + offset - pa_offset, PROT_READ,
MAP_PRIVATE, fd, pa_offset);
if (addr == MAP_FAILED)
handle_error("mmap");
s = write(STDOUT_FILENO, addr + offset - pa_offset, length);
if (s != length) {
if (s == -1)
handle_error("write");
fprintf(stderr, "частичная запись");
exit(EXIT_FAILURE);
}
munmap(addr, length + offset - pa_offset);
close(fd);
exit(EXIT_SUCCESS); }
ftruncate(2), getpagesize(2), memfd_create(2), mincore(2), mlock(2), mmap2(2), mprotect(2), mremap(2), msync(2), remap_file_pages(2), setrlimit(2), shmat(2), userfaultfd(2), shm_open(3), shm_overview(7)
Описание в proc(5) следующих файлов: /proc/[pid]/maps, /proc/[pid]/map_files и /proc/[pid]/smaps.
B.O. Gallmeister, POSIX.4, O'Reilly, pp. 128–129 and 389–391.
Русский перевод этой страницы руководства был сделан aereiae <aereiae@gmail.com>, Alexey <a.chepugov@gmail.com>, Azamat Hackimov <azamat.hackimov@gmail.com>, Dmitriy S. Seregin <dseregin@59.ru>, Dmitry Bolkhovskikh <d20052005@yandex.ru>, ITriskTI <ITriskTI@gmail.com>, Max Is <ismax799@gmail.com>, Yuri Kozlov <yuray@komyakino.ru>, Иван Павлов <pavia00@gmail.com> и Малянов Евгений Викторович <maljanow@outlook.com>
Этот перевод является бесплатной документацией; прочитайте Стандартную общественную лицензию GNU версии 3 или более позднюю, чтобы узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ.
Если вы обнаружите ошибки в переводе этой страницы руководства, пожалуйста, отправьте электронное письмо на man-pages-ru-talks@lists.sourceforge.net.
5 февраля 2023 г. | Linux man-pages 6.03 |