Операционные системы 2011 (1114689), страница 48
Текст из файла (страница 48)
После этого он может работать с этой областьюпамяти аналогично тому, как если бы она была выделена динамически (например, путемобращения к malloc()), однако, как уже говорилось, разделяемая область памяти неуничтожается автоматически даже после того, как процесс, создавший илииспользовавший ее, перестанет с ней работать.Физическая памятьВиртуальное адресноепр-во процессаВиртуальное адресноепр-во процессаРис.
100. Разделяемая память.Рассмотрим набор системных вызовов для работы с разделяемой памятью. Длясоздания/подключения к ресурсу разделяемой памяти IPC используется функция shmget().#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>int shmget (key_t key, int size, int shmflg);Аргументы данной функции: key — ключ для доступа к разделяемой памяти; sizeзадает размер области памяти, к которой процесс желает получить доступ.
Если врезультате вызова shmget() будет создана новая область разделяемой памяти, то ее размербудет соответствовать значению size. Если же процесс подключается к существующейобласти разделяемой памяти, то значение size должно быть не более ее размера, иначевызов вернет -1. Заметим, что если процесс при подключении к существующей области174разделяемой памяти указал в аргументе size значение, меньшее ее фактического размера,то впоследствии он сможет получить доступ только к первым size байтам этой области.Третий параметр определяет флаги, управляющие поведением вызова. Подробнееалгоритм создания/подключения разделяемого ресурса был описан выше.В случае успешного завершения вызов возвращает положительное число —дескриптор области памяти, в случае неудачи возвращается -1.
Но наличие у процессадескриптора разделяемой памяти не дает ему возможности работать с ресурсом,поскольку при работе с памятью процесс работает в терминах адресов. Поэтомунеобходима еще одна функция, которая присоединяет полученную разделяемую память кадресному пространству процесса, — это функция shmat().#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>char *shmat(int shmid, char *shmaddr, int shmflg);При помощи этого вызова процесс подсоединяет область разделяемой памяти,дескриптор которой указан в shmid, к своему виртуальному адресному пространству.После выполнения этой операции процесс сможет читать и модифицировать данные,находящиеся в области разделяемой памяти, адресуя ее как любую другую область всвоем собственном виртуальном адресном пространстве.В качестве второго аргумента процесс может указать виртуальный адрес в своемадресном пространстве, начиная с которого необходимо подсоединить разделяемуюпамять.
Если в качестве значения этого аргумента передается 0, то это означает, чтосистема сама может выбрать адрес начала разделяемой памяти. Передача конкретногоадреса (положительного целого) в этом параметре имеет смысл лишь в определенныхслучаях, и это означает, что процесс желает связать начало области разделяемой памяти сконкретным адресом. В подобных случаях необходимо учитывать, что возможныколлизии с имеющимся адресным пространством.Третий аргумент представляет собой комбинацию флагов. В качестве значенияэтого аргумента может быть указан флаг SHM_RDONLY, который указывает на то, чтоподсоединяемая область будет использоваться только для чтения. Реализация тех илииных флагов будет зависеть от аппаратной поддержки соответствующего свойства.
Еслиаппаратура не поддерживает защиту памяти от записи, то при установке флагаSHM_RDONLY ошибка, связанная с модификацией содержимого памяти, не сможет бытьобнаружена (поскольку программным способом невозможно выявить, в какой моментпроисходит обращение на запись в данную область памяти).Эта функция возвращает адрес (указатель) в виртуальном адресном пространствепроцесса, начиная с которого будет отображаться присоединяемая разделяемая память.
Ис этим указателем можно работать стандартными средствами языка C. В случае неудачивызов возвращает -1.Соответственно, для открепления разделяемой памяти от адресного пространствапроцесса используется функция shmdt().#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>int shmdt(char *shmaddr);Данный вызов позволяет отсоединить разделяемую память, ранее присоединеннуюпосредством вызова shmat(). Параметр shmaddr — адрес прикрепленной к процессу175памяти, который был получен при вызове shmat(). В случае успешного выполненияфункция вернет значение 0, в случае неудачи возвращается -1.И, напоследок, рассмотрим функцию shmctl() управления ресурсом разделяемаяпамять.#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);Данный вызов используется для получения или изменения процессомуправляющих параметров, связанных с областью разделяемой памяти, наложения иснятия блокировки на нее и ее уничтожения.
Аргументы вызова — дескриптор областипамяти, команда, которую необходимо выполнить, и структура, описывающаяуправляющие параметры области памяти. Тип shmid_ds описан в заголовочном файле<sys/shm.h>, и представляет собой структуру, в полях которой хранятся права доступак области памяти, ее размер, число процессов, подсоединенных к ней в данный момент, истатистика обращений к области памяти.Возможные значения аргумента cmd:IPC_STAT – скопировать структуру, описывающую управляющие параметрыобласти памяти по адресу, указанному в параметре bufIPC_SET – заменить структуру, описывающую управляющие параметры областипамяти, на структуру, находящуюся по адресу, указанному в параметре buf. Выполнитьэту операцию может процесс, у которого эффективный идентификатор пользователясовпадает с владельцем или создателем очереди, либо процесс с правамипривилегированного пользователя, при этом процесс может изменить только владельцаобласти памяти и права доступа к ней.IPC_RMID – удалить очередь.
Как уже говорилось, удалить очередь может толькопроцесс, у которого эффективный идентификатор пользователя совпадает с владельцемили создателем очереди, либо процесс с правами привилегированного пользователя.SHM_LOCK, SHM_UNLOCK – блокировать или разблокировать область памяти.Выполнить эту операцию может только процесс с правами привилегированногопользователя.Это единственные средства синхронизации в данном ресурсе, их реализациядолжна поддерживаться аппаратурой.Пример.
Схема работы с общей памятью в рамках одного процесса. В данномпримере процесс создает ресурс разделяемая память, размером в 100 байт (и ссоответствующими флагами), присоединяет ее к своему адресному пространству; приэтом указатель на начало данной области сохраняется в переменной shmaddr. Дальшепроцесс производит различные манипуляции, а перед своим завершением он удаляетданную область разделяемой памяти. В данном примере считается, что putm() иwaitprocess() – некие пользовательские функции, определенные в другом месте.int main(int argc, char **argv){key_t key;char *shmaddr;key = ftok(“/tmp/ter”, ’S’);shmid = shmget(key, 100, 0666 | IPC_CREAT | IPC_EXCL);shmaddr = shmat(shmid, NULL, 0);176/* работаем с разделяемой памятью, как с обычной */putm(shmaddr);waitprocess();shmctl(shmid, IPC_RMID, NULL);return 0;}3.2.3 Массив семафоров IPCСемафоры представляют собой одну из форм IPC и обычно используются длясинхронизации доступа нескольких процессов к разделяемым ресурсам, так как сами посебе другие средства IPC не предоставляют механизма синхронизации.Как уже говорилось, семафор представляет собой особый вид числовойпеременной, над которой определены две неделимые операции: уменьшение ее значения свозможным блокированием процесса и увеличение значения с возможнымразблокированием одного из ранее заблокированных процессов.
Объект System V IPCпредставляет собой набор семафоров. Как правило, использование семафоров в качествесредства синхронизации доступа к другим разделяемым объектам предполагаетследующую схему:- с каждым разделяемым ресурсом связывается один семафор из набора;- положительное значение семафора означает возможность доступа кресурсу (ресурс свободен), неположительное – отказ в доступе (ресурсзанят);- перед тем как обратиться к ресурсу, процесс уменьшает значениесоответствующего ему семафора, при этом, если значение семафорапосле уменьшения должно оказаться отрицательным, то процесс будетзаблокирован до тех пор, пока семафор не примет такое значение, чтобыпри уменьшении его значение оставалось неотрицательным;- закончив работу с ресурсом, процесс увеличивает значение семафора(при этом разблокируется один из ранее заблокированных процессов,ожидающих увеличения значения семафора, если таковые имеются);- в случае реализации взаимного исключения используется двоичныйсемафор, т.е.
такой, что он может принимать только значения 0 и 1:такой семафор всегда разрешает доступ к ресурсу не более чем одномупроцессу одновременноРассмотрим набор вызовов для оперирования с семафорами в UNIX System V.3.2.3.1 Доступ к семафоруДля получения доступа к массиву семафоров (или его создания) используетсясистемный вызов semget():#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>int semget (key_t key, int nsems, int semflag);Первый параметр функции semget() – ключ для доступа к разделяемому ресурсу,второй - количество семафоров в создаваемом наборе (длина массива семафоров) и третийпараметр – флаги, управляющие поведением вызова.
Подробнее процесс созданияразделяемого ресурса описан выше. Отметим семантику прав доступа к такому типуразделяемых ресурсов, как семафоры: процесс, имеющий право доступа к массивусемафоров по чтению, может проверять значение семафоров; процесс, имеющий праводоступа по записи, может как проверять, так и изменять значения семафоров.177В случае, если среди флагов указан IPC_CREAT, аргумент nsems долженпредставлять собой положительное число, если же этот флаг не указан, значение nsemsигнорируется. Отметим, что в заголовочном файле <sys/sem.h> определена константаSEMMSL, задающая максимально возможное число семафоров в наборе. Если значениеаргумента nsems больше этого значения, вызов semget() завершится неудачно.В случае успеха вызов semget() возвращает положительный дескрипторсозданного разделяемого ресурса, в случае неудачи -1.3.2.3.2 Операции над семафоромИспользуя полученный дескриптор, можно производить изменять значения одногоили нескольких семафоров в наборе, а также проверять их значения на равенство нулю,для чего используется системный вызов semop():#include <sys/types.h>#include<sys/ipc.h>#include<sys/sem.h>int semop (int semid, struct sembuf *semop, size_t nops)Этому вызову передаются следующие аргументы:semid – дескриптор массива семафоров;semop – массив из объектов типа struct sembuf, каждый из которых задаетодну операцию над семафором;nops – длина массива semop.
Количество семафоров, над которыми процессможет одновременно производить операцию в одном вызове semop(), ограниченоконстантой SEMOPM, описанной в файле <sys/sem.h>. Если процесс попытаетсявызвать semop() с параметром nops, большим этого значения, этот вызов вернетнеуспех.Структура имеет sembuf вид:struct sembuf {short sem_num; /* номер семафора в векторе */short sem_op;/* производимая операция */short sem_flg; /* флаги операции */}Поле операции в структуре интерпретируется следующим образом. Пусть значениесемафора с номером sem_num равно sem_val.1. если значение операции не равно нулю: оценивается значение суммы sem_val + sem_op. если эта сумма больше либо равна нулю, то значение данногосемафора устанавливается равным этой сумме: sem_val =sem_val + sem_op если же эта сумма меньше нуля, то действие процесса будетприостановлено до тех пор, пока значение суммы sem_val +sem_op не станет больше либо равно нулю, после чего значениесемафора устанавливается равным этой сумме: sem_val =sem_val + sem_op2.
Если код операции sem_op равен нулю: Если при этом значение семафора (sem_val) равно нулю,происходит немедленный возврат из вызова Иначе происходит блокирование процесса до тех пор, пока значениесемафора не обнулится, после чего происходит возврат из вызова178Таким образом, ненулевое значение поля sem_op обозначает необходимостьприбавить к текущему значению семафора значение sem_op, а нулевое – дождатьсяобнуления семафора.