Чтение онлайн

ЖАНРЫ

UNIX: взаимодействие процессов

Стивенс Уильям Ричард

Шрифт:

2. Со специальными файлами для обеспечения неименованного отображения памяти (разделы 12.4 и 12.5).

3. С shm_open для создания участка разделяемой неродственными процессами памяти Posix.

#include <sys/mman.h>

void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);

/* Возвращает начальный адрес участка памяти в случае успешного завершения. MAP_FAILED – в случае ошибки */

Аргумент addr может указывать начальный адрес участка памяти процесса,

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

Аргумент len задает длину отображаемого участка в байтах; участок может начинаться не с начала файла (или другого объекта), а с некоторого места, задаваемого аргументом offset. Обычно offset = 0. На рис. 12.5 изображена схема отображения объекта в память.

Рис. 12.5. Пример отображения файла в память

Защита участка памяти с отображенным объектом обеспечивается с помощью аргумента prot и констант, приведенных в табл. 12.1. Обычное значение этого аргумента — PROT_READ | PROT_WRITE, что обеспечивает доступ на чтение и запись.

Таблица 12.1. Аргумент prot для вызова mmap

prot Описание
PROT_READ Данные могут быть считаны
PROT_WRITE Данные могут быть записаны
PROT_EXEC Данные могут быть выполнены
PROT_NONE Доступ к данным закрыт

Таблица 12.2. Аргумент flag для вызова mmap

flag Описание
MAP SHARED Изменения передаются другим процессам
MAP_PRIVATE Изменения не передаются другим процессам и не влияют на отображенный объект
MAP_FIXED Аргумент addr интерпретируется как адрес памяти

Аргумент flags может принимать значения из табл. 12.2. Можно указать только один из флагов — MAP_SHARED или MAP_PRIVATE, прибавив к нему при необходимости MAP_FIXED. Если указан флаг MAP_PRIVATE, все изменения будут производиться только с образом объекта в адресном пространстве процесса; другим процессам они доступны не будут. Если же указан флаг MAP_SHARED, изменения отображаемых данных видны всем процессам, совместно использующим объект.

Для обеспечения переносимости пpoгрaмм флаг MAP_FIXED указывать не следует. Если он не указан, но аргумент addr представляет собой ненулевой указатель, интерпретация этого аргумента зависит от реализации. Ненулевое значение addr обычно трактуется как указатель на желаемую область памяти, в которую нужно произвести отображение. В переносимой программе значение addr должно быть нулевым и флаг MAP_FIXED не должен быть указан.

Одним из способов добиться совместного использования памяти родительским и дочерним процессами является вызов mmap с флагом MAP_SHARED перед вызовом fork. Стандарт Posix.1 гарантирует в этом случае, что все отображения памяти, установленные родительским процессом, будут унаследованы дочерним. Более того, изменения в содержимом объекта, вносимые родительским процессом, будут видны дочернему, и наоборот. Эту схему мы вскоре продемонстрируем в действии.

Для отключения отображения объекта в адресное пространство процесса используется

вызов munmap:

#include <sys/mman.h>

int munmap(void *addr, size_t len);

/* Возвращает 0 в случае успешного завершения, –1 – в случае ошибки */

Аргумент addr должен содержать адрес, возвращенный mmap, a len — длину области отображения. После вызова munmap любые попытки обратиться к этой области памяти приведут к отправке процессу сигнала SIGSEGV (предполагается, что эта область памяти не будет снова отображена вызовом mmap).

Если область была отображена с флагом MAP_PRIVATE, все внесенные за время работы процесса изменения сбрасываются.

В изображенной на рис. 12.5 схеме ядро обеспечивает синхронизацию содержимого файла, отображенного в память, с самой памятью при помощи алгоритма работы с виртуальной памятью (если сегмент был отображен с флагом MAP_SHARED). Если мы изменяем содержимое ячейки памяти, в которую отображен файл, через некоторое время содержимое файла будет соответствующим образом изменено ядром. Однако в некоторых случаях нам нужно, чтобы содержимое файла всегда было в соответствии с содержимым памяти. Тогда для осуществления моментальной синхронизации мы вызываем msync:

#include <sys/mman.h>

int msync(void *addr, size_t len, int flags);

/* Возвращает 0 в случае успешного завершения, –1 – в случае ошибки */

Аргумент flags представляет собой комбинацию констант из табл. 12.3.

Таблица 12.3. Значения аргумента flags для функции msync

Константа Описание
MS_ASYNC Осуществлять асинхронную запись
MS_SYNC Осуществлять синхронную запись
MS_INVALIDATE Сбросить кэш

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

Почему вообще используется отображение в память?

До сих пор мы всегда говорили об отображении в память содержимого файла, который сначала открывается вызовом open, а затем отображается вызовом mmap. Удобство состоит в том, что все операции ввода-вывода осуществляются ядром и скрыты от программиста, а он просто пишет код, считывающий и записывающий данные в некоторую область памяти. Ему не приходится вызывать read, write или lseek. Часто это заметно упрощает код.

ПРИМЕЧАНИЕ

Вспомните нашу реализацию очередей сообщений Posix с использованием mmap, где значения сохранялись в структуре msg_hdr и считывались из нее же (листинги 5.26 и 5.28).

Следует, однако, иметь в виду, что не все файлы могут быть отображены в память. Попытка отобразить дескриптор, указывающий на терминал или сокет, приведет к возвращению ошибки при вызове mmap. К дескрипторам этих типов доступ осуществляется только с помощью read и write (и аналогичных вызовов).

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

Поделиться с друзьями: