Отображаемая память

Отображаемая память позволяет различным процессам общаться через общедоступный файл. Отображаемая память может использоваться для взаимодействия процессов или как простой способ для обращения к содержимому файла. Отображаемая память формирует ассоциацию между файлом и памятью процесса. Linux разбивает файл на куски размером страницы и затем копирует их в страницы виртуальной памяти так, чтобы они могли быть представлены в адресном пространстве процесса. Таким образом, процесс может читать содержание файла обычным доступом к памяти. Он может также изменить содержимое файла, записывая в память. Что позволяет быстро взаимодействовать с файлом.

Существуют иные методы использования отображаемой памяти. Некоторые из них описаны в разделе "Другие методы использования для mmap."

Отображение обычного файла

Чтобы отобразить обычный файл в память процесса используйте вызов mmap (Memory MAPped , произносимое "em-map"). Первый параметр - адрес, начиная с которого Вы хотели бы, чтобы Linux отобразил файл; значение NULL заставляет Linux выбирать первый доступный адрес. Второй параметр - длина отображения в байтах. Третий параметр определяет защиту для отображаемого адресного интервала. Защита состоит из поразрядного "или" из PROT_READ, PROT_WRITE , и PROT_EXEC, что соответствует чтению, записи, и разрешению на выполнение. Четвертый параметр - значение флажка, определяющее дополнительные опции. Пятый параметр - дескриптор открытого файла, который будет отображен. Последний параметр - смещение от начала файла, с которого начнется отображение. Вы можете отобразить весь или часть файла в память, выбирая начальное смещение и длину.

Значение флажка - поразрядное "или" этих ограничений:

Успешный вызов возвращает указатель на начало памяти. При ошибке, возвращает MAP_FAILED . Когда вы закончили работу с управлением памятью, освободите ее при помощи munmap. Передайте адрес начала и длину области отображаемой памяти. Linux автоматически освобождает отображаемую память при завершении процесса.

Пример программы

Давайте посмотрим на две программы демонстрирующие использование области отображаемой памяти, с целью чтения и записи в файлам. Первая программа, листинг 5.5, генерирует случайное число и пишет его в файл. Вторая программа, листинг 5.6, читает число, печатает его, и удваивает его в файле. Обе программы считывают имена отображаемых файлов из командной строки.

