Уильям Стивенс - UNIX: взаимодействие процессов
В этом разделе мы работали с отображением файлов в память с помощью mmap. В упражнении 13.1 нам придется немного изменить две наших программы для работы с разделяемой памятью Posix, и мы получим те же результаты.
12.7. Резюме
Разделяемая память представляет собой самую быстродействующую форму IPC, потому что данные из разделяемой области памяти доступны всем потокам и процессам, с ней работающим. Обычно для координации совместных действий потоков и процессов, использующих разделяемую память, требуется некоторая форма синхронизации.
В этой главе мы подробно рассмотрели свойства функции mmap и отображение обычных файлов в память, потому что это один из способов обеспечения взаимодействия между неродственными процессами. После отображения файла в память для обращения к нему больше не нужно использовать вызовы read, write и lseek — вместо этого можно напрямую работать с ячейками памяти, относящимися к той области, указатель на которую возвращает mmap. Замена явных операций с файлом на обращение к ячейкам памяти может упростить программу и в некоторых случаях увеличить быстродействие.
Если необходимо совместное использование области памяти после вызова fork, можно упростить решение этой задачи, используя неименованное отображение в память. Для этого в ядрах Berkeley при вызове mmap указывается флаг MAP_ANON, а в ядрах SVR4 производится отображение специального файла /dev/zero.
Причина, по которой мы столь детально разобрали работу mmap, заключается в том, что отображение файлов в память часто оказывается очень полезным, а также в том, что mmap используется для работы с разделяемой памятью Posix, которая является предметом изучения следующей главы.
Для работы с памятью стандартом Posix определено еще четыре функции:
■ mlockall делает всю память процесса резидентной; munlockall снимает эту блокировку;
■ mlock делает определенный диапазон адресов процесса резидентным. Аргументами функции являются начальный адрес и длина области. Функция munlock разблокирует указываемую область памяти.
Упражнения
1. Что произойдет с программой в листинге 12.7, если добавить еще один повтор цикла for?
2. Предположим, что имеются два процесса, один из которых отправляет сообщения другому. Для этого используются очереди сообщений System V. Нарисуйте схему передачи сообщений от отправителя к получателю. Теперь представьте, что используется реализация очередей сообщений Posix из раздела 5.8, и нарисуйте аналогичную схему.
3. Мы говорили, что при вызове mmap с флагом MAP_SHARED для синхронизации содержимого файла и памяти используются алгоритмы ядра для работы с виртуальной памятью. Прочитайте страницу документации, относящуюся к /dev/zero, чтобы узнать, что происходит, когда ядро записывает изменения обратно в этот файл.
4. Измените программу в листинге 12.2, указав MAP_PRIVATE вместо MAP_SHARED. Проверьте, что результаты будут такими же, как и при выполнении программы из листинга 12.1. Что будет содержаться в файле, отображенном в память?
5. В разделе 6.9 мы отметили, что единственным способом использовать select с очередью сообщений System V является создание неименованной области памяти, порождение процесса и блокирование его в вызове msgrcv, причем сообщение должно считываться в разделяемую память. Родительский процесс также создает два канала, один из которых используется для уведомления его о том, что сообщение помещено в разделяемую память, а другой — для уведомления дочернего процесса о возможности помещения нового сообщения в эту память. Тогда родительский процесс может вызвать select для открытого на чтение конца канала вместе с любыми другими дескрипторами. Напишите программу, реализующую этот алгоритм. Для выделения области неименованной разделяемой памяти используйте функцию my_shm (листинг А.31). Для создания очереди сообщений и помещения в нее записей используйте программы msgcreate и msgsnd из раздела 6.6. Родительский процесс должен просто выводить размер и тип всех считываемых дочерним процессом сообщений.
ГЛАВА 13
Разделяемая память Posix
13.1. Введение
В предыдущей главе рассматривались общие вопросы, связанные с разделяемой памятью, и детально разбиралась функция mmap. Были приведены примеры, в которых вызов mmap использовался для создания области памяти, совместно используемой родительским и дочерним процессами. В этих примерах использовалось:
■ отображение файлов в память (листинг 12.2);
■ неименованное отображение памяти в системе 4.4BSD (листинг 12.4);
■ неименованное отображение файла /dev/zero (листинг 12.5).
Теперь мы можем расширить понятие разделяемой памяти, включив в него память, совместно используемую неродственными процессами. Стандарт Posix.1 предоставляет два механизма совместного использования областей памяти для неродственных процессов:
1. Отображение файлов в память: файл открывается вызовом open, а его дескриптор используется при вызове mmap для отображения содержимого файла в адресное пространство процесса. Этот метод был описан в главе 12, и его использование было проиллюстрировано на примере родственных процессов. Однако он позволяет реализовать совместное использование памяти и для неродственных процессов.
2. Объекты разделяемой памяти: функция shm_open открывает объект IPC с именем стандарта Posix (например, полным именем объекта файловой системы), возвращая дескриптор, который может быть использован для отображения в адресное пространство процесса вызовом mmap. Данный метод будет описан в этой главе.
Оба метода требуют вызова mmap. Отличие состоит в методе получения дескриптора, являющегося аргументом mmap: в первом случае он возвращается функцией open, а во втором — shm_open. Мы показываем это на рис. 13.1. Стандарт Posix называет объектами памяти (memory objects) и отображенные в память файлы, и объекты разделяемой памяти стандарта Posix.
13.2. Функции shm_open и shm_unlink
Процесс получения доступа к объекту разделяемой памяти Posix выполняется в два этапа:
1. Вызов shm_open с именем IPC в качестве аргумента позволяет либо создать новый объект разделяемой памяти, либо открыть существующий.
Рис. 13.1. Объекты памяти Posix: отображаемые в память файлы и объекты разделяемой памяти
2. Вызов mmap позволяет отобразить разделяемую память в адресное пространство вызвавшего процесса.
Аргумент пате, указанный при первом вызове shm_open, должен впоследствии использоваться всеми прочими процессами, желающими получить доступ к данной области памяти.
ПРИМЕЧАНИЕ
Причина, по которой этот процесс выполняется в два этапа вместо одного, на котором в ответ на имя объекта возвращался бы адрес соответствующей области памяти, заключается в том, что функция mmap уже существовала, когда эта форма разделяемой памяти была включена в стандарт Posix. Разумеется, эти два действия могли бы выполняться и одной функцией. Функция shm_open возвращает дескриптор (вспомните, что mq_open возвращает значение типа mqd_t, a sem_open возвращает указатель на значение типа sem_t), потому что для отображения объекта в адресное пространство процесса функция mmap использует именно дескриптор этого объекта.
#include <sys/mman.h>
int shm_open(const char *name, int oflag, mode_t mode);
/* Возвращает неотрицательный дескриптор в случае успешного завершения, -1 – в случае ошибки */
int shm_unlink(const char *name);
/* Возвращает 0 в случае успешного завершения, -1 – в случае ошибки */
Требования и правила, используемые при формировании аргумента name, были описаны в разделе 2.2.
Аргумент oflag должен содержать флаг O_RDONLY либо O_RDWR и один из следующих: O_CREAT, O_EXCL, O_TRUNC. Флаги O_CREAT и O_EXCL были описаны в разделе 2.3. Если вместе с флагом O_RDWR указан флаг O_TRUNC, существующий объект разделяемой памяти будет укорочен до нулевой длины.
Аргумент mode задает биты разрешений доступа (табл. 2.3) и используется только при указании флага O_CREAT. Обратите внимание, что в отличие от функций mq_open и sem_open для shm_open аргумент mode указывается всегда. Если флаг O_CREAT не указан, значение аргумента mode может быть нулевым.
Возвращаемое значение shm_open представляет собой целочисленный дескриптор, который может использоваться при вызове mmap в качестве пятого аргумента.
Функция shm_unlink удаляет имя объекта разделяемой памяти. Как и другие подобные функции (удаление файла из файловой системы, удаление очереди сообщений и именованного семафора Posix), она не выполняет никаких действий до тех пор, пока объект не будет закрыт всеми открывшими его процессами. Однако после вызова shm_unlink последующие вызовы open, mq_open и sem_open выполняться не будут.