
- •1. Процессы, коммуникация и координация в распределенных системах
- •1.1. Процессы
- •1.1.1. Структуры действий как процессы
- •1.1.2. Структурирование процессов
- •1.1.3. Последовательное представление процессов с помощью трасс
- •1.1.4. Рашуженис процесса на подпроцессы
- •1.1.5. Действия как переходы состояний
- •1,2. Описания систем через множество процессов
- •1.2.1. Сети Петри
- •1.2.2. Термы для описания процессов
- •1.2.3. Синхронизация и координация агентов
- •1.2.4. Предикаты над процессами
- •1.3. Языки программирования для описания взаимодействующих систем
- •1.3.1. Коммуникация через обмен сообщениями
- •1.3.2. Общие программные переменные
- •1.3.3. Языковые средства для параллельных ходов работы
- •1.3.4. Потоки ввода/вывода
- •2.1. Основные аспекты операционных систем
- •2.1.1. Функции операционной системы
- •2.1.2. Режимы обработки
1.3. Языки программирования для описания взаимодействующих систем
Для распределенных систем особое значение имеют действия, которые служат для обмена информацией (коммуникация и координация) между разделенными (параллельно) работающими компонентами системы. Даже синхронизирующие действия могут трактоваться как виды обмена информацией (над управляющими состояниями). Языки программирования для описания процессов содержат, в частности, операторы для обмена сообщениями между параллельно выполняющимися программами.
1.3.1. Коммуникация через обмен сообщениями
Обмен сообщениями между двумя компонентами в параллельно рабо таюшей системе может в принципе достигаться с помощью двух допол нительных действий: отправления и получения. В одном процессе отправление/получение могут быть задействованы многие партнеры. На пример, один отправитель может одновременно посылать сообщение многим получателям (адресатам) (англ. broadcasting). Однако часто огра ничиваются системами, в которых для каждого пересылаемого в системе сообщения существует единственный отправитель и самое большее один адресат.
Важным аспектом при обмене сообщениями является концепция адресации получателя. При посылке сообщения во всяком случае должен быть специфицирован получатель или крут возможных получателей. Аналогично при получении должен быть специфицирован желаемый отправитель или круг отправителей. При попытке получить сообщение (точнее, при опросе, имеется ли налицо сообщение) должен быть назван отправитель или крут возможных отправителей. Существуют самые различные способы адресации сообщений. Например, для адресации могут использоваться имена (метки) параллельно выполняющихся программ.
Каналы для обмена сообщениями типа ш объявляются аналогично о&ьяатеншо программных переменных:
channel ш с
Среда передачи сообщений, которая служит для установления связи между отправителем и получателем, носит название канал. С помощью каналов могут адресоваться сообщения. Отправитель в действии отправки называет не имя получателя, а только имя канала. Аналогично обстоит дело и для получателя. В процедурном ("императивном", т. е. ориентированном на присваивания) языке действия пересылки по каналу с выражаются с помощью оператора
send Е on с
где Е - любое выражение типа ш ; с - имя канала.
В дополнение к этому действие приема сообщения выражается оператором
receive х on с
где х - идентификатор программной переменной типа m ; с - имя канала.
Если к моменту выполнения действия приема еще отсутствует посланная информация, то процесс "ждет" до поступления сообщения. Если пересылка никогда не последует, то действие приема не завершается, что соответствует .локальному тупику.
Мы расширим синтаксис для формулирования императивной программы, как он был описан в части I, до описания параллельной композиции ||, описания каналов, операторов посылки и приема сообщений. Возникает расширенный процедурный язык, который может использоваться для формулирования параллельно выполняющихся программ, обменивающихся сообщениями.
Пример (производитель/потребитель). Следующая схема программы состоит из двух параллельно выполняющихся последовательных программ, из которых вторая посылает сообщения первой.
Г channel m с;
var m х, var m z := xo, zo; fT while —b(x) do receive x on c;
consume (x)
od
II
while —b(z) do produce_next(z); send z on с
od il J
Здесь consume и poduce_next - заданные процедуры, a b - булевская функция. Из этой схема можно получить, например, параллельную программу' дтя вычисления функции факториал - за счет того, что мы выберем следующие конкретные процедуры:
consume(х) н if х * 0 then г := х «г fi ,
produce_next(z) = z:=z~ I, b(x) = (x = 0) .
Дополнительно пусть г - программная переменная, которой первоначально присвоено значение 1, и пусть справедливо
х0 = zo = п+1.
Тогда после выполнения программы имеет место г = п!.
Символ IT стоит в качестве parbegin, т. е. обозначения начала раздела, который состоит из некоторого числа параьлельно выполняющихся операторов. Эти операторы разделяются символом ||. Раздел завершается символом JJ, который стоит в качестве parend - этот символ помечает конец раздела, состоящего из параллельных операторов.
Различают два основных способа обмена сообщениями между отправителем и получателем;
(1) неявные буферы: готовый к посылке передающий процесс не додже!) ждать готовности к приему принимающего процесса. Сообщение посылается немедленно, и в случае необходимости оно помещается на промежуточное хранение, после чего посылающий процесс продолжает свою работу. Соответствующие события отправителя и получателя находятся в причинной связи. Посылка является причиной для получения;
(2) рандеву ("рукопожатие"): посылающий процесс ждет, пока принимающий процесс будет готов к приему сообщения. Обмен информацией имеет место в событии, после чего оба процесса продолжают свою работу. Обмен сообщениями соответствует общему событию.
В случае (1) говорят об асинхронной, а в случае (2) - о синхронной передаче. Необходимо тщательно различать эти две концепции, так как взаимодействующие программы могут проявлять различное поведение в зависимости от того, какая концепция коммуникации ими поддерживается.
Р
I
S/E
/ \
С Р
\ /
S/E
/ \
С Р
\ /
Рис. 1.34. Процесс производитель/потребитель с синхронной пересылкой/получением
Для обсуждения различных концепций обмена сообщениями введем две синтаксические формы. В дальнейшем мы будем использовать операторы
send Е on с
и соответственно
receive х on с
для асинхронного обмена сообщениями с неявными буферами, и будем писать
Е ! с
для синхронного действия посылки и X ? с
для синхронного действия приема при обмене сообщениями через рандеву.
При этом соглашении наш пример производитель/потребитель в нотации рандеву может быть записан следующим образом:Г channel ш с;
var m x, var m z := xo, z0; [Г while -'b(x) do x ? c;
consume (x)
od
while ~^b(z) do produce_next(z); z ! с
od
Дтя задачи производитель/потребитель шггерпретапия рандеву допускает по сути дела только единственный ход работы, показанный на рис. 1.34 (где С обозначает действие coasume(x), Р - действие produce_next(z), а S/E - общее действие обмена сообщением z ! с/х ? с).
Неявные буферы допускают значительно больший параллелизм, чем синхронный обмен сообщениями. Для случая производитель/потребитель мы получаем процесс, заданный на рис. 1.35.
Р Е
I J
S с
IV
Е
С
Рис. 1.35. Процесс производитель/потребитель с асинхронным обменом сообщениями
Единственное ограничение в параллельной работе обоих последовательных процессов здесь состоит в том, что сообщение не может быть принято до того, как оно будет послано. Действие посылки является причинным предположением для соответствующего действия по приему сообщения.
il
J
грамме Р', осуществляющей обмен по принципу рандеву, то получается следующее отношение между этими программами:
Каждый свободный от тупиков ход работы программы р' есть линеаризация хода работы р, причем действия посылки/приема сообщений могут толковаться как одно действие.
Определенные ходы работы р и их линеаризации исключаются как ходы работы р'.
Р', возможно, приведет к тупикам, которые исключены для Р.
Неявные буферы реже приводят к тупикам, однако требуется больше места в памяти для промежуточного хранения сообщений.
Пример (синхронная и асинхронная коммуникация). Рассмотрим следующие две программы:
[Г 1 ! с 1; х ? с2 II 2 ! с2; у ? с 1 JJ
IT send 1 on cl; receive x on c2 || send 2 on c2; receive у on cl 11
Программа (1) приводит к тупику', поскольку оба процесса ждут отправителя, тогда как программа (2) свободна от тупиков. С
При концепции неявного буфера система параллельных программ может завершить работу даже в том случае, когда приняты не все из посланных сообщений. При концепции рандеву это не имеет места. Вопрос о том, какой способ обмена сообщениями является более подходящим, существенно зависит от применений.
Через концепцию канала для действий посылки/приема сообщений с самого начала не устанавливается какого-либо однозначного отношения между отправителем и получателем - и тот и другой обращаются к каналам. Какой получатель и какое сообщение в конце концов он получит в случае многих одновременных операций посылки и многих готовых к приему получателей, программой не предопределяется. Поэтому снова речь идет о недетерминированности.
Пример (недетерминированность при посылке/приеме). В программе
Г var nat xl, х2 := 0, 0; channel nat с;
IT send 1 on с I send 2 on с [| receive xl on с || receive x2 on с JJ J
не устанавливается, будет ли после ее выполнения достигнуто состояние, для которого справедливо высказывание
(xl = 1 л х2 = 2)
или состояние, для которого справедливо высказывание
(х2=1лх1 = 2). □
Недетермшшрованность возникает типичным образом при обмене сообщениями, когда многие отправители или получатели параллельно хотят обмениваться по одному канату. Канал ведет к линеаризации обмена сообщениями и тем самым к, возможно, недетерминированному упорядочению.