Listing 5.5 (mmap-write.c) Записывает случайное число в отображаемую память

        #include <stdlib.h>
        #include <stdio.h>
        #include <fcntl.h>
        #include <sys/mman.h>
        #include <sys/stat.h>
        #include <time.h>
        #include <unistd.h>
	#define FILE_LENGTH 0x100
	/* Возвратить случайное число из диапазона [low, high]. */
	int random_range (unsigned const low, unsigned const high)
	{
		unsigned const range = high - low + 1;
		return low + (int) (((double) range) * rand () / (RAND_MAX + 1.0));
	}
	int main (int argc, char* const argv[])
	{
		int fd;
		void* file_memory;
		/* Инициализируем генератор случайных чисел. */          
		srand (time (NULL));

		/* Открываем(создаем) файл, достаточно  большой, чтобы хранить целое число без знака. */          
		fd = open (argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
		lseek (fd, FILE_LENGTH+1, SEEK_SET);
		write (fd, "", 1);
		lseek (fd, 0, SEEK_SET);

		/* Создаем отображение в памяти. */          
		file_memory = mmap (0, FILE_LENGTH, PROT_WRITE, MAP_SHARED, fd, 0);
		close (fd);

		/* Пишем случайное целое число в отображенную память. */     
		sprintf((char*) file_memory, "%d\n", random_range (-100, 100));

		/* Освобождаем память. */ 
		munmap (file_memory, FILE_LENGTH);

		return 0;
	}

Программа mmap-write открывает файл, создавая его, если он прежде не существовал. Третий параметр указывает режим доступа для чтения и записи. Поскольку мы не знаем длину файла, мы используем lseek , чтобы гарантировать, что файл является достаточно большим, чтобы сохранить целое число . После чего возвращаемся к началу файла.

Программа отображает файл и затем закрывает дескриптор файла, потому что он больше не нужен. Программа пишет случайное целое число в отображаемую память, таким образоми в файл, и освобождает отображаемую память. Вызов munmap ненужен, потому что Linux автоматически освободил бы отображаемую память и файл, когда программа завершится.

Listing 5.6 (mmap-read.c) Читает число из файла отображенного в памяти и удваивает число.

	#include <stdlib.h>
	#include <stdio.h>
	#include <fcntl.h>
	#include <sys/mman.h>
	#include <sys/stat.h>
	#include <unistd.h>
	#define FILE_LENGTH 0x100
	int main (int argc, char* const argv[])
	{
		int fd;
		void* file_memory;
		int integer;
		/* Открыть файл. */
		fd = open (argv[1], O_RDWR, S_IRUSR | S_IWUSR);
		/* Отобразить файл в память.  */
		file_memory = mmap (0, FILE_LENGTH, PROT_READ | PROT_WRITE,
				MAP_SHARED, fd, 0);
		close (fd);
		/* Чтение целого  числа, распечатка и умножение на 2.  */
		scanf (file_memory, "%d", &nteger);
		printf ("значение: %d\n", integer);
		sprintf ((char*) file_memory, "%d\n", 2 * integer);
		/* Освобождение памяти. */ 
		munmap (file_memory, FILE_LENGTH);
		return 0;
	}

Программа mmap-read читает число из файла и затем записывает его удвоенное значение в файлу. Открывает файл и отображает для того, чтобы читать и писать в него. Поскольку мы можем предположить, что файл является достаточно большим, чтобы сохранить целое число без знака, мы не должны использовать lseek , как в предыдущей программе. Программа читает и анализирует значение используя scanf и затем форматирует и записывает удвоенное значение, используя sprintf . Вот пример запуска примеров программ. Здесь отображаем файл /tmp/integer-file.

          % ./mmap-write /tmp/integer-file
          % cat /tmp/integer-file
          42
          % ./mmap-read /tmp/integer-file
          value: 42
          % cat /tmp/integer-file
          84

Заметьте, что текст 42 был записан в файл на диске без вызова write , и читался без вызова read . Отметьте, что эти программы пишут и читают целое число как строку только в целях демонстрации - нет никакой необходимости содержанию отображаемого файла быть текстом. Вы можете сохранить и восстановить произвольный набор двоичных данных.

Совместный доступ к файлу

Различные процессы могут взаимоедйствовать используя области отображаемой памятью, связанные с одним и тем же файлом. Укажите флажок MAP_SHARED для того, чтобы любые операции записи в область памяти немедленно передаются файлу и видимым другим процессам. Если Вы не определяете этот флажок, Linux может буферизовать операции записи перед передачей их к файлу.

С другой стороны вы можете заставить Linux синхронизировать буфер с файлом на диске, вызвав msync . Его первые два параметра определяют область отображенной памяти, как и для munmap . Третий параметр может содержать следующие значения флажка:

Например, чтобы сбросить на диск буфер общедоступного файл, отображенный в адресе mem_addr длины mem_length байт:

          msync (mem_addr, mem_length, MS_SYNC | MS_INVALIDATE);

Как и с совместно используемой памятью, пользователи областей отображенной памяти должны следовать протоколу, чтобы избежать условий гонки. Например, семафор может использоваться, чтобы препятствовать доступуболее одного процесса к отображенной памяти.

Частные отображения

Указывая флаг MAP_PRIVATE при вызове mmap создается область копирования при записи. Любая операция записи отражается только в памяти этого процесса; другие процессы, которые отображают тот же самый файл, не будут видеть изменения. Вместо того, чтобы писать непосредственно странице, разделенной всеми процессами, процесс пишет частной копии этой страницы. Все последующие операции читения и записи процессом используют эту же страницу.

Другие применения mmap

Вызов mmap может использоваться и в целях не связанных с взаимодействием процессов. Одно из применений - замена операций для чтения и записи. Например, вместо того, чтобы явно читать содержимое файла в память, программа могла бы отобразить файл в память и просмотреть эго используя чтение памяти. Для некоторых программ, это более удобно и может работать быстрее, чем явные операции ввода - вывода.

Одна мощная методика, используемая некоторыми программами - формирование структуры данных в файле отображенной памяти. При следующем запуске, программа отображает тот файл назад в память, и структуры данных восстановлены в их предыдущеее состояние. Обратите внимание, что указатели в этих структурах данных будут недопустимы, если они все не указывают в пределах той отображенной области памяти или если файл отображен по тем же адресам, которые занимал первоначально.