Unix - статьи


         

Разделяемая память


Спецификация SVID описывает интерфейс для работы с разделяемыми блоками памяти. Разделяемые блоки памяти представляют собой область памяти, отображенную адресное пространство нескольких процессов. Если один процесс запишет данные в разделяемую область, другой процесс может считывать их оттуда как из собственной области глобальной памяти. Мы продемонстрируем использование разделяемой памяти на уже знакомом примере клиент-сервер. Как и в случае с сообщениями, нам нужно описать общие структуры данных для клиента и сервера в заголовочном файле (на диске это файл shmemtypes.h):

#define FTOK_FILE "./shmemserv" #define MAXLEN 512 struct memory_block { int server_lock; int client_lock; int turn; int readlast; char string[MAXLEN]; };

Механизм разделяемой памяти не налагает никаких ограничений на структуру блока памяти. Структура memory_block определенна нами, исходя исключительно из наших собственных потребностей. Первые четыре поля структуры memory_block – служебные, они нужны для реализации модифицированного алгоритма Петерсона, о котором будет сказано ниже. Последнее поле предназначено собственно для передачи данных. В нашем заголовочном файле мы не определяем ключ для идентификации разделяемого блока, но указываем имя некоего файла (в нашем случае – исполнимого файла сервера). Это имя будет передано функции ftok() для получения ключа. Естественно, это метод сработает только если сервер будет скомпилирован под именем shmemserv. Рассмотрим исходный код инициализации сервера:

key_t key; int shmid; struct memory_block * mblock; key = ftok(FTOK_FILE, 1); shmid = shmget(key, sizeof(struct memory_block), 0666 | IPC_CREAT); mblock = (struct memory_block *) shmat(shmid, 0, 0);

Как уже отмечалось, ftok() генерирует ключ, используя в качестве «затравки» имя файла, в нашем случае - имя исполнимого файла сервера. Использование имени самой программы для генерации ключа до некоторой степени гарантирует уникальность ключа. Функции для работы с разделяемой памятью объявлены в файлах sys/ipc.h sys/shm.h. Разделяемый блок памяти выделяется при помощи функции shmget(2), которой передаются три параметра. В первом параметре передается ключ, идентифицирующий выделяемый блок памяти. Второй параметр позволяет указать размер блока в байтах. В третьем параметре передается маска прав доступа и флаги, аналогичные флагам msgget(). Функция shmget() возвращает идентификатор выделенного блока памяти (его не следует путать с указателем на блок). Для того, чтобы получить указатель на созданный блок разделяемой памяти, этот блок нужно отобразить в локальное адресное пространство процесса. Отображение блока разделяемой памяти в адресное пространство процесса выполняет функция shmat(2). У этой функции тоже три параметра. Первый параметр, это идентификатор, возвращенный функцией shmget(). Во втором параметре передается желательный начальный адрес для отображения разделяемого блока в локальном адресном пространстве. Функция shmat() «постарается» отобразить разделяемый блок в локальное пространство, начиная с указанного адреса, но успешный результат не гарантирован. Если во втором параметре shmat() передать нулевое значение, функция сама выберет начальный адрес области отображения. Значение желательного адреса должно быть выравнено по границе страничных областей. Можно также не выравнивать адрес, но передать в третьем параметре функции флаг SHM_RND, и тогда функция сама скорректирует значение адреса. Среди дополнительных флагов, которые можно передать в третьем параметре, отметим флаг SHM_RDONLY, который присваивает отображаемой области статус «только для чтения». При успешном выполнении функция shmat() возвращает указатель на начало области отображения, с которым мы можем работать как с обычным указателем на выделенный блок памяти. Для того чтобы понять дальнейшую работу сервера, следует иметь в виду, что сами по себе объекты разделяемой памяти не предоставляют никаких средств синхронизации доступа, так что нам приходится самим позаботиться об этих средствах. Для синхронизации работы клиента и сервера и разграничения доступа мы используем упомянутый уже алгоритм Петерсона [], который позволяет разграничить доступ к блоку разделяемой памяти, используя неатомарные операции.



Содержание  Назад  Вперед