
- •Сравнение характеристик разных ос.
- •Задание
- •Вопросы коллоквиума.
- •«Наследники» Создание дочерних процессов
- •Задание
- •Вопросы коллоквиума
- •«Трубы» Использование программных каналов
- •Задание
- •Вопросы коллоквиума
- •«На деревню дедушке» Использование очереди сообщений
- •Задание
- •Вопросы коллоквиума
- •«Вспомнить всё» Использование семафоров и разделяемой памяти
- •Задание
- •Вопросы коллоквиума
Задание
В одну очередь сообщений А (табл. 1) серверов посылают сообщения разных типов. Количество сообщений каждого типа определяется из табл. 2. Один из клиентов принимает все сообщения В типа, другой клиент принимает С сообщений любого типа, D клиентов принимают по Е сообщений остальных типов, последний клиент принимает, что осталось.
Вопросы коллоквиума
Что делает системный вызов msgget.
Что делает системный вызов msgsnd.
Что делает системный вызов msgrcv.
Что делает системный вызов msgctl.
Чем очередь сообщений отличается от программного канала.
Кто имеет право на удаление очереди сообщений.
«Вспомнить всё» Использование семафоров и разделяемой памяти
В ОС UNIX в адресном пространстве ядра имеется таблица семафоров, в которой отслеживаются все создаваемые в системе наборы семафоров. В каждом элементе таблицы семафоров находятся следующие данные об одном наборе семафоров:
Имя, представляющее собой целочисленный идентификационный ключ, присвоенный набору семафоров процессом, который его создал. Другие процессы могут, указывая этот ключ, "открывать" набор и получать дескриптор для доступа к набору.
UID создателя набора семафоров и идентификатор его группы. Процесс, эффективный UID которого совпадает с UID создателя, может удалять набор и изменять параметры управления им.
UID назначенного владельца и идентификатор его группы. Эти идентификаторы обычно совпадают с идентификаторами создателя, но процесс создателя может устанавливать эти идентификаторы для переназначения владельца набора и принадлежности к группе.
Права доступа к набору для чтения-записи по категориям "владелец", "группа" и "прочие". Процесс, имеющий право на чтение набора, может запрашивать значения семафоров и UID назначенного владельца и группы. Процесс, имеющий право на запись в набор, может изменять значения семафоров.
Количество семафоров в наборе.
Время изменения одного или нескольких значений семафоров последним процессом.
Время последнего изменения управляющих параметров набора каким-либо процессом.
Указатель на массив семафоров.
Семафоры в наборе обозначаются индексами массива: например, первый семафор в наборе имеет индекс нуль, второй — единицу и т.д. В каждом семафоре содержатся следующие данные:
значение семафора;
идентификатор процесса, который оперировал семафором в последний раз;
число процессов, заблокированных в текущий момент и ожидающих увеличения значения семафора;
число процессов, заблокированных в текущий момент и ожидающих обращения значения семафора в нуль.
Как и сообщения, семафоры хранятся в адресном пространстве ядра и являются устойчивыми, т.е. сохраняются независимо от завершения создавшего их процесса. У каждого набора семафоров есть назначенный владелец, и удалить набор или изменить его управляющие параметры могут только процессы, имеющие права привилегированного пользователя, создателя набора или назначенного владельца. Если набор семафоров удаляется, то ядро активизирует все процессы, которые в данный момент заблокированы семафорами этого набора; все произведенные данными процессами системные вызовы прерываются и возвращают код неудачного завершения, -1.
В заголовке <sys/ipc.h> объявляются тип данных struct ipc_perm, который используется в данном наборе семафоров для хранения UID, GID, ID создателя и его группы, идентификатор и права доступа на чтение и запись.
Элементы таблицы семафоров имеют тип данных struct semid_ds, который определяется в заголовке <sys/sem.h>. Ниже перечислены названия полей этой структуры и данные, которые в них хранятся.
Поле |
Данные |
sem_perm |
Данные, хранящиеся в записи struct ipc_perm |
sem_nsems |
Число семафоров в наборе |
sem_base |
Указатель на массив семафоров |
sem_otime |
Время, когда какой-либо процесс в последний раз выполнял операции над семафорами |
sem_ctime |
Время, когда тот или иной процесс в последний раз изменял управляющие параметры набора |
Помимо этого, тип данных struct sem, определенный в заголовке <sys/sem.h>, используется для представления данных, хранящихся в семафоре.
Поле |
Данные |
semval |
Целочисленное значение текущего семафора |
sempid |
Идентификатор процесса, который выполнял операции над данным семафором в последний раз |
semncnt |
Число процессов, которые заблокированы и ожидают увеличения значения семафора |
semzcnt |
Число процессов, которые заблокированы и ожидают обращения значения семафора в нуль |
Разделяемая память позволяет множеству процессов отображать часть своих виртуальных адресов в общую область памяти. Благодаря этому любой процесс может записывать данные в разделяемую область памяти, и эти данные будут доступны для чтения и модификации другим процессам.
Разделяемая память была задумана как средство повышения производительности при обработке сообщений. Дело в том, что при передаче сообщения из процесса в очередь сообщений данные копируются из виртуального адресного пространства процесса в область данных ядра. Затем, когда другой процесс принимает это сообщение, ядро копирует данные сообщения из своей области в виртуальное адресное пространство процесса-получателя. Таким образом, содержащиеся в сообщении данные копируются дважды: вначале из процесса в ядро, а затем в другой процесс. При наличии разделяемой памяти такие дополнительные издержки отсутствуют: эта память выделяется в виртуальном адресном пространстве ядра всякий раз, когда процессу необходимо записывать данные. Они обрабатываются непосредственно в области памяти ядра. При этом, однако, в разделяемой памяти не предусмотрены какие-либо методы управления доступом для процессов, которые ею пользуются. Поэтому среда межпроцессного взаимодействия формируется, как правило, путем использования разделяемой памяти и семафоров.
Процесс, подсоединившийся к разделяемой области памяти, получает указатель на эту разделяемую память. Им можно пользоваться так, как будто он получен в результате вызова функции динамического распределения памяти. Единственное различие состоит в том, что данные в разделяемой памяти не исчезают, даже если процесс, создавший разделяемую область, завершается.
В любой момент времени в системе может существовать множество разделяемых областей памяти.
В UNIX в адресном пространстве ядра имеется таблица разделяемой памяти, в которой отслеживаются все разделяемые области памяти, создаваемые в системе. В каждом элементе таблицы находятся следующие данные об одной разделяемой области памяти:
Имя, представляющее собой целочисленный идентификационный ключ, присвоенный разделяемой области памяти процессом, который ее создал. Другие процессы могут, указывая этот ключ, "открывать" данную область и получать дескриптор для дальнейшего обращения к области и отсоединения от нее.
UID создателя разделяемой области памяти и идентификатор его группы. Процесс, эффективный UID которого совпадает с UID создателя разделяемой области памяти, может удалять область и изменять параметры управления ею.
UID назначенного пользователя-владельца и идентификатор его группы. Эти идентификаторы обычно совпадают с идентификаторами создателя, но создатель может устанавливать эти идентификаторы для переназначения владельца области и принадлежности к группе.
Права доступа к области для чтения-записи по категориям "владелец", "группа" и "прочие". Процесс, имеющий право на чтение из данной области, может читать из нее данные и запрашивать UID назначенного пользователя и его группы. Процесс, имеющий разрешение на запись в область, может записывать в нее данные.
Размер разделяемой области памяти в байтах.
Время, когда какой-либо процесс в последний раз подсоединялся к области.
Время, когда какой-либо процесс в последний раз отсоединялся от области.
Время, когда какой-либо процесс в последний раз изменил управляющие параметры области.
В заголовке <sys/ipc.h> объявляется тип данных struct ipc_perm, используемый для хранения UID создателя, UID владельца, идентификаторов их групп, присвоенного ключевого имени и прав на чтение и запись, установленных для разделяемой области памяти.
Элементы таблицы разделяемой памяти относятся к типу данных struct shmid_ds, который определяется в заголовке <sys/shm.h>. Ниже перечислены информационные поля этой структуры и данные, которые в них хранятся.
Поле |
Данные |
shm_perm |
Данные, хранящиеся в записи struct ipc_perm |
shm_segsz |
Размер разделяемой области памяти в байтах |
shm_lpid |
Идентификатор процесса, который в последний раз подсоединялся к области |
shm_cpid |
Идентификатор процесса-создателя |
shm_nattch |
Число процессов, подсоединенных к области в данный момент |
shm_atime |
Время, когда процесс в последний раз подсоединялся к области |
shm_dtime |
Время, когда процесс в последний раз отсоединялся от области |
shm_ctime |
Время, когда последний процесс изменил управляющие параметры области |
Прототип функции semget имеет следующий вид:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget (key_t key, int num_sem, int flag );
Эта функция "открывает" набор семафоров, идентификатор которого задан значением аргумента key, и возвращает неотрицательный целочисленный дескриптор. Его можно использовать в других вызовах семафоров для запроса и изменения значения семафора, а также для запроса и/или установки управляющих параметров набора семафоров.
Если значение аргумента key — положительное целое, рассматриваемый вызов пробует открыть набор семафоров, ключевой идентификатор которого совпадает с указанным значением. Если же значением key является макрос IPC_PRIVATE, вызов создает новый набор семафоров, который будет использоваться исключительно вызывающим процессом. Такие "частные" семафоры обычно выделяются родительским процессом, создающим затем один или несколько порожденных процессов. Родительский и порожденные процессы пользуются этими семафорами для синхронизации своей работы.
При нулевом значении аргумента flag вызов прерывает свою работу, если отсутствует набор семафоров, ключевой идентификатор которого совпадал бы с заданным значением key; в противном случае возвращается дескриптор этого набора. Если процессу необходимо создать новый набор с идентификатором key (и набора с таким идентификатором нет), то значение аргумента flag должно представлять собой результат побитового логического сложения константы IPC_CREAT и числовых значений прав доступа к новому набору для чтения и записи.
Значение num_sem может быть равно нулю, если флаг IPC_CREAT в аргументе flag не указан, или числу семафоров во вновь создаваемом наборе.
Для гарантированного создания нового набора семафоров можно указать одновременно с флагом IPC_CREAT флаг IPC_EXCL. Тогда вызов будет успешно выполнен только в том случае, если он создаст новый набор с заданным значением key.
В случае неудачи этот вызов возвращает -1.
Прототип функции semop имеет следующий вид:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop (int semfd, struct sembuf* opPtr, int len );
С помощью этой функции можно изменять значение одного или нескольких семафоров в наборе (указанном аргументом semfd) и/или проверять равенство их значений нулю. Аргумент opPtr — это указатель на массив объектов типа struct sembuf, каждый из которых задает одну операцию (запрос или изменение значения). Аргумент len показывает, сколько элементов имеется в массиве, указанном аргументом opPtr.
Тип данных struct sembuf определяется в заголовке <sys/sem.h>:
struct sembuf{
short sem num; // индекс семафора
short sem op; // операция над семафором
short sem flg; // флаг(и) операции
};
Переменная sem_op может иметь следующие значения.
Значение sem_op |
Смысл |
Положительное число |
Увеличить значение указанного семафора на эту величину |
Отрицательное число |
Уменьшить значение указанного семафора на эту величину |
0 |
Проверить равенство значения семафора нулю |
Если вызов semop попытается уменьшить значение семафора до отрицательного числа или посчитает, что значение семафора равно нулю, когда на самом деле это не так, то ядро заблокирует вызывающий процесс. Этого не произойдет в том случае, если в полях sem_flg элементов массива, где sem_op меньше или равно нулю, указан флаг IPC NOWAIT.
В случае успешного выполнения этот вызов возвращает нуль, а в случае неудачи -1.
Прототип функции semctl имеет следующий вид:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semfd, int num, int cmd, union semun arg );
С помощью этой функции можно запрашивать и изменять управляющие параметры набора семафоров, указанного аргументом semfd, а также удалять семафор.
Значение semfd — это дескриптор набора семафоров, который берется из вызова функции semget.
Значение num — это индекс семафора, а следующий аргумент, cmd, задает операцию, которая должна быть выполнена над конкретным семафором данного набора.
Аргумент arg — это объект типа union, который может использоваться для задания или выборки управляющих параметров одного или нескольких семафоров набора в соответствии с аргументом cmd. Тип данных union semun
определяется в заголовке <sys/sem.h>:
union semun{
int val; // значение семафора
struct semid_ds *buf; // управляющие параметры набора
ushort *array; // массив значений семафоров
}
Ниже перечислены возможные значения аргумента cmd и их смысл.
Значение cmd |
Что должен сделать процесс |
IPC_STAT |
Копировать управляющие параметры набора в объект, указанный аргументом arg.buf. У вызывающего процесса должно быть право на чтение набора |
IPC_SET |
Заменить управляющие параметры набора семафоров данными, определенными в объекте, на который указывает аргумент arg.buf. Чтобы выполнить эту операцию, вызывающий процесс должен иметь права привилегированного пользователя, создателя или назначенного владельца набора. Этот вызов может устанавливать UID владельца набора и идентификатор его группы, а также права доступа |
IPC_RMID |
Удалить семафор из системы. Чтобы выполнить эту операцию, вызывающий процесс должен иметь права привилегированного пользователя, создателя или назначенного владельца набора |
GETALL |
Скопировать все значения семафоров в массив, на который указывает arg.array |
SETALL |
Установить все значения семафоров равными значениям, содержащимся в массиве, на который указывает arg.array |
GETVAL |
Возвратить значение семафора с номером num. Аргумент arg не используется |
SETVAL |
Установить значение семафора с номером num равным значению, указанному в arg.val |
GETPID |
Возвратить идентификатор процесса, который выполнял операции над семафором с номером num последним. Аргумент arg не используется |
GETNCNT |
Возвратить количество процессов, которые в текущий момент заблокированы и ожидают увеличения значения семафора с номером num. Аргумент arg не используется |
GETZCNT |
Возвратить количество процессов, которые в текущий момент заблокированы и ожидают обращения значения семафора с номером num в нуль. Аргумент arg не используется |
В случае успешного выполнения этот вызов возвращает значение, соответствующее конкретному cmd, а в случае неудачи — -1.
Прототип функции shmget имеет следующий вид:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget (key_t key, int size, int flag );
Этот функция открывает разделяемую область памяти, идентификатор которой совпадает со значением аргумента key. и возвращает неотрицательный целочисленный дескриптор. Его можно использовать в других вызовах разделяемой памяти.
Если значение аргумента key — положительное целое, данный вызов пробует открыть разделяемую область памяти, ключевой идентификатор которой совпадает с этим значением. Если же значением key является макрос IPC_PRIVATE, вызов выделяет новую разделяемую область памяти, которая будет использоваться исключительно вызывающим процессом. Такая "частная" разделяемая область памяти обычно выделяется родительским процессом, который затем создает один или несколько порожденных процессов. Родительский и порожденные процессы пользуются этой разделяемой памятью для обмена данными.
Аргумент size задает размер области разделяемой памяти, которая может быть подсоединена к вызывающему процессу с помощью вызовы shmat. Если в результате этого вызова создается новая область разделяемой памяти, ее размер будет определяться аргументом size. В случае, когда вызов открывает уже существующую область, значение аргумента size может быть меньше размера выделенной области разделяемой памяти или равно ему. Если в последнем случае значение size меньше фактического размера области разделяемой памяти, то вызывающий процесс может получить доступ только к первым size байтам области разделяемой памяти.
Если аргумент flag равен нулю и нет области разделяемой памяти, идентификатор которой совпадал бы с заданным значением key, то этот вызов завершается неудачно. В противном случае он возвращает дескриптор этой области. Если процессу необходимо создать разделяемую область памяти с заданным key (и области с таким идентификатором нет), то значение аргумента flag должно представлять собой результат побитового логического сложения константы IPC_CREAT и прав доступа к новой области памяти для чтения и записи.
В случае успешного выполнения эта функция возвращает положительный дескриптор, а в случае неудачи — -1.
Прототип функции shmat выглядит следующим образом:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
void* shmat (int shmid, void* addr, int flag );
Эта функция подсоединяет область разделяемой памяти, указанную аргументом shmid, к виртуальному адресному пространству вызывающего процесса. Процесс может затем читать данные из указанной области памяти и записывать в нее данные. Если это вновь создаваемая область разделяемой памяти, то ядро реально выделяет область памяти только тогда, когда первый процесс вызывает рассматриваемую функцию для подсоединения к новой области.
Аргумент addr задает начальный виртуальный адрес адресного пространства вызывающего процесса, в которое необходимо отобразить разделяемую память. Если это значение равно нулю, ядро может само найти в вызывающем процессе подходящий виртуальный адрес для отображения разделяемой памяти. Большинство приложений должны устанавливать значение addr в нуль, если они явно не хранят в разделяемой области памяти ссылки на указатели или адреса (например, не держат в этой области связный список). Очень важно, чтобы каждый процесс, обращающийся к данной области памяти, указывал один и тот же начальный виртуальный адрес области, в которую отображена разделяемая память.
Если значение аргумента addr не равно нулю, аргумент flag может содержать флаг SHM_RND. Этот флаг указывает ядру на то, что виртуальный адрес, заданный в аргументе addr, можно округлить до границы страницы памяти. Если флаг SHM_RND отсутствует и значение аргумента addr не равно нулю, соответствующий вызов завершается неудачно (это означает, что ядро не может отобразить разделяемую память в область, заданную виртуальным адресом).
Аргумент flag может иметь также значение SHM_RDONLY, которое говорит о том, что вызывающий процесс подсоединяется к разделяемой памяти только для чтения. Если этот флаг не установлен, то по умолчанию процесс может читать и записывать данные в разделяемую память с учетов разрешений, установленных создателем данной области.
Этот вызов возвращает виртуальный адрес области отображения разделяемой памяти, а в случае неудачи — -1. Следует отметить, что любой процесс, с целью подсоединения одной разделяемой области памяти к виртуальным адресным пространствам многих процессов, может вызывать функцию shmat многократно.
Прототип функции shmdt имеет следующий вид:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmdt ( void* addr );
Эта функция отсоединяет разделяемую память от заданного аргументом addr виртуального адреса вызывающего процесса.
Прежде чем вызывать данную функцию, необходимо получить посредством вызова shmat значение addr.
В случае успешного выполнения рассматриваемая функция возвращает 0, а в случае неудачи — -1.
Прототип функции semctl имеет следующий вид:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmcti (int shmid, int cmd, struct shmid_ds* buf );
С помощью этой функции можно запрашивать управляющие параметры разделяемой области памяти, указанной аргументом shmid, изменять эти параметры и удалять данную область памяти.
Значение shmid — это дескриптор разделяемой области памяти, полученный посредством вызова функции shmget.
Аргумент buf — это адрес объекта типа struct shmid_ds, который можно использовать для задания и выборки управляющих параметров разделяемой памяти, указанных аргументом cmd. Ниже перечислены возможные значения аргумента cmd и вызываемые ими действия:
Значение cmd |
Что должен сделать процесс |
IPC_STAT |
Копировать управляющие параметры разделяемой области памяти в объект, указанный аргументом buf |
IPC_SET |
Заменить управляющие параметры разделяемой области памяти параметрами, определенными в объекте, на который указывает аргумент buf. Чтобы выполнить эту операцию, вызывающий процесс должен иметь права привилегированного пользователя, создателя или назначенного владельца разделяемой памяти. Рассматриваемый вызов может устанавливать только UID владельца области и идентификатор его группы, а также права доступа |
IPC_RMID |
Удалить разделяемую область памяти из системы. Чтобы выполнить эту операцию, вызывающий процесс должен иметь права привилегированного пользователя, создателя или назначенного владельца области. Если к разделяемой области памяти, подлежащей удалению, подсоединены один или несколько процессов, то операция удаления будет отложена до тех пор, пока эти процессы не отсоединятся от нее |
SHM_LOCK |
Блокировать разделяемую область памяти. Для выполнения этой операций вызывающий процесс должен обладать правами привилегированного пользователя |
SHM_UNLOCK |
Разблокировать разделяемую область памяти. Для выполнения этой операции вызывающий процесс должен обладать правами привилегированного пользователя |
В случае успешного выполнения рассматриваемый вызов возвращает 0, а в случае неудачи -1.
В примере creator создаёт область разделяемой памяти и набор семафоров, writer записывает в разделяемую память, reader читает из нее, а polismen управляет процессом записи/чтения.
/*creator.c*/
#include<stdio.h>
#include<sys/types.h>
#include<iostream.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/shm.h>
void main(void)
{
int i,shmId,semId;
unsigned short int ray[2]={1,0};// кол-во и значения семафоров
union semun{
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;} ar;// управление семафорами
shmId = shmget(11,120,IPC_CREAT|0666);// идентификатор памяти 11
if(shmId==-1)
{
cout<<" память не выделяется\n";
exit;
}
semId=semget(99,2,IPC_CREAT|0666);// идентификатор семафоров 99
if(semId==-1)
{
cout<<" семафоры не созданы\n";
exit;
}
ar.array=ray;
i = semctl(semId,0,SETALL,ar);// установка значений всех семафоров
if(i==-1)
{
cout<<" семафоры не установлены\n";
exit;
}
}
/*writer.c*/
#include<stdio.h>
#include<sys/types.h>
#include<iostream.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/shm.h>
int main(void)
{
FILE *fp;
int i,shmId,semId;
char buff[40],*addr;
struct sembuf check_sem[1];
/* уменьшить 0-й до 0-разрешение для reader, уменьшить 1-й до -1-блокируем -writer*/
unsigned short int ray[2]={0,1};// кол-во и значения семафоров
union semun{
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;} ar;// управление семафорами
fp=fopen("string-3.txt","r");
shmId = shmget(11,120,0);// идентификатор памяти 11
if(shmId==-1)
{
cout<<" памяти нет\n";
exit;
}
addr=(char*)shmat(shmId,0,0);
semId=semget(99,0,0);// идентификатор семафоров 99
if(semId==-1)
{
cout<<" семафоры не подключены\n";
exit;
}
for(;;)
{
fgets(buff,40,fp);
strcpy(addr,buff);
cout<<buff;
ar.array=ray;
i = semctl(semId,0,SETALL,ar);// отпустить reader
check_sem[0].sem_num=1;
check_sem[0].sem_op =0;
check_sem[0].sem_flg=1;
i = semop(semId,check_sem,1);// заблокировать writer
if(i==-1)
{
cout<<" семафоры не доступны\n";
fclose(fp);
exit(-1);
}
}
}
/*reader.c*/
#include<stdio.h>
#include<sys/types.h>
#include<iostream.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/shm.h>
int main(void)
{
int i,shmId,semId;
char bb,buff[40],*addr;
struct sembuf check_sem[1]={0,0,0};
unsigned short int ray[2]={1,1};// кол-во и значения семафоров
union semun{
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;} ar;// управление семафорами
shmId = shmget(11,120,0);// идентификатор памяти 11
if(shmId==-1)
{
cout<<" памяти нет\n";
exit;
}
addr=(char*)shmat(shmId,0,0);
semId=semget(99,0,0);// идентификатор семафоров 99
if(semId==-1)
{
cout<<" семафоров не доступны\n";
exit;
}
for(;;)
{
i = semop(semId,check_sem,1);
if(i==-1)
{
cout<<" семафоры не доступны\n";
exit(-1);
}
strcpy(buff,addr);
cout<<buff;
ar.array=ray;
i = semctl(semId,0,SETALL,ar);// установка значений всех семафоров
}
}
/*polismen.c*/
#include<stdio.h>
#include<sys/types.h>
#include<iostream.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/shm.h>
void main(void)
{
int j,i,shmId,semId;
char buff[40],dr;
unsigned short int ray[2]={1,0};// кол-во и значения семафоров
union semun{
int val;
struct semid_ds *buf;
unsigned short int *array;
struct seminfo *__buf;} ar;
shmId = shmget(11,120,0);// идентификатор памяти 11
if(shmId==-1)
{
cout<<" памяти нет\n";
exit;
}
semId=semget(99,0,0);// идентификатор семафоров 99
if(semId==-1)
{
cout<<" семафоры не доступны\n";
exit;
}
for(j=0;j<4;j++)
{
cin>>dr;
ar.array=ray;
i = semctl(semId,0,SETALL,ar);
if(i==-1)
{
cout<<" семафоры не установлены\n";
exit;
}
}
i = semctl(semId,0,IPC_RMID,0);
if(i==-1)
{
cout<<" семафоры не удаляются\n";
exit;
}
i = shmctl(shmId,IPC_RMID,0);
if(i==-1)
{
cout<<" память не удаляется\n";
exit;
}
}