Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Int semget (key_t key, int nsems, int semflag).




 

 

где key - ключ,

nsems - количество семафоров (длина массива семафоров)

semflag - флаги. Через флаги можно определить права доступа и те операции, которые должны выполняться (открытие семафора, проверка, и т.д.).

Функция semget() возвращает целочисленный идентификатор созданного разделяемого ресурса, либо -1, если ресурс не удалось создать.

 

2)Операции над семафором

int semop (int semid, struct sembuf *semop, size_t nops)

где

 

semid – идентификатор ресурса,

*semop - указатель на структуру, определяющую операции, которые необходимо произвести над семафором.

 

struct sembuf { short sem_num; /*номер семафора в векторе*/

short sem_op; /*производимая операция*/

short sem_flg; /*флаги операции*/

}

 

nops - количество указателей на эту структуру, которые передаются функцией semop(). То есть операций может быть несколько и операционная система гарантирует их атомарное выполнение.

Поле операции интерпретируется следующим образом. Пусть значение семафора с номером sem_num равно sem_val. В этом случае, если значение операции не равно нулю, то оценивается значение суммы sem_val + sem_op. Если эта сумма больше либо равна нулю, то значение данного семафора устанавливается равным сумме предыдущего значения и кода операции, т.е. sem_val:= sem_val+sem_op. Если эта сумма меньше нуля, то действие процесса будет приостановлено до наступления одного из следующих событий:

1. Значение суммы sem_val + sem_op станет больше либо равно нулю.

2. Пришел какой-то сигнал. Значение semop в этом случае будет равно -1.

 

Если код операции semop равен нулю, то процесс будет ожидать обнуления семафора. Если мы обратились к функции semop с нулевым кодом операции, а к этому моменту значение семафора стало равным нулю, то никакого ожидания не происходит.

Рассмотрим третий параметр - флаги. Если третий параметр равен нулю, то это означает, что флаги не используются. Флагов имеется большое количество в т.ч. IPC_NOWAIT (при этом флаге во всех тех случаях, когда мы говорили, что процесс будет ожидать, он не будет ожидать).

 

Пример. Использование разделяемой памяти и семафоров.

Рассмотрим двухпроцессную программу:

 

1 процесс - создает ресурсы “разделяемая память” и “семафоры”, далее он начинает принимать строки со стандартного ввода и записывает их в разделяемую память.

2 процесс - читает строки из разделяемой памяти.

Таким образом, мы имеем критический участок в момент, когда один процесс еще не дописал строку, а другой ее уже читает. Поэтому следует установить некоторые синхронизации и задержки.

 

1й процесс:

#include <stdio.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

 

int main(void)

{ key_t key;

int semid, shmid;

struct sembuf sops;

char *shmaddr;

char str[256];

key = ftok(“/usr/mash/exmpl”,’S’); // создаем уникальный ключ

semid = semget(key,1,0666 | IPC_CREAT); // создаем один семафор с определенными // правами доступа

shmid = shmget(key,256, 0666 | IPC_CREAT); /*создаем разделяемую память на 256 элементов */

shmaddr = shmat(shmid, NULL, 0); /* подключаемся к разделу памяти, в shaddr -

указатель на буфер с разделяемой памятью*/

semctl(semid,0,IPC_SET, (union semun) 0); //инициализируем семафор со значением 0

sops.sem_num = 0; sops.sem_flg = 0;

 

// запуск бесконечного цикла

while(1) { printf(“Введите строку:”);

if ((str = gets(str)) == NULL) break;

sops.sem_op=0; // ожидание обнуления семафора

semop(semid, &sops, 1);

strcpy(shmaddr, str); // копируем строку в разд. память

sops.sem_op=3; // увеличение семафора на 3

semop(semid, &sops, 1);

}

shmaddr[0]=’Q’; // укажем 2-ому процессу на то,

sops.sem_op=3; // что пора завершаться

semop(semid, &sops, 1);

sops.sem_op = 0; // ждем, пока обнулится семафор

semop(semid, &sops, 1);

shmdt(shmaddr); // отключаемся от разд. памяти

semctl(semid, 0, IPC_RMID, (union semun) 0); // убиваем семафор

shmctl(shmid, IPC_RMID, NULL); // уничтожаем разделяемую память

exit(0); }

 

 

2й процесс:

/* здесь нам надо корректно определить существование ресурса, если он есть - подключиться, если нет - сделать что-то еще, но как раз этого мы делать не будем */

#include <stdio.h>

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

 

int main(void)

{ key_t key; int semid;

struct sembuf sops;

char *shmaddr;

char st=0;

 

// далее аналогично предыдущему процессу - инициализации ресурсов

semid = semget(key,1,0666 | IPC_CREAT);

shmid = shmget(key,256, 0666 | IPC_CREAT);

shmaddr = shmat(shmid, NULL, 0);

sops.sem_num = 0; sops.sem_flg = 0;

 

// запускаем цикл

while(st!=’Q’) {

printf(“Ждем открытия семафора \n”);

// ожидание положительного значения семафора

sops.sem_op=-2;

semop(semid, &sops, 1);

// будем ожидать, пока “значение семафора”+”значение sem_op” не перевалит за

0, то есть если придет “3”, то “3-2=1”

// теперь значение семафора равно 1

st = shmaddr[0];

{ /*критическая секция - работа с разделяемой памятью - в этот момент

первый процесс к разделяемой памяти доступа не имеет*/}

/* после работы - закроем семафор */

sem.sem_op=-1;

semop(semid, &sops, 1);

// вернувшись в начало цикла мы опять будем ждать, пока значение семафора не станет больше нуля

}

shmdt(shmaddr); // освобождаем разделяемую память и выходим

exit(0);

}

 

Механизм сокетов

Средства межпроцессного взаимодействия ОС Unix, представленные в системе IPC, решают проблему взаимодействия двух процессов, выполняющихся в рамках одной операционной системы.

Но необходимы:

• Унифицированный механизм, позволяющий использовать одни и те же подходы для локального и нелокального взаимодействия.

• Общий интерфейс, позволяющий пользоваться услугами различных протоколов по выбору пользователя.

 

Эти проблемы решает механизм сокетов (sockets)

Сокеты подразделяются на несколько типов в зависимости от типа коммуникационного соединения, который они используют. Два основных типа коммуникационных соединений и, соответственно, сокетов представляет собой соединение с использованием виртуального канала и дейтаграммное соединение.

1)

 

Соединение с использованием виртуального канала (TCP) – это последовательный поток байтов, гарантирующий надежную доставку сообщений с сохранением порядка их следования. Данные начинают передаваться только после того, как виртуальный канал установлен, и канал не разрывается, пока все данные не будут переданы. Примером соединения с установлением виртуального канала является механизм каналов в UNIX, аналогом такого соединения из реальной жизни также является телефонный разговор. Заметим, что границы сообщений при таком виде соединений не сохраняются, т.е. приложение, получающее данные, должно само определять, где заканчивается одно сообщение и начинается следующее. Такой тип соединения может также поддерживать передачу экстренных сообщений вне основного потока данных, если это возможно при использовании конкретного выбранного протокола.

2) Детаграммное соединение (UDP) используется для передачи отдельных пакетов, содержащих порции данных – дейтаграмм. Для дейтаграмм не гарантируется доставка в том же порядке, в каком они были посланы. Вообще говоря, для них не гарантируется доставка вообще, надежность соединения в этом случае ниже, чем при установлении виртуального канала. Однако дейтаграммные соединения, как правило, более быстрые. Примером дейтаграммного соединения из реальной жизни может служить обычная почта: письма и посылки могут приходить адресату не в том порядке, в каком они были посланы, а некоторые из них могут и совсем пропадать.

Поскольку сокеты могут использоваться как для локального, так и для удаленного взаимодействия, встает вопрос о пространстве адресов сокетов. При создании сокета указывается т.н. коммуникационный домен, к которому данный сокет будет принадлежать. Коммуникационный домен определяет форматы адресов и правила их интерпретации. Мы будем рассматривать два основных домена:

домен AF_UNIX для локального взаимодействия

домен AF_INET для взаимодействия в рамках сети (префикс AF обозначает сокращение от address family – семейство адресов).

В домене AF_UNIX формат адреса – это допустимое имя файла, в домене AF_INET адрес образуют имя хоста + номер порта.

Далее мы рассмотрим принципиальный набор функций, которые предоставляются для работы с сокетами.

 

Для использования всех приведенных далее функций необходимо подключить следующие include-файлы:

#include <sys/types.h>

#include <sys/socket.h>

 

1) Создание сокета

int socket (int domain, int type, int protocol);

domain – константа, определяющая коммуникационный домен, который будет использоваться для взаимодействия, в частности значение AF_UNIX определяет домен локального межпроцессного взаимодействия внутри одной UNIX-системы, а значение AF_INET соответствует домену взаимодействия удаленных систем с использованием TCP/IP.

type определяет тип соединения (сокета). В частности type может принимать значения SOCK_STREAM, что соответствует сокету потока или виртуального канала, и SOCK_DGRAM, если мы хотим установить дейтаграммное соединение.

protocol – код протокола, который будет использоваться для взаимодействия. Значение IPPROTO_TCP используют, если тип сокета – виртуальный канал, IPPROTO_UDP – если речь идет о дейтаграммном сокете. Если protocol=0, то система сама подберет подходящий протокол.

Функция возвращает дескриптор сокета - некоторое положительное число в случае успешной работы и «-1» в случае возникновения проблем (в errno – причина).

 

2) Функция связывания

int bind(int sockfd, struct sockaddr * myaddr, int sizeaddr);

где sockfd – дескриптор сокета, который вернула функция socket.

sizeaddr – размер той структуры в байтах, на которую указывает второй аргумент (myaddr). Использование этого аргумента обусловлено тем, что реально в зависимости от домена форматы структур могут быть разными, например, в файле sys/un.h:

Struct sockaddr_un

{

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...