Рефераты. Interprocess Communication p> Мы описали два средства взаимодействия между процессами. Что же мы увидели? Понятно, что названия и описания интерфейсов мало понятны. Прежде всего следует заметить то, что как только мы переходим к вопросу взаимодействия процессов, у нас возникает проблема синхронизации. И здесь мы уже видим проблемы, связанные с тем, что после того, как мы поработали с разделяемой памятью или очередью сообщений, в системе может оставаться
“хлам”, например, процессы, которые ожидают сообщений, которые в свою очередь не были посланы. Так, если мы обратились к функции получения сообщений с типом, которое вообще не пришло, и если не стоит ключ
IPC_NOWAIT, то процесс будет ждать его появления, пока не исчезнет ресурс.
Или мы можем забыть уничтожить ресурс (и система никого не поправит) - этот ресурс останется в виде загрязняющего элемента системы.

Когда человек начинает работать с подобными средствами, то он берет на себя ответственность за все последствия, которые могут возникнуть. Это первый набор проблем - системная синхронизация и аккуратность. Вторая проблема - синхронизация данных, когда приемник и передатчик работают синхронно. Заметим, что самый плохой по синхронизации ресурс из рассмотренных нами - разделяемая память. Это означает, что корректная работа с разделяемой памятью не может осуществляться без использования средств синхронизации, и, в частности, некоторым элементом синхронизации может быть очередь сообщений. Например, мы можем записать в память данные и послать сообщение приемнику, что информация поступила в ресурс, после чего приемник, получив сообщение, начинает считывать данные. Также в качестве синхронизирующего средства могут применяться сигналы.

И это главное - не язык интерфейсов, а проблемы, которые могут возникнуть при взаимодействии параллельных процессов.

Лекция №18

К сегодняшнему дню мы разобрали два механизма взаимодействия процессов в системе IPC - это механизм общей (или разделяемой) памяти и механизм сообщений. Мы с вами выяснили, что одной из основных проблем, возникающей при взаимодействии процессов, является проблема синхронизации. Ярким примером механизма, для которого эта проблема является наиболее острой, является механизм взаимодействия процессов с использованием разделяемой памяти.

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

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

Следующий механизм, который мы с вами рассмотрели - очередь сообщений.
Имеется возможность совместной работы с разделяемым объектом, который называется очередь сообщений. Имеется сообщение, которое состоит из некоторого спецификатора типа, и некоторого набора данных. Процесс, подтвердив ключ и имея права доступа к этому разделяемому ресурсу, может осуществлять действия по записи сообщений в очередь, и по чтению сообщений из очереди.

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

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

Итак, к сегодняшнему дню мы познакомились с двумя этими механизмами.
Напомню, как только мы переходим к работе от однопроцессной задачи к задаче многопроцессной, у нас сразу же возникают проблемы, связанные с тем, что любой параллелизм накладывает определенную ответственность на программу.
Это ответственность по синхронизации доступа к разделяемой памяти, ответственность за правильность подпрограммы, занимающейся приемом и передачей сообщений и т.д. Можно, например, ошибиться в механизме передачи и приема сообщений за счет того, что какой-то процесс будет бесконечно долго ожидать несуществующее сообщение, то, которое никогда в очереди не появится, и система вам никогда такую ошибку не зафиксирует. Т.е. возможны зависания процессов, могут образоваться неосвобожденные ресурсы ("мусор"), и это приводит к деградации системы.

Сейчас мы напишем следующую программу: первый процесс будет читать некоторую текстовую строку из стандартного ввода и в случае, если строка начинается с буквы 'a', то эта строка в качестве сообщения будет передана процессу А, если 'b' - процессу В, если 'q' - то процессам А и В и затем будет осуществлен выход. Процессы А и В распечатывают полученные строки на стандартный вывод.

Основной процесс
#include
#include
#include
#include struct { long mtype; /* тип сообщения */ char Data[256]; /* сообщение */

} Message;

int main()
{ key_t key; int msgid; char str[256]; key=ftok("/usr/mash",'s'); /*получаем уникальный ключ, однозначно определяющий доступ к ресурсу данного типа */ msgid=msgget(key, 0666 | IPC_CREAT); /*создаем очередь сообщений , 0666 определяет права доступа */ for(;;) { /* запускаем вечный цикл */ gets(str); /* читаем из стандартного ввода строку
*/ strcpy(Message.Data, str); /* и копируем ее в буфер сообщения
*/ switch(str[0]){ case 'a': case 'A': Message.mtype=1; /* устанавливаем тип и посылаем сообщение в очередь*/ msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str)+1, 0); break; case 'b': case 'B': Message.mtype=2; msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str)+1, 0); break; case q': case 'Q': Message.mtype=1; msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str)+1, 0);

Message.mtype=2; msgsnd(msgid, (struct msgbuf*) (&Message), strlen(str)+1, 0); sleep(10); /* ждем получения сообщений процессами А и В */ msgctl(msgid, IPC_RMID, NULL); /* уничтожаем очередь*/ exit(0); default: break;

}
}
}
Процесс-приемник А /* процесс В аналогичен с точностью до четвертого параметра в msgrcv */
#include
#include
#include
#include struct { long mtype; char Data[256];

} Message;

int main()
IPC_CREAT); /*создаем очередь сообщений */ for(;;) Message.Data[0]='Q') break; printf("%s",Message.Data);
exit(0);

Семафоры

С точки зрения тех проблем, с которыми мы знакомимся, семафоры - это есть вполне законное и существующее понятие. Впервые ввел это понятие достаточно известный ученый Дейкстра. Семафор - это некоторый объект, который имеет целочисленное значение S, и с которым связаны две операции:
V(S) и P(S).

Операция P(S) уменьшает значение семафора на 1, и если S(0 процесс продолжает работу. Если S0, то процесс продолжает выполнение. Если S(0, то разблокируется один из процессов, ожидающий в очереди процессов, связанной с семафором S, и текущий процесс продолжит выполнение.

Считается, что операции P(S) и V(S) неделимы. Это означает, что выполнение этих операций не может прерваться до их завершения. Т.е. если семафор реализован в системе, то это должна быть одна команда.

Частным случаем продекларированного семафора является двоичный семафор, максимальное значение которого равно единичке. При этом значение S может быть равно 1, это означает, что ни один из процессов (связанных с этим семафором) не находится в критическом участке. При S=0 один из процессов находится в критическом участке {вот-вот попадет в очередь}, а другой нормально функционирует. При S= -1 один из процессов находится в критическом участке, а другой заблокирован и находится в очереди.

На самом деле двоичные семафоры наиболее часто находили применение в аппаратных реализациях, например, в многомашинных комплексах с общей оперативной памятью.

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

Давайте рассмотрим средства, предоставляемые системой IPC, для создания, управления и взаимодействия с семафорами.

int semget(key_t key, int n, int flags); int semop(int semid, struct sembuf * sops, int n);

struct sembuf{ short sem_num; /* номер семафора в массиве семафоров */ short sem_op; /* код операции, которую надо выполнить */ short sem_flg; /* флаги */

}

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

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

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

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

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

-1).

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

Страницы: 1, 2, 3, 4, 5, 6, 7, 8



2012 © Все права защищены
При использовании материалов активная ссылка на источник обязательна.