Стивенс Уильям Ричард
Шрифт:
Для обеспечения переносимости пpoгрaмм флаг MAP_FIXED указывать не следует. Если он не указан, но аргумент addr представляет собой ненулевой указатель, интерпретация этого аргумента зависит от реализации. Ненулевое значение addr обычно трактуется как указатель на желаемую область памяти, в которую нужно произвести отображение. В переносимой программе значение addr должно быть нулевым и флаг MAP_FIXED не должен быть указан.
Одним из способов добиться совместного использования памяти родительским и дочерним процессами является вызов mmap с флагом MAP_SHARED перед вызовом fork. Стандарт Posix.1 гарантирует в этом случае, что все отображения памяти, установленные родительским процессом, будут унаследованы дочерним. Более того, изменения в содержимом объекта, вносимые родительским процессом, будут видны дочернему, и наоборот. Эту схему мы вскоре продемонстрируем в действии.
Для отключения отображения объекта в адресное пространство процесса используется вызов munmap:
Аргумент addr должен содержать адрес, возвращенный mmap, a len — длину области отображения. После вызова munmap любые попытки обратиться к этой области памяти приведут к отправке процессу сигнала SIGSEGV (предполагается, что эта область памяти не будет снова отображена вызовом mmap).
Если область была отображена с флагом MAP_PRIVATE, все внесенные за время работы процесса изменения сбрасываются.
В изображенной на рис. 12.5 схеме ядро обеспечивает синхронизацию содержимого файла, отображенного в память, с самой памятью при помощи алгоритма работы с виртуальной памятью (если сегмент был отображен с флагом MAP_SHARED). Если мы изменяем содержимое ячейки памяти, в которую отображен файл, через некоторое время содержимое файла будет соответствующим образом изменено ядром. Однако в некоторых случаях нам нужно, чтобы содержимое файла всегда было в соответствии с содержимым памяти. Тогда для осуществления моментальной синхронизации мы вызываем msync:
Аргумент 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, необходимый для разделения памяти между процессами.
ПРИМЕЧАНИЕ
Детали реализации mmap и связь этого вызова с механизмами реализации виртуальной памяти описаны в [14] для 4.4BSD и [6] для SVR4.
12.3. Увеличение счетчика в отображаемом в память файле
Изменим программу в листинге 12.1 (которая не работала) таким образом, чтобы родительский и дочерний процессы совместно использовали область памяти, в которой хранится счетчик. Для этого используем отображение файла в память вызовами open и mmap. В листинге 12.2 приведен текст новой программы.