Н. Вирт - Программирование на языке Модула-2 (1160777), страница 25
Текст из файла (страница 25)
Предположим, например, что последовательностьслов должна читаться из файла и загружаться в память с адресами нач, нач+1,... . Пусть опятьпервое слово задает длину последовательности. Функция ADR(x) дает адрес переменной х, где хможет иметь любой тип.ReadWord (in, длина);длина = длина - 1; а := ADR(буфер);WHILE длина > 0 DOReadWord(in,a^); a := а + 1; длина := длина - 1ENDТипы WORD и ADDRESS тоже импортируются из модуля SYSTEM. Это значит, что взаголовках программ, использующих эти типы низкого уровня, будет явное упоминание об этом.Модуль SYSTEM содержит типы данных и связанные с ними процедуры, обработка которыхпроисходит по особым правилам, известным компилятору.
Следовательно, этот модуль тесносвязан с конкретным компилятором и его нельзя описать в виде отдельного модуля. По этойпричине он называется псевдомодулем. Но можно тем не менее считать, что он описываетсяследующим модулем определений:DEFINITION MODULE SYSTEM;TYPE WORD,ADDRESS;PROCEDURE ADR(x: ЛюбойТип): ADDRESS;(* адрес переменной х *)PROCEDURE TSIZE(ЛюбойТип): CARDINAL;(* длина объекта данного типа в словах *)PROCEDURE NEWPROCESS(P: PROC: A: ADDRESS;n: CARDINAL; VAR q: ADDRESS);PROCEDURE TRANSFER(VAR кто,кому: ADDRESS);126…END SYSTEM.Точки означают, что в модуле могут содержаться и другие средства, зависящие отконкретной реализации.Реализации. Модулы могут, но не обязаны предоставлять возможность задать дляпеременной Фиксированный адрес.
Если эта возможность имеется, то адрес задаетсянепосредственно за идентификатором переменной в ее описании. Примеры приводятся вследующем разделе.30. ПАРАЛЛЕЛЬНЫЕ ПРОЦЕССЫ И СОПРОГРАММЫВ этом разделе введем ряд понятий мультипрограммирования, т.е. программированияразличных, параллельно выполняемых вычислений. Применение этих средств намеренноограничено областью так называемых слабо связанных процессов. Мы исключаем из рассмотрениясильно связанные массивы процессов, считая эту область слишком узкой и специальной. Вместоэтого ограничимся рассмотрением программ, описывающих несколько процессов,взаимодействующих сравнительно редко и поэтому называемых слабо связанными.
Под словом"редко" подразумевается, что взаимодействия происходят в немногих - четко определенных и явновыделенных точках программы, а слово "процесс" означает последовательность действий, т.е.принципиально последовательный процесс. Программирование в том виде, в котором мы егоизучали до сих пор, может, следовательно, рассматриваться как частный случайпрограммирования, включающий лишь один процесс. И наоборот, при мультипрограммированииможно использовать все средства и методы, изученные до сих пор, и требуется лишь простодобавить несколько новых средств описания параллельных процессов и их взаимодействия. Вэтом отношении мы следуем традиции ранних языков мультипрограммирования, таких, какМодула-1 и Concurrent Pascal (Параллельный Паскаль), разработанный Бринчем Хансеном.В общем случае следует выделять следующие типы систем мультипрограммирования.1.
ЭВМ содержит нисколько одинаковых процессоров. Запрограммированные процессывыполняются в истинном параллельном режиме.2. ЭВМ содержит единственный процессор. В каждый момент времени выполняется лишьодин процесс, и время от времени происходит переключение процессов, т.е. мультиплексирование127по времени. Более общий случай - когда в системе, содержащей m процессоров, выполняются ппроцессов, причем m обычно меньше, чем п. Такой режим называется квазипараллельным.3. На нескольких процессорах с различными возможностями выполняются разныепроцессы. Некоторые из этих процессов таковы, что в них есть Фрагменты, которые можновыполнять только на определенных процессорах.
Типичным примером таких специализированныхпроцессоров являются устройства ввода-вывода.Наша цель - определить систему понятий и нотацию, которые позволят выразить общиеаспекты всех трех перечисленных типов систем в одинаковых терминах и на высоком уровнеабстракции. Можно с некоторой степенью приближения считать, что различие между типами 1 и 2- это лишь вопрос реализации. Точнее, если мы запишем логические процессы и ихвзаимодействие таким образом, что они смогут выполняться в режиме истинной параллельности,то система, построенная на одном процессоре, может использоваться для выполнения этихпроцессов в квазипараллельном режиме.
Случай 3 требует особого отношения, посколькуочевидно, что наличие процессоров со специфическими возможностями не может быть скрытопросто как деталь реализации.В этой главе мы обсудим описание процессов и их взаимодействия в терминах Модулы-2.Короме того, дадим реализацию, опирающуюся на однопроцессорную машину, и понятиесопрограммы, т.е. такую систему, которая реализует квазипараллельность. Программирование дляспециализированных устройств (ввода и вывода) и их работа в режиме истинной параллельностиописаны в следующем разделе. Для описания параллельных процессов введен модуль Processes(процессы). Его достоинство в том, что он содержит средства мультипрограммирования навысоком уровне абстракции, причем для этого Фактически не требуется никаких дополнительныхязыковых средств. Он предоставляет все возможности Модулы-1, а также большинствовозможностей языка Concurrent Pascal.DEFINITION MODULE Processes;TYPE SIGNAL;PROCEDURE StartProcess(P: PROC; n: CARDINAL);(* начать параллельный процесс, задаваемый программой Р с рабочей областью размеромп.
PROC - стандартный тип, определенный как PROC = PROCEDURE *)PROCEDURE SEND(VAR s: SIGNAL);(* возобновляется один из процессов, ждущих s *)PROCEDURE WAIT(VAR s: SIGNAL);(* ждать, пока другой процесс не пошлет сигнал s *)PROCEDURE Awaited(s: SIGNAL): BOOLEAN;(* Awalted(s) ="по крайней мере один процесс ждет s" *)PROCEDURE InlUVAR s: SIGNAL);128(* обязательная инициализация *)END Processes.Вызов StartProcess(P,n) начинает выполнение процесса, который выражается процедурой Р.Будет ли этот процесс выполняться параллельно или квазипараллельно, зависит, конечно, отреализации используемого модуля Processes.
Каждый процесс требует рабочей области.определенного размера для размещения своих локальных переменных. Размер рабочей области всловах задается параметром п. Его величина зависит от числа локальных переменных и локальныхвызовов, использованных в этом процессе. (Размер типичной минимальной рабочей области - 100слов.)Связь между процессами осуществляется двумя различными способами: посредствомобщих, разделяемых перемепнных и посредством так называемых сигналов. Общие переменныеиспользуются для передачи данных между процессами, и здесь возникает проблемасогласованного взаимодействия. Когда некоторый процесс осуществляет какие-либо действия наднекоторой общей переменной, то нельзя, чтобы в этот момент ее использовал или менял ещекакой-то процесс.
Разумным решением проблемы было бы заключить общие переменные вмодуль, который гарантировал взаимное исключение процессов. Такой модуль называетсямонитором, он будет обсуждаться ниже. Сигналы, экспортируемые как тип данных измодуля Processes, сами по себе не несут информации, а служат для синхронизации. К сигналамприменимы две операции (не считая обязательной начальной инициализации): процесс можетпослать сигнал и может ждать сигнала (посланного другим процессом.) Каждый сигналобозначает возникновение некоторого условия. Мы настоятельно рекомендуем указывать этоусловие в виде комментария при описании сигнала. Посылка сигнала возобновляет не болееодного процесса.
(В противном случае один из возобновляемых процессов мог бы быстронарушить условие и тогда другие процессы работали бы с неверной предпосылкой, считая условиеистинным.) Посылка сигнала, если его не ждет ни один процесс, эквивалентна пустому оператору.Программист должен понимать, не заботясь о деталях реализации, что в системах,использующих квазипараллельность, вызовы SEND и WAIT подразумевают (или могутподразумевать) переключение вызывающего процесса на другой (ждущий) процесс, и что этоединственные места, где такое переключение может происходить. Следовательно, ожиданиенаступления определенного события нельзя программировать с использованием пустых циклов(так называемого ожидания занятого).
а только используя явные вызовы WAIT.Другое важное правило программирования состоит в том, что разделяемые переменныеописываются и скрываются в мониторе. Монитор - модуль, который гарантирует взаимноеисключение процессов и тем самым обеспечивает целостность своих локальных данных. Доступ кэтим данным (поскольку они скрыты) ограничен лишь операторами вызова процедур(экспортируемых из) монитора, а так как монитор гарантируат, что процесс, вызывающий егопроцедуру, временно задерживается, пока другой процесс выполняет любую из процедурмонитора, тем самым автоматически достигается взаимное исключение. Модуль специфицируетсякак монитор указанием в его заголовке приоритета.
Значение приоритета -число типа CARDINAL.Здесь достаточно знать, что указание любого приоритета делает модуль монитором.Следующий пример призван проиллюстрировать приведенные правила. Oн решает одну изклассических задач мультипрограммирования: задачу обмена данными между разнымипроцессами. Обычно при решении таких задач используется буфер. Чем больше буфер, тем слабеепроцессы связаны. Мы считаем, что процессы могут помещать элементы данных в буфер иизвлекать их оттуда. Буфер - принципиально разделяемая переменная. Вместе с процедурамиПоместить и Извлечь он изолирован в мониторе.
Поскольку мы не знаем, (не должны и не хотимзнать) частностей рассматриваемых процеосов, обратимся непосредственно к ключевой проблемемультипрограммирования, к монитору, через который осуществляется взаимодействие процессов.Сами процессы содержат вызовы процедур Поместить и Извлечь и обычно являютсяциклическими. Этими вызовами и ограничивается их взаимодействие. Если процесс содержитвызовы процедуры Поместить, то это поставщик. Процессы, содержащие вызовы Извлечь,129называются потребителями.
В нашем примере буфер описан как переменная-массив, используемаяциклическим образом. Могут возникать два условия для ожидания: при вызове поставщикомпроцедуры Поместить буфер может оказаться полон, а при вызове потребителем Извлечь пустым.