Управление массивом семафоров.
#include <sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
int semctl(int semid, int num, int cmd, union semun arg)
С помощью этого системного вызова можно запрашивать и изменять управляющие параметры разделяемого ресурса, а также удалять его.
Первый параметр вызова – дескриптор массива семафоров. Параметр num представляет собой индекс семафора в массиве, параметр cmd задает операцию, которая должна быть выполнена над данным семафором. Последний аргумент имеет тип union semun и используется для считывания или задания управляющих параметров одного семафора или всего массива, в зависимости от значения аргумента cmd. Тип данных union semun определен в файле <sys/sem.h> и выглядит следующим образом:
union semun {
int val; // значение одного семафора
struct semid_ds *buf; /* параметры массива семафоров в целом */
ushort *array; /* массив значений семафоров */
}
где struct semid_ds – структура, описанная в том же файле, в полях которой хранится информация о всем наборе семафоров в целом, а именно, количество семафоров в наборе, права доступа к нему и статистика доступа к массиву семафоров.
Приведем некоторые наиболее часто используемые значения аргумента cmd:
IPC_STAT – скопировать управляющие параметры набора семафоров по адресу arg.buf
IPC_SET– заменить управляющие параметры набора семафоров на те, которые указаны в arg.buf. Чтобы выполнить эту операцию, процесс должен быть владельцем или создателем массива семафоров, либо обладать правами привилегированного пользователя, при этом процесс может изменить только владельца массива семафоров и права доступа к нему.
IPC_RMID – удалить массив семафоров. Чтобы выполнить эту операцию, процесс должен быть владельцем или создателем массива семафоров, либо обладать правами привилегированного пользователя
GETALL,SETALL– считать / установить значения всех семафоров в массив, на который указывает arg.array
GETVAL – возвратить значение семафора с номером num. Последний аргумент вызова игнорируется.
SETVAL – установить значение семафора с номером num равным arg.val
В случае успешного завершения вызов возвращает значение, соответствующее конкретной выполнявшейся операции (0, если не оговорено иное), в случае неудачи – -1.
XIV. Работа с разделяемой памятью с синхронизацией семафорами.
Программа будет оперировать с разделяемой памятью.
1 процесс – создает ресурсы “разделяемая память” и “семафоры”, далее он начинает принимать строки со стандартного ввода и записывает их в разделяемую память.
2 процесс – читает строки из разделяемой памяти.
Таким образом мы имеем критический участок в момент, когда один процесс еще не дописал строку, а другой ее уже читает. Поэтому следует установить некоторые синхронизации и задержки.
1й процесс:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#define NMAX 256
int main(int argc, char **argv)
{
key_t key;
int semid, shmid;
struct sembuf sops;
char *shmaddr;
char str[NMAX];
key = ftok(“/usr/ter/exmpl”, ’S’);
/* создаем уникальный ключ */
semid = semget(key, 1, 0666 | IPC_CREAT);
/* создаем один семафор с определенными правами доступа */
shmid = shmget(key, NMAX, 0666 | IPC_CREAT);
/* создаем разделяемую память на 256 элементов */
shmaddr = shmat(shmid, NULL, 0);
/* подключаемся к разделу памяти, в shaddr – указатель на буфер с разделяемой памятью */
semctl(semid,0,SETVAL, (int) 0);
/* инициализируем семафор значением 0 */
sops.sem_num = 0;
sops.sem_flg = 0;
do { /* запуск цикла */
printf(“Введите строку:”);
if (fgets(str, NMAX, stdin) == NULL)
{
/* окончание ввода */
/* пишем признак завершения – строку “Q” */
strcpy(str, “Q”);
}
/* в текущий момент семафор открыт для этого процесса */
strcpy(shmaddr, str); /* копируем строку в разд. память */
/* предоставляем второму процессу возможность войти */
sops.sem_op = 3; /* увеличение семафора на 3 */
semop(semid, &sops, 1);
/* ждем, пока семафор будет открыт для 1го процесса - для следующей итерации цикла */
sops.sem_op = 0; /* ожидание обнуления семафора */
semop(semid, &sops, 1);
} while (str[0] != ‘Q’);
/* в данный момент второй процесс уже дочитал из разделяемой памяти и отключился от нее – можно ее удалять*/
shmdt(shmaddr) ; /* отключаемся от разделяемой памяти */
shmctl(shmid, IPC_RMID, NULL);
/* уничтожаем разделяемую память */
semctl(semid, 0, IPC_RMID, (int) 0);
/* уничтожаем семафор */
return 0;
}
2й процесс:
/* необходимо корректно определить существование ресурса, если он есть - подключиться */
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <string.h>
#define NMAX 256
int main(int argc, char **argv)
{
key_t key;
int semid, shmid;
struct sembuf sops;
char *shmaddr;
char str[NMAX];
key = ftok(“/usr/ter/exmpl”,’S’);
/* создаем тот же самый ключ */
semid = semget(key, 1, 0666 | IPC_CREAT);
shmid = shmget(key, NMAX, 0666 | IPC_CREAT);
/* аналогично предыдущему процессу - инициализации ресурсов */
shmaddr = shmat(shmid, NULL, 0);
sops.sem_num = 0;
sops.sem_flg = 0;
/* запускаем цикл */
do {
printf(“Waiting… \n”); /* ожидание на семафоре */
sops.sem_op = -2;
/* будем ожидать, пока “значение семафора” + ”значение sem_op” не станет положительным, т.е. пока значение семафора не станет как минимум 3 (3-2=1 > 0) */
semop(semid, &sops, 1);
/* теперь значение семафора равно 1 */
strcpy(str, shmaddr); /* копируем строку из разд.памяти */
/*критическая секция - работа с разделяемой памятью - в этот момент первый процесс к разделяемой памяти доступа не имеет*/
if (str[0] == ‘Q’)
{
/*завершение работы - освобождаем разделяемую память */
shmdt(shmaddr);
}
/*после работы – обнулим семафор*/
sops.sem_op=-1;
semop(semid, &sops, 1);
printf(“Read from shared memory: %s\n”, str);
} while (str[0] != ‘Q’);
return 0;
}
Отметим, что данный пример демонстрирует два разных приема использования семафоров для синхронизации: первый процесс блокируется в ожидании обнуления семафора, т.е. для того, чтобы он мог войти в критическую секцию, значение семафора должно стать нулевым; второй процесс блокируется при попытке уменьшить значение семафора до отрицательной величины, для того, чтобы этот процесс мог войти в критическую секцию, значение семафора должно быть не менее 3. Обратите внимание, что в данном примере, помимо взаимного исключения процессов, достигается строгая последовательность действий двух процессов: они получают доступ к критической секции строго по очереди.
БИЛЕТ 43
Механизм сокетов.
Средства межпроцессного взаимодействия ОС UNIX, представленные в системе IPC, решают проблему взаимодействия двух процессов, выполняющихся в рамках одной операционной системы. Однако, очевидно, их невозможно использовать, когда требуется организовать взаимодействие процессов в рамках сети. Это связано как с принятой системой именования, которая обеспечивает уникальность только в рамках данной системы, так и вообще с реализацией механизмов разделяемой памяти, очереди сообщений и семафоров, – очевидно, что для удаленного взаимодействия они не годятся. Следовательно, возникает необходимость в каком-то дополнительном механизме, позволяющем общаться двум процессам в рамках сети. Однако если разработчики программ будут иметь два абсолютно разных подхода к реализации взаимодействия процессов, в зависимости от того, на одной машине они выполняются или на разных узлах сети, им, очевидно, придется во многих случаях создавать два принципиально разных куска кода, отвечающих за это взаимодействие. Понятно, что это неудобно и хотелось бы в связи с этим иметь некоторый унифицированный механизм, который в определенной степени позволял бы абстрагироваться от расположения процессов и давал бы возможность использования одних и тех же подходов для локального и нелокального взаимодействия. Кроме того, как только мы обращаемся к сетевому взаимодействию, встает проблема многообразия сетевых протоколов и их использования. Понятно, что было бы удобно иметь какой-нибудь общий интерфейс, позволяющий пользоваться услугами различных протоколов по выбору пользователя.
Обозначенные проблемы был призван решить механизм, впервые появившийся в UNIX – BSD (4.2) и названный сокетами (sockets).
Сокеты представляют собой в определенном смысле обобщение механизма каналов, но с учетом возможных особенностей, возникающих при работе в сети. Кроме того, они предоставляют больше возможностей по передаче сообщений, например, могут поддерживать передачу экстренных сообщений вне общего потока данных. Общая схема работы с сокетами любого типа такова: каждый из взаимодействующих процессов должен на своей стороне создать и отконфигурировать сокет, после чего процессы должны осуществить соединение с использованием этой пары сокетов. По окончании взаимодействия сокеты уничтожаются.
Механизм сокетов чрезвычайно удобен при разработке взаимодействующих приложений, образующих систему «клиент-сервер». Клиент посылает серверу запросы на предоставление услуги, а сервер отвечает на эти запросы.
Схема использования механизма сокетов для взаимодействия в рамках модели «клиент-сервер» такова. Процесс-сервер запрашивает у ОС сокет и, получив его, присваивает ему некоторое имя (адрес), которое предполагается заранее известным всем клиентам, которые захотят общаться с данным сервером. После этого сервер переходит в режим ожидания и обработки запросов от клиентов. Клиент, со своей стороны, тоже создает сокет и запрашивает соединение своего сокета с сокетом сервера, имеющим известное ему имя (адрес). После того, как соединение будет установлено, клиент и сервер могут обмениваться данными через соединенную пару сокетов. Ниже мы подробно рассмотрим функции, выполняющие все необходимые действия с сокетами, и напишем пример небольшой серверной и клиентской программы, использующих сокеты.
Типы сокетов. Коммуникационный домен.
Сокеты подразделяются на несколько типов в зависимости от типа коммуникационного соединения, который они используют. Два основных типа коммуникационных соединений и, соответственно, сокетов представляет собой соединение с использованием виртуального канала и датаграммное соединение.
Соединение с использованием виртуального канала – это последовательный поток байтов, гарантирующий надежную доставку сообщений с сохранением порядка их следования. Данные начинают передаваться только после того, как виртуальный канал установлен, и канал не разрывается, пока все данные не будут переданы. Примером соединения с установлением виртуального канала является механизм каналов в UNIX, аналогом такого соединения из реальной жизни также является телефонный разговор. Заметим, что границы сообщений при таком виде соединений не сохраняются, т.е. приложение, получающее данные, должно само определять, где заканчивается одно сообщение и начинается следующее. Такой тип соединения может также поддерживать передачу экстренных сообщений вне основного потока данных, если это возможно при использовании конкретного выбранного протокола.
Датаграммное соединение используется для передачи отдельных пакетов, содержащих порции данных – датаграмм. Для датаграмм не гарантируется доставка в том же порядке, в каком они были посланы. Вообще говоря, для них не гарантируется доставка вообще, надежность соединения в этом случае ниже, чем при установлении виртуального канала. Однако датаграммные соединения, как правило, более быстрые. Примером датаграммного соединения из реальной жизни может служить обычная почта: письма и посылки могут приходить адресату не в том порядке, в каком они были посланы, а некоторые из них могут и совсем пропадать.
Поскольку сокеты могут использоваться как для локального, так и для удаленного взаимодействия, встает вопрос о пространстве адресов сокетов. При создании сокета указывается так называемый коммуникационный домен, к которому данный сокет будет принадлежать. Коммуникационный домен определяет форматы адресов и правила их интерпретации. Мы будем рассматривать два основных домена: для локального взаимодействия – домен AF_UNIX и для взаимодействия в рамках сети – домен AF_INET (префикс AF обозначает сокращение от «address family» – семейство адресов). В домене AF_UNIX формат адреса – это допустимое имя файла, в домене AF_INET адрес образуют имя хоста + номер порта.
Заметим, что фактически коммуникационный домен определяет также используемые семейства протоколов. Так, для домена AF_UNIX это будут внутренние протоколы ОС, для домена AF_INET – протоколы семейства TCP/IP.Современные системы поддерживают и другие коммуникационные домены, напримерBSD UNIX поддерживает также третий домен – AF_NS, использующий протоколы удаленного взаимодействия Xerox NS.
Ниже приведен набор функций для работы с сокетами.