Разделяемая память в UNIX. Системные вызовы shmget(), shmat(), shmdt()
С точки зрения операционной системы, наименее семантически нагруженным средством System V IPC является разделяемая память (shared memory). Мы уже упоминали об этой категории средств связи на лекции. Для текущего семинара нам достаточно знать, что операционная система может позволить нескольким процессам совместно использовать некоторую область адресного пространства. Внутренние механизмы, позволяющие реализовать такое использование, будут подробно рассмотрены на лекции, посвященной сегментной, страничной и сегментно-страничной организации памяти.
Все средства связи System V IPC требуют предварительных инициализирующих действий (создания) для организации взаимодействия процессов.
Для создания области разделяемой памяти с определенным ключом или доступа по ключу к уже существующей области применяется системный вызов shmget() . Существует два варианта его использования для создания новой области разделяемой памяти.
· Стандартный способ. В качестве значения ключа системному вызову поставляется значение, сформированное функцией ftok() для некоторого имени файла и номера экземпляра области разделяемой памяти. В качестве флагов поставляется комбинация прав доступа к создаваемому сегменту и флага IPC_CREAT . Если сегмент для данного ключа еще не существует, то система будет пытаться создать его с указанными правами доступа. Если же вдруг он уже существовал, то мы просто получим его дескриптор. Возможно добавление к этой комбинации флагов флага IPC_EXCL . Этот флаг гарантирует нормальное завершение системного вызова только в том случае, если сегмент действительно был создан (т. е. ранее он не существовал), если же сегмент существовал, то системный вызов завершится с ошибкой, и значение системной переменной errno, описанной в файлеerrno.h, будет установлено в EEXIST.
· Нестандартный способ. В качестве значения ключа указывается специальное значение IPC_PRIVATE . Использование значения IPC_PRIVATE всегда приводит к попытке создания нового сегмента разделяемой памяти с заданными правами доступа и с ключом, который не совпадает со значением ключа ни одного из уже существующих сегментов и который не может быть получен с помощью функции ftok() ни при одной комбинации ее параметров. Наличие флагов IPC_CREAT и IPC_EXCL в этом случае игнорируется.
Системный вызов shmget() Прототип системного вызова #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> int shmget(key_t key, int size, int shmflg); Описание системного вызова Системный вызов shmget предназначен для выполнения операции доступа к сегменту разделяемой памяти и, в случае его успешного завершения, возвращает дескрипторSystem V IPC для этого сегмента (целое неотрицательное число, однозначно характеризующее сегмент внутри вычислительной системы и использующееся в дальнейшем для других операций с ним). Параметр key является ключом System V IPC для сегмента, т. е. фактически его именем из пространства имен System V IPC. В качестве значения этого параметра может использоваться значение ключа, полученное с помощью функции ftok() , или специальное значение IPC_PRIVATE . Использование значения IPC_PRIVATE всегда приводит к попытке создания нового сегмента разделяемой памяти с ключом,который не совпадает со значением ключа ни одного из уже существующих сегментов и который не может быть получен с помощью функции ftok() ни при одной комбинации ее параметров. Параметр size. определяет размер создаваемого или уже существующего сегмента в байтах. Если сегмент с указанным ключом уже существует, но его размер не совпадает с указанным в параметре size, констатируется возникновение ошибки. Параметр shmflg – флаги – играет роль только при создании нового сегмента разделяемой памяти и определяет права различных пользователей при доступе к сегменту, а также необходимость создания нового сегмента и поведение системного вызова при попытке создания. Он является некоторой комбинацией (с помощью операции побитовое или – " | ") следующих предопределенных значений и восьмеричных прав доступа: · IPC_CREAT – если сегмента для указанного ключа не существует, он должен быть создан; · IPC_EXCL – применяется совместно с флагом IPC_CREAT . При совместном их использовании и существовании сегмента с указанным ключом, доступ к сегменту не производится и констатируется ошибочная ситуация, при этом переменная errno, описанная в файле <errno.h>, примет значение EEXIST ; · 0400 – разрешено чтение для пользователя, создавшего сегмент; · 0200 – разрешена запись для пользователя, создавшего сегмент; · 0040 – разрешено чтение для группы пользователя, создавшего сегмент; · 0020 – разрешена запись для группы пользователя, создавшего сегмент; · 0004 – разрешено чтение для всех остальных пользователей; · 0002 – разрешена запись для всех остальных пользователей. Возвращаемое значение Системный вызов возвращает значение дескриптора System V IPC для сегмента разделяемой памяти при нормальном завершении и значение -1 при возникновении ошибки. |
Доступ к созданной области разделяемой памяти в дальнейшем обеспечивается ее дескриптором, который вернет системный вызов shmget() . Доступ к уже существующей области также может осуществляться двумя способами:
· Если мы знаем ее ключ, то, используя вызов shmget() ,можем получить ее дескриптор. В этом случае нельзя указывать в качестве составной части флагов флаг IPC_EXCL , а значение ключа, естественно, не может бытьIPC_PRIVATE . Права доступа игнорируются, а размер области должен совпадать с размером, указанным при ее создании.
· Либо мы можем воспользоваться тем, что дескриптор System V IPC действителен в рамках всей операционной системы, и передать его значение от процесса, создавшего разделяемую память, текущему процессу. Отметим, что при создании разделяемой памяти с помощью значения IPC_PRIVATE – это единственно возможный способ.
После получения дескриптора необходимо включить область разделяемой памяти в адресное пространство текущего процесса. Это осуществляется с помощью системного вызова shmat() . При нормальном завершении он вернет адрес разделяемой памяти в адресном пространстве текущего процесса. Дальнейший доступ к этой памяти осуществляется с помощью обычных средств языка программирования.
Системный вызов shmat() Прототип системного вызова #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> char *shmat(int shmid, char *shmaddr, int shmflg); Описание системного вызова Системный вызов shmat предназначен для включения области разделяемой памяти в адресное пространство текущего процесса. Данное описание не является полным описанием системного вызова, а ограничивается рамками текущего курса. Для полного описания обращайтесь к UNIX Manual. Параметр shmid является дескриптором System V IPC для сегмента разделяемой памяти, т. е. значением, которое вернул системный вызов shmget() при создании сегмента или при его поиске по ключу. В качестве параметра shmaddr в рамках нашего курса мы всегда будем передавать значение NULL, позволяя операционной системе самой разместить разделяемую память в адресном пространстве нашего процесса. Параметр shmflg в нашем курсе может принимать только два значения: 0 – для осуществления операций чтения и записи над сегментом и SHM_RDONLY – если мы хотим только читать из него. При этом процесс должен иметь соответствующие права доступа к сегменту. Возвращаемое значение Системный вызов возвращает адрес сегмента разделяемой памяти в адресном пространстве процесса при нормальном завершении и значение -1 при возникновении ошибки. |
После окончания использования разделяемой памяти процесс может уменьшить размер своего адресного пространства, исключив из него эту область с помощью системного вызова shmdt() . Отметим, что в качестве параметра системный вызов shmdt() требует адрес начала области разделяемой памяти в адресном пространстве процесса, т. е. значение, которое вернул системный вызов shmat() , поэтому данное значение следует сохранять на протяжении всего времени использования разделяемой памяти.
Системный вызов shmdt() Прототип системного вызова #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> int shmdt(char *shmaddr); Описание системного вызова Системный вызов shmdt предназначен для исключения области разделяемой памяти из адресного пространства текущего процесса. Параметр shmaddr является адресом сегмента разделяемой памяти,т. е. значением, которое вернул системный вызов shmat() . Возвращаемое значение Системный вызов возвращает значение 0 при нормальном завершении и значение -1 при возникновении ошибки. |