Согласно стандарту SUSv3, аргумент offset вызова mmap() должен выравниваться по странице; то же самое касается аргумента addr, если указан флаг MAP_FIXED. Linux соответствует этой спецификации. Но впоследствии было отмечено, что данные требования расходятся с более ранними стандартами, которые не так жестко регулировали поведение указанных аргументов. В результате такой формулировки ряд стандартных реализаций формально (и без особой на то причины) перестали считаться таковыми. Стандарт SUSv4 возвращается к менее строгим требованиям:
• реализация может требовать, чтобы аргумент offset был кратным размеру страницы в системе;
• при указании флага MAP_FIXED реализация может требовать выравнивания аргумента addr по странице;
• если указан флаг MAP_FIXED, а addr не равен нулю, то аргументы addr и offset будут иметь остаток после деления, равный размеру страницы в системе.
Похожая ситуация сложилась с аргументом addr в вызовах mprotect(), msync() и munmap(). Стандарт SUSv3 гласит, что он должен быть выровнен по странице, однако в стандарте SUSv4 это требование смягчено и оставлено на усмотрение реализации.
В листинге 45.1 демонстрируется применение вызова mmap() для создания приватного файлового отображения. Данная программа является упрощенной версией команды cat(1). Она отображает файл (целиком), имя которого задано в виде аргумента командной строки, и затем записывает содержимое отображения в стандартный вывод.
Листинг 45.1. Использование mmap() для создания приватного файлового отображения
mmap/mmcat.c
#include
#include
#include
#include "tlpi_hdr.h"
int
main(int argc, char *argv[])
{
char *addr;
int fd;
struct stat sb;
if (argc!= 2 || strcmp(argv[1], "-help") == 0)
usageErr("%s file\n", argv[0]);
fd = open(argv[1], O_RDONLY);
if (fd == -1)
errExit("open");
/* Получаем размер файла и указываем на его основе размеры отображения
и буфера, которые будут записаны */
if (fstat(fd, &sb) == -1)
errExit("fstat");
addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED)
errExit("mmap");
if (write(STDOUT_FILENO, addr, sb.st_size)!= sb.st_size)
fatal("partial/failed write");
exit(EXIT_SUCCESS);
}
mmap/mmcat.c
Системный вызов munmap() выполняет действие, обратное mmap(), — удаляет отображение из виртуального адресного пространства вызывающего процесса.
#include
int munmap(void *
Возвращает 0 при успешном завершении или –1 при ошибке
Аргумент addr обозначает начальный адрес участка памяти, с которого нужно удалить отображение. Он должен совпадать с началом страницы (в стандарте SUSv3 это обязательное требование, но стандарт SUSv4 всего лишь предусматривает такую
Аргумент length представляет собой неотрицательное целое число, обозначающее размер участка, с которого будет удаляться отображение (в байтах). Диапазон адресов будет округлен до следующего значения, кратного размеру страницы в системе.
Обычно отображение удаляется целиком. Следовательно, нужно указать адрес, возвращенный предыдущим вызовом mmap(), и использовать то же значение length, что и при создании отображения. Например:
addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED)
errExit("mmap");
/* Код для работы с отображенным участком */
if (munmap(addr, length) == -1)
errExit("munmap");
Но можно также выполнить частичное удаление. В этом случае отображение либо урезается, либо делится на две части — в зависимости от места выполнения удаления. Кроме того, существует возможность указать диапазон адресов, охватывающий несколько отображений, в результате чего все они будут удалены.
Если в диапазоне адресов, заданных с помощью аргументов addr и length, не обнаруживается отображение, то вызов munmap() не выполняет никаких действий и просто возвращает 0 (сигнализируя об успехе).
Во время удаления отображения ядро уничтожает все блокировки памяти, удерживаемые процессом в заданном диапазоне адресов (эти блокировки устанавливаются с использованием вызовов mlock() или mlockall(); см. раздел 46.2).
Когда процесс завершается или выполняет вызов exec(), все его отображения удаляются автоматически.
Чтобы записать содержимое отображения обратно в исходный файл, перед удалением следует сделать вызов msync() (см. раздел 45.5).
Для отображения файла нужно выполнить следующие шаги.
1. Получить дескриптор файла (обычно с помощью вызова open()).
2. Передать этот файловый дескриптор вызову mmap() в виде аргумента fd.
В результате mmap() отобразит содержимое открытого файла на адресное пространство вызывающего процесса. После завершения вызова mmap() можно закрыть файловый дескриптор, не затрагивая отображения. Но в некоторых случаях его лучше оставить открытым (примеры приводятся в листинге 45.1 и в главе 50).