KRIEGSSPIELE!
Вы хотите отреагировать на этот пост ? Создайте аккаунт всего в несколько кликов или войдите на форум.

Бек. Введение в системное программирование. 1988

Страница 2 из 2 Предыдущий  1, 2

Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Пт Фев 12, 2021 11:25 am

ГЛАВА 5. КОМПИЛЯТОРЫ

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

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

В разд. 5.2 обсуждаются машинно-зависимые расширения базовой схемы, представленной в разд.5.1. Эти расширения относятся в основном к генерации объектного кода и оптимизации. В разд.5.3 описаны некоторые машинно-независимые дополнения к базовой схеме.

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

5.1. ОСНОВНЫЕ ФУНКЦИИ КОМПИЛЯТОРА

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

Бек. Введение в системное программирование. 1988 - Страница 2 8822410
Рис.5.1. Пример программы на Паскале.

Для облегчения построения компиляторов язык высокого уровня обычно описывается в терминах некоторой грамматики. Эта грамматика определяет форму (синтаксис) допустимых предложений языка. Например, оператор присваивания может быть определен в грамматике как имя переменной, за которой следует оператор присваивания (:=), за которым следует выражение. Проблема компиляции может быть сформулирована как проблема поиска соответствия написанных программистом предложений структурам, определенным грамматикой, и генерации соответствующего кода для каждого предложения [Автор придерживается термина "компилятор" и не применяет более широко используемый у нас термин "транслятор".- Прим. ред.].

Предложения исходной программы удобнее представлять в виде последовательности ЛЕКСЕМ (tokens), чем просто как строку символов. Лексемы можно понимать как фундаментальные кирпичики, из которых строится язык. Например, лексемой может быть ключевое слово, имя целой переменной, арифметический оператор и т.д. Просмотр исходного текста, распознавание и классификация различных лексем называются ЛЕКСИЧЕСКИМ АНАЛИЗОМ. Часть компилятора, которая выполняет эту функцию, обычно называют СКАНЕРОМ [В нашей литературе принят термин "лексический анализатор".- Прим. ред.].

Как только лексемы выделены, каждое предложение программы может быть распознано как некоторая конструкция языка, как, например, декларативные операторы или оператор присваивания, описанные с помощью грамматики. Процесс, называемый СИНТАКСИЧЕСКИМ АНАЛИЗОМ или СИНТАКСИЧЕСКИМ РАЗБОРОМ, осуществляется той частью компилятора, которая обычно называется СИНТАКСИЧЕСКИМ АНАЛИЗАТОРОМ (parser). Последним шагом базовой схемы процесса трансляции является генерация объектного кода. Большинство компиляторов генерирует непосредственно программу в машинных кодах, а не программу на ассемблере, предназначенную для последующей трансляции в машинные коды.

Хотя мы указали на три основных шага в процессе компиляции - лексический анализ, синтаксический анализ и генерацию кодов,- важно отметить, что компилятор вовсе не обязан делать три просмотра транслируемой программы. Для некоторых языков возможна компиляция программы за один просмотр. В этом разделе мы обсудим, как может работать однопросмотровый компилятор. Однако компиляторы для других языков и компиляторы, осуществляющие изощренную оптимизацию объектного кода или какой-либо другой анализ программы, обычно являются многопросмотровыми. Мы обсудим разбиение процесса компиляции на отдельные просмотры в разд.5.4. В разд.5.5 приводится несколько примеров построения реальных компиляторов.

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

5.1.1. ГРАММАТИКИ

[В некоторых работах под грамматикой понимают понятие, объединяющее синтаксис и семантику.- Прим. ред.]

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

I := J + K

и

I := X + Y

где X и Y являются действительными переменными, a I, J, K - целыми переменными. Эти два предложения имеют одинаковый синтаксис. Оба являются операторами присваивания, в которых присваиваемое значение определяется выражением, состоящим из двух имен переменных, разделенных оператором сложения. Однако семантика этих двух предложений совершенно различна. Первое предложение говорит о том, что переменные в выражении должны быть сложены с использованием целых арифметических операций, а результат сложения должен быть присвоен переменной I. Второе предложение задает сложение с плавающей точкой, результат которого должен быть преобразован в целое число перед присваиванием. Очевидно, эти два предложения будут скомпилированы в совершенно различные последовательности машинных команд, хотя их грамматическое описание одинаково. Различия между ними проявятся на этапе генерации объектного кода.

Существует несколько различных форм записи грамматик, среди которых мы рассмотрим форму Бекуса-Наура (БНФ). БНФ не самое мощное из известных средств описания синтаксиса. Фактически она даже не является вполне адекватной для описания некоторых реально существующих языков программирования. Однако эта форма достаточно проста, широко используется и предоставляет достаточные для большинства приложений средства. На рис.5.2 изображена одна из возможных грамматик БНФ для очень узкого подмножества языка Паскаль. Полное описание грамматики БНФ для Паскаля содержится в книге Йенсен [1974]. В этом разделе нам осталось обсудить эту грамматику и показать, как она связана с примером программы, изображенным на рис.5.1.

Бек. Введение в системное программирование. 1988 - Страница 2 8822610
Рис. 5.2. Упрощенная грамматика Паскаля.

Грамматика БНФ состоит из множества правил вывода, каждое из которых определяет синтаксис некоторой конструкции языка программирования. Рассмотрим, например, правило 13 на рис.5.2:

<read> ::= READ ( <id - list> )

Это определение синтаксиса предложения READ языка Паскаль, обозначенное в грамматике как <read>. Символ ::= можно читать как "является по определению". С левой стороны от этого символа находится определяемая конструкция языка (в нашем случае - <read>), а с правой - описание синтаксиса этой конструкции. Строки символов, заключенные в угловые скобки < и >, называются НЕТЕРМИНАЛЬНЫМИ СИМВОЛАМИ (т.е. являются именами конструкций, определенными внутри грамматики). То, что не заключено в угловые скобки, называется ТЕРМИНАЛЬНЫМИ СИМВОЛАМИ грамматики (лексемами). В этом правиле вывода нетерминальными символами являются (read) и <id-list>. Терминальными символами являются лексемы READ, (, ). Таким образом, это правило определяет, что конструкция <read> состоит из лексемы READ, за которой следует лексема (, за ней следует конструкция языка, называемая <id-list>, за которой следует лексема ). Пробелы при описании грамматических правил не существенны и вставляются только для наглядности, Для распознавания нетерминального символа (read) необходимо чтобы было определение для нетерминального символа <id-list>. Это определение дается правилом 6 на рис.5.2:

<id-list> ::= id | <id-list> , id

Это правило предлагает две возможности, разделенные символом |, для синтаксиса нетерминального символа <id-list>. Первая возможность состоит в том, что <id-list> состоит просто из лексемы id (запись id означает идентификатор, распознаваемый сканером). Другой вариант состоит в том, что <id-list> состоит из <id-list>, за которым следует лексема ",", за которой следует лексема id. Обратите внимание, что это правило является рекурсивным. Это означает, что конструкция <id-list> определяется фактически в терминах себя самой. Рассмотрев несколько примеров, вы убедитесь, что в соответствии с этим правилом, нетерминальным символом <id-list> является любая последовательность из одной или более лексем id, разделенных запятыми. Таким образом,

ALPHA

является нетерминальным символом <id-list>, состоящим из единственного идентификатора

ALPHA-ALPHA, BETA

также являются конструкцией <id-list>, состоящей из конструкции <id-list> ALPHA, за которой следует ",", за которой идет идентификатор BETA и т.д.

Результат анализа исходного предложения в терминах грамматических конструкций удобно представлять в виде дерева. Такие деревья обычно называют ДЕРЕВЬЯМИ ГРАММАТИЧЕСКОГО РАЗБОРА или СИНТАКСИЧЕСКИМИ ДЕРЕВЬЯМИ. На рис.5.3а изображено дерево грамматического разбора для предложения

READ (VALUE)

с использованием только что обсужденных двух правил вывода.

Бек. Введение в системное программирование. 1988 - Страница 2 8822810
Рис.5.3. Дерево грамматического разбора для двух предложений программы на рис.5.1.

Правило вывода 9 на рис.5.2 дает определение синтаксиса предложения присваивания:

<assign> ::= id := <exp>

Это означает, что конструкция <assign> состоит из лексемы id, за которой следует лексема :=, за которой идет конструкция <exp>. Правило 10 дает определение конструкции (ехр):

<ехр> ::= <term> | <ехр> + <term> | <exp> - <term>

Используя те же соображения, что и при анализе конструкции <id-list>, мы видим, что это правило определяет конструкцию <ехр> как состоящую из любой последовательности конструкций <term>, соединенных операторами плюс или минус. Аналогично правило 11 определяет конструкцию <term> как последовательность конструкций <factor>, разделенных, знаками * или DIV. В соответствии с правилом 12 конструкция <factor> может состоять из идентификатора id или целого int, которое также распознается сканером, или из конструкции <ехр>, заключенной в круглые скобки.

На рис.5.3б изображено дерево грамматического разбора для предложения 14 на рис.5.1, основанное на только что рассмотренных правилах вывода. Вы должны внимательно изучить этот рисунок, чтобы убедиться в правильном понимании того, как выполняется грамматический анализ исходного предложения на основании введенных грамматических правил. В разд.5.1.3 мы обсудим методы осуществления подобного синтаксического анализа компиляторами.

Обратите внимание, что в соответствии с деревом грамматического разбора на рис.5.3б умножение и деление производятся перед сложением или вычитанием. Прежде всего должны вычисляться термы SUMSQ DIV 100 и MEAN*MEAN, поскольку эти промежуточные результаты являются операндами (левым и правым поддеревом) операции вычитания. Иначе то же самое можно выразить, сказав, что операции умножения и деления имеют более высокий РАНГ (precedence), чем операции сложения и вычитания. Такое ранжирование операций является следствием того, как записаны правила 10-12 (см. упр.5.1.3). В разд.5.1.3 мы увидим, как подобное ранжирование может быть использовано при осуществлении процесса грамматического разбора.

Дерево грамматического разбора на рис.5.3 представляет собой единственно возможный результат анализа этих двух предложений в терминах грамматики, изображенной на рис.5.2. Для некоторых грамматик подобной единственности может не существовать. Если для одного и того же предложения можно построить несколько различных деревьев грамматического разбора, то соответствующая грамматика называется НЕОДНОЗНАЧНОЙ (ambiguous). При разработке компиляторов обычно предпочитают пользоваться однозначными грамматиками, поскольку в некоторых случаях неоднозначная грамматика оставляет сомнения относительно того, какой объектный код должен быть сгенерирован для анализируемого предложения (см., например, упр.5.1.4).

На рис.5.4 изображено дерево грамматического разбора для всей программы рис.5.1. Вы должны внимательно проанализировать этот рисунок, с тем чтобы понять, каким образом форма записи и структура программы соответствуют правилам вывода грамматики, приведенной на рис.5.2.

Бек. Введение в системное программирование. 1988 - Страница 2 8822910

Бек. Введение в системное программирование. 1988 - Страница 2 8823010
Рис.5.4. Дерево грамматического разбора для программы на рис.5.1.

5.1.2. ЛЕКСИЧЕСКИЙ АНАЛИЗ

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

<ident> ::= <letter> | <ident> <letter> | <ident> <digit>
<letter> ::= A | B | C | D | ... |Z
<digit> ::= 0 | 1 | 2 | 3 | ... | 9

В этом случае сканер будет распознавать в качестве отдельных лексем единичные символы A, B, 0, 1 и т.д. Далее в процессе грамматического разбора последовательность этих символов будет интерпретирована как конструкция языка <ident>. Однако в рамках такого подхода распознавание простых идентификаторов должны осуществлять общие алгоритмы грамматического разбора (например, описанные в следующем разделе). Специализированные программы сканера могут выполнить эту функцию гораздо более эффективно. Поскольку основная часть программы состоит из подобных многосимвольных идентификаторов, сокращение времени компиляции может быть весьма существенным. Кроме того, такие ограничения, как максимальная длина идентификатора, гораздо легче учесть в сканере, чем в общих алгоритмах грамматического разбора.

Аналогично сканер обычно распознает непосредственно как односимвольные, так и многосимвольные лексемы. Например, строку символов READ предпочтительнее рассматривать как отдельную лексему, нежели чем последовательность, состоящую из четырех лексем R, E, A, D. Строка := будет распознана как оператор присваивания, а не как символ :, за которым следует =. Возможен, конечно, и такой подход, когда многосимвольные лексемы рассматриваются как состоящие из отдельных лексем - символов, но это существенно усложняет процесс грамматического разбора.

Результатом работы сканера является последовательность лексем. Для повышения эффективности последующих действий каждая лексема обычно представляется некоторым кодом фиксированной длины (например, целым числом), а не в виде строки символов переменной длины. При подобной записи для грамматики, приведенной на рис.5.2, лексеме PROGRAM соответствует целое число 1, идентификатору id соответствует число 22 и т.д.

Бек. Введение в системное программирование. 1988 - Страница 2 8823310
Рис.5.5. Коды лексем для грамматики на рис.5.2.

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

На рис.5.6 изображен результат обработки сканером программы, приведенной на рис.5.1, с использованием кодировки лексем, представленной на рис.5.5. Для лексем типа 22 (идентификаторы) соответствующими спецификаторами являются указатели на таблицу символов (обозначаемые ^SUM, ^SUMSQ и т.д.). Для лексем, имеющих тип 23 (целые числа), спецификаторами являются значения соответствующих чисел (обозначаемые #0, #100 и т.д.).

Бек. Введение в системное программирование. 1988 - Страница 2 8823410
Рис.5.6. Результат лексического разбора программы на рис.5.1.

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

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

Процесс лексического разбора в том виде, как мы его описали, является весьма простым. Однако многие языки имеют специфические особенности, которые должны учитываться при программировании сканера. Например, сканер должен учитывать информацию, связанную с соглашениями по формату расположения строк исходной программы. Так, в языке Фортран число в колонках 1-5 в исходном предложении должно рассматриваться как номер предложения, а не как целое число. Сканер должен учитывать также следующую языково-зависимую информацию: являются ли пробелы ограничителями лексем (как в Паскале) или нет (как в Фортране), могут ли предложения свободно размещаться в нескольких строках входного текста (как в Паскале) или же для этого требуются специальные признаки продолжения (как в Фортране).

Правила распознавания лексем могут различаться в разных частях программы. Так, например, фрагмент READ не должен распознаваться в качестве ключевого слова, если он встречается внутри строки символов, заключенной в кавычки. Пробелы же оказываются существенными именно внутри таких закавыченных строк, даже если они не существенны в остальных частях программы. Аналогично в программе на Фортране строка F6.3 должна интерпретироваться по-разному в зависимости от того, расположена ли она в предложении FORMAT или в каком-то другом месте программы.

В некоторых языках встречаются также нетипичные случаи, которые должны учитываться сканером. Например, в предложении на языке Фортран

DO 10 I=1, 100

сканер должен распознавать DO как ключевое слово, 10 как номер предложения, I как идентификатор и т.д. Однако в предложении

DO 10 I=1

сканер должен распознать DO 10 I как идентификатор, а все предложение как предложение присваивания переменной DO10I значения 1. В этом случае сканер должен посмотреть вперед, есть ли запятая, прежде чем он сможет сделать вывод о правильной интерпретации фрагмента предложения DO. Языки, в которых зарезервированные слова могут употребляться в другом смысле, представляют для сканера еще больше трудностей. В ПЛ/1, например, любое ключевое слово также может использоваться в качестве идентификатора. Такие слова, как IF, THEN, ELSE, могут являться как ключевыми словами, так и именами переменных, определенных программистом. Так, например, предложение

IF THEN=ELSE THEN IF=THEN; ELSE THEN=IF

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

В настоящее время разработан ряд средств для автоматического конструирования лексических анализаторов по спецификациям, записанным на специальном языке. Описание одного из таких средств, обладающих возможностями обработки рассмотренных выше нестандартных ситуаций, содержится в работе Ахо [1977].
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Пт Фев 12, 2021 11:33 am

5.1.3. СИНТАКСИЧЕСКИЙ АНАЛИЗ

Во время синтаксического анализа предложения исходной программы распознаются как языковые конструкции, описываемые используемой грамматикой. Мы можем рассматривать этот процесс как построение дерева грамматического разбора для транслируемых предложений. Методы грамматического разбора можно разбить на два больших класса - ВОСХОДЯЩИЕ и НИСХОДЯЩИЕ - в соответствии с порядком построения дерева грамматического, разбора. Нисходящие методы (методы сверху вниз) начинают с правила грамматики, определяющего конечную цель анализа с корня дерева грамматического разбора, и пытаются так его наращивать, чтобы последующие узлы дерева соответствовали синтаксису анализируемого предложения. Восходящие методы (методы снизу вверх) начинают с конечных узлов дерева грамматического разбора и пытаются объединить их построением узлов все более и более высокого уровня до тех пор, пока не будет достигнут корень дерева.

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

Восходящий метод грамматического разбора, который мы рассмотрим, называется методом ОПЕРАТОРНОГО ПРЕДШЕСТВОВАНИЯ. Он основан на анализе пар последовательно расположенных операторов исходной программы и решении вопроса о том, какой из них должен выполняться первым. Рассмотрим, например, арифметическое выражение

A + B * C - D

В соответствии с обычными правилами арифметики умножение и деление осуществляются до сложения и вычитания. Можно сказать, что умножение и деление имеют более высокий уровень предшествования, чем сложение и вычитание. При анализе первых двух операторов (+ и *) выяснится, что оператор + имеет более низкий уровень предшествования, чем оператор *. Часто это записывают следующим образом:

Бек. Введение в системное программирование. 1988 - Страница 2 8823710

+ <. *

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

* .> -

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

A + B * C - D
. <. . .> .

Отсюда следует, что подвыражение B*C должно быть вычислено до обработки любых других операторов рассматриваемого выражения. В терминах дерева грамматического разбора это означает, что операция * расположена на более низком уровне узлов дерева, чем операции + или -. Таким образом, рассматриваемый метод грамматического разбора должен распознать конструкцию В * С, интерпретируя ее в терминах заданной грамматики, до анализа соседних термов предложения.

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

Первым шагом при разработке процессора грамматического разбора, основанного на методе операторного предшествования, должно быть установление отношений предшествования между операторами грамматики. При этом под ОПЕРАТОРОМ понимается любой терминальный символ (т.е. любая лексема). Таким образом, мы должны, в частности, установить отношение предшествования между лексемами BEGIN, READ, id и (. Матрица на рис.5.7 задает отношения предшествования для грамматики, приведенной на рис.5.2. Каждая клетка этой матрицы определяет отношение предшествования (если оно существует) между лексемами, соответствующими строке и столбцу, на пересечении которых находится эта клетка. Например, мы видим, что

PROGRAM =.= VAR

и

BEGIN <. FOR

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

; .> END

но

END .> ;

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

Бек. Введение в системное программирование. 1988 - Страница 2 8823910
Рис.5.7. Матрица предшествования для грамматики на рис.5.2.

Существуют алгоритмы автоматического построения матриц предшествования, подобных изображенной на рис.5.7, на основе описания грамматики (см., например, Ахо [1977]). Для применимости метода операторного предшествования необходимо, чтобы отношения предшествования были заданы однозначно. Например, не должно быть одновременно отношений ; <. BEGIN и ; .> BEGIN. Это требование выполняется для грамматики рис.5.2, однако надо иметь в виду, что несущественные ее изменения могут привести к тому, что некоторые из отношений перестанут быть однозначными и метод оперативного предшествования станет неприменимым.

На рис.5.8 изображен результат применения метода операторного предшествования к двум предложениям программы рис.5.1. На рис.5.8а изображен анализ предложения READ строки 9 этой программы. Это предложение анализируется по лексемам слева направо. Для каждой пары соседних операторов определено отношение предшествования. В строке (i) на рис.5.8а процессор грамматического разбора выделил фрагмент, ограниченный отношениями <. и .>, для распознавания в терминах грамматики. В данном случае этот фрагмент содержит единственную лексему id. Эта лексема может быть распознана как нетерминальный символ <factor> в соответствии с грамматическим правилом 12. Однако эта лексема может быть также распознана как нетерминальные символы <prog-name> (правило 2) и <id-list> (правило 6). Для рассматриваемого метода не важно, КАКОЙ конкретно нетерминальный символ распознан. Лексема id интерпретируется просто как некоторый нетерминальный символ <Ni>. В строке (ii) на рис.5.8а изображен результат анализа предложения, в котором лексема id заменена на <Ni>. Из него следует, что далее надо распознавать правый фрагмент предложения.

Бек. Введение в системное программирование. 1988 - Страница 2 8824110
Рис.5.8. Грамматический разбор двух предложений программы на рис.5.1 методом операторного предшествования.

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

READ ( <N1> )

который соответствует, за исключением имени нетерминального символа, грамматическому правилу 13. Это правило является единственным, применимым для этого фрагмента. Однако, так же как и прежде, мы просто обозначим этот фрагмент как нетерминальный символ <N2>.

На этом разбор предложения READ закончен. Если мы сравним дерево грамматического разбора на рис.5.3а с только что построенным, то увидим, что они полностью совпадают, за исключением используемых имен нетерминальных символов. Это означает, что мы правильно определили синтаксис анализируемого предложения, что и является целью процесса грамматического разбора. Сами же имена нетерминальных символов были выбраны произвольно автором записи грамматики и не имеют никакого отношения к смыслу предложений исходной программы.

На рис. 5.8б изображен аналогичный пошаговый процесс грамматического разбора предложения присваивания в строке 14 программы на рис.5.1. Обратите внимание, что процесс сканирования слева направо продолжается на каждом шаге грамматического разбора лишь до тех пор, пока не определился очередной фрагмент предложения для грамматического распознавания, т.е. первый фрагмент, ограниченный отношениями <. и .>. Как только подобный фрагмент выделен, он интерпретируется как некоторый очередной нетерминальный символ в соответствии с каким-нибудь правилом грамматики. Этот процесс продолжается до тех пор, пока предложение не будет распознано целиком. Вы должны внимательно проследить все этапы, изображенные на рис.5.86, для того, чтобы убедиться в правильном понимании процесса анализа с использованием матрицы предшествования, приведенной на рис.5.7. Обратите внимание, что каждый фрагмент дерева грамматического разбора строится, начиная с оконечных узлов вверх, в сторону корня дерева. Отсюда и возник термин ВОСХОДЯЩИЙ РАЗБОР.

Бек. Введение в системное программирование. 1988 - Страница 2 8824210
Рис.5.8. Продолжение.

Сравнивая деревья грамматического разбора, изображенные на рис.5.8б и рис.5.3б, мы обнаружим некоторые различия. Например, идентификатор SUMSQ на рис.5.3 был сначала интерпретирован как (factor), а потом как (term), являющийся одним из операндов операции DIV. На рис.5.8б идентификатор SUMSQ интерпретирован как единственный нетерминальный символ <Ni>, являющийся операндом DIV. Таким образом, <Ni> на дереве, изображенном на рис.5.8б, соответствует двум нетерминальным символам - (factor) и (term) - на рис.5.3б, Имеются и другие подобные различия между этими двумя деревьями.

Они вытекают из свободы образования имен нетерминальных символов, распознаваемых в рамках метода операторного предшествования. Интерпретация SUMSQ на рис.5.3б сначала в качестве (factor), а потом (term) является просто переименованием нетерминальных символов. Такое переименование необходимо, поскольку в соответствии с грамматическим правилом И первым операндом операции умножения должен быть (term), а не (factor). Так как для метода операторного предшествования имена нетерминальных символов несущественны, то подобное переименование в процессе распознавания становится ненужным. Собственно говоря, три различных имени - <ехр>, <term> и <factor> - были включены в грамматику только как средства описания отношения предшествования между операторами (например, для указания того, что умножение следует выполнять прежде сложения). Поскольку эта информация содержится в нашей матрице предшествования, то становится ненужным различать эти три имени в процессе грамматического разбора.

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

Другой метод грамматического разбора, который мы рассмотрим в этом разделе, это нисходящий метод, называемый РЕКУРСИВНЫМ СПУСКОМ. Процессор грамматического разбора, основанный на этом методе, состоит из отдельных процедур для каждого нетерминального символа, определенного в грамматике. Каждая такая процедура старается во входном потоке найти подстроку, начинающуюся с текущей лексемы, которая может быть интерпретирована как нетерминальный символ, связанный с данной процедурой. В процессе своей работы она может вызывать другие подобные процедуры или даже рекурсивно саму себя для поиска других нетерминальных символов. Если эта процедура находит соответствующий нетерминальный символ, то она заканчивает свою работу, передает в вызвавшую ее программу признак успешного завершения и устанавливает указатель текущей лексемы на первую лексему после распознанной подстроки. Если же процедура не может найти подстроку, которая могла бы быть интерпретирована как требуемый нетерминальный символ, она заканчивается с признаком неудачи или же вызывает процедуру выдачи диагностического сообщения и процедуру восстановления.

Рассмотрим в качестве примера грамматическое правило 13 на рис.5.2. Процедура метода рекурсивного спуска, соответствующая нетерминальному символу <read>, прежде всего исследует две последовательные лексемы, сравнивая их с READ и (. В случае совпадения эта процедура вызывает другую процедуру, соответствующую нетерминальному символу <id-list>. Если процедура <id-list> завершится успешно, то процедура <read> сравнивает следующую лексему с ). Если все эти проверки окажутся успешными, то процедура <read> завершается с признаком успеха и устанавливает указатель текущей лексемы на лексему, следующую за ). В противном случае процедура <read> завершается с признаком неудачи.

Эта процедура лишь немного сложнее, чем те несколько определенных грамматикой альтернатив для соответствующих нетерминалов. Дополнительно процедура должна решать, какую альтернативу попробовать в качестве следующей. Для метода рекурсивного спуска необходимо, чтобы соответствующую альтернативу можно было выбрать на основании анализа очередной входной лексемы. Существуют другие нисходящие методы грамматического разбора, для которых это требование может не выполняться. Однако они не являются столь эффективными, как метод рекурсивного спуска. Так, например, процедура, соответствующая <stmt>, анализирует очередную лексему для того, чтобы выбрать одну из четырех возможных альтернатив. Если это лексема READ, то вызывается процедура, соответствующая нетерминальному символу <read>; если это лексема id, то вызывается процедура, соответствующая нетерминальному символу <assign>, поскольку это единственная альтернатива, которая может начинаться с лексемы id, и т.д.

Если мы попытаемся написать полный набор процедур для грамматики на рис.5.2, мы столкнемся со следующей трудностью. Процедура для <id-list>, соответствующая правилу 6, будет не в состоянии выбрать одну из двух альтернатив, поскольку обе альтернативы id и <id-list> могут начинаться с лексемы id. Тут скрыта и более существенная трудность. Если процедура каким-либо образом решит попробовать вторую альтернативу (<id - list>, id), то она немедленно вызовет рекурсивно саму себя для поиска нетерминального символа <id-list>.

Это приведет еще к одному рекурсивному вызову и т.д., в результате чего образуется бесконечная цепочка рекурсивных вызовов. Причина этого заключается в том, что одна из альтернатив для <id-list> начинается также с <id-list). Нисходящий грамматический разбор не применим непосредственно для грамматик, содержащих подобные ЛЕВЫЕ РЕКУРСИИ. Те же проблемы возникнут и в отношении правил 3, 7, 10 и 11. Методы исключения левой рекурсии из правил грамматики можно найти в работах Грис [1971] и Ахо [1977].

Бек. Введение в системное программирование. 1988 - Страница 2 8824510
Рис.5.9. Упрошенная грамматика Паскаля, модифицированная для грамматического разбора методом рекурсивного спуска.

На рис.5.9 изображена та же грамматика, что и на рис.5.2, но с исключенной левой рекурсией. Рассмотрим, например, правило 6а на рис.5.9:

<id-list> ::= id { , id }

Эта нотация, являющаяся широко принятым расширением БНФ, означает, что конструкция, заключенная в фигурные скобки, может быть либо опущена, либо повторяться один или более число раз. Таким образом, правило 6а определяет нетерминальный символ <id-list> как состоящий из единственной лексемы id или же из произвольного числа следующих друг за другом лексем id, разделенных запятой. Это очевидно, эквивалентно правилу 6 на рис.5.2. В соответствии с этим новым определением процедура, соответствующая нетерминальному символу <id-list>, сначала ищет лексему id, а затем продолжает сканировать входной текст до тех пор, пока следующая пара лексем не совпадет с запятой и id. Такая запись устраняет проблему левой рекурсии, а также решает вопрос, какую из возможных альтернатив <id-list> пробовать первой.

Аналогичные изменения сделаны в правилах 3а, 7а, 10а и 11а на рис.5.9. Вам надо сравнить эти правила с соответствующими им правилами на рис.5.2 для того, чтобы убедиться в понимании сделанных изменений. Обратите внимание, что грамматика осталась рекурсивной: <ехр> определено в терминах <term>, который в свою очередь определен в терминах <factor>, а одна из альтернатив для нетерминального символа <factor> включает в себя <ехр>. Это означает, что рекурсивные вызовы процедур грамматического разбора по-прежнему возможны. Однако непосредственная левая рекурсия устранена. Цепочка вызовов, начиная с <ехр> и далее процедур, связанных с <term>, <factor> и опять <ехр>, должна продвинуть указатель текущей лексемы из входного файла как минимум на одну лексему вперед.

На рис.5.10 изображен грамматический разбор методом рекурсивного спуска предложения READ в строке 9 на рис.5.1 с использованием грамматики на рис.5.9. На рис.510а изображены процедуры, соответствующие нетерминальным символам <read> и <id-list>, логика которых совпадает с приведенным выше описанием. При этом предполагается, что переменная TOKEN содержит тип следующей лексемы из входного потока (в рамках схемы кодирования, изображенной на рис.5.5). Вам надо внимательно ознакомиться с этими процедурами, чтобы убедиться, что вы понимаете, каким образом они получены из исходной грамматики.

Бек. Введение в системное программирование. 1988 - Страница 2 8824710
Рис.5.10. Грамматический разбор предложения READ методом рекурсивной спуска.

Заметьте, что в процедуре IDLIST символ запятая (,), за которым не следует лексема id, рассматривается как ошибка и процедура заканчивается с признаком неудачи. Если же последовательность лексем типа "id, id" является правильной конструкцией языка, то метод рекурсивного спуска не даст правильных результатов. Для такой грамматики необходимо использовать более сложные методы грамматического разбора, которые должны допускать во время нисходящего разбора возврат по входной строке после обнаружения того факта, что за последней запятой не следует лексема id.

На рис.5.10б графически представлен процесс грамматического разбора методом рекурсивного спуска для анализируемого предложения. На фрагменте (i) изображен вызов процедуры READ, которая обнаружила лексемы READ и ( во входном потоке (это изображено штриховыми линиями). Во фрагменте (ii) процедура READ вызвала процедуру IDLIST (изображено штриховой линией), которая обработала лексему id. Во фрагменте (iii) процедура IDLIST закончила свою работу, передала управление процедуре READ с признаком успешного завершения; процедура READ обработала входную лексему ). На этом анализ исходного предложения завершен. Процедура READ закончит свою работу с признаком успешного завершения, что означает, что нетерминальный символ <read> обнаружен. Обратите внимание, что последовательность вызова процедур и обработки лексем целиком определяется структурой предложения READ. Фрагмент (iii) полностью совпадает с деревом грамматического разбора на рис.5.3а. Обратите внимание также на то, что дерево грамматического разбора строилось, начиная со своего корня, что и повлекло за собой термин НИСХОДЯЩИЙ РАЗБОР.

На рис.5.11 представлен разбор методом рекурсивного спуска оператора присваивания в строке 14 на рис.5.1. На рис.5.11а изображены процедуры для нетерминальных символов, необходимые для разбора этого предложения. Вы должны внимательно сравнить эти процедуры с соответствующими правилами грамматики. На. рис.5.11б изображены шаги грамматического разбора (вызовы процедур и обработка лексем), аналогичные приведенным на рис.5.10б. Читателю настоятельно рекомендуется проследить каждый шаг анализа этого предложения с использованием процедур рис.5.11а. Сравните деревья грамматического разбора на рис.5.11б и рис.5.3б. Обратите внимание, что различия между этими двумя деревьями в точности соответствуют различиям между грамматиками, изображенными на рис.5.9 и рис.5.2.

Бек. Введение в системное программирование. 1988 - Страница 2 8824910

Бек. Введение в системное программирование. 1988 - Страница 2 8825010

Бек. Введение в системное программирование. 1988 - Страница 2 8825110
Рис. 5.11. Грамматический разбор предложения присваивания методом рекурсивного спуска.

Мы привели примеры грамматического разбора отдельных предложений методом рекурсивного спуска. Однако этот метод применим и ко всей программе в целом. В этом случае для осуществления синтаксического анализа следует просто обратиться к процедуре, соответствующей нетерминальному символу <prog>. В результате работы этой процедуры будет построено дерево грамматического разбора для всей программы. Вы можете написать недостающие процедуры для всех нетерминальных символов грамматики на рис.5.9 и применить метод рекурсивного спуска к программе на рис.5.1. В качестве результата вы должны получить дерево грамматического разбора, аналогичное изображенному на рис.5.4. Различия должны быть связаны только с теми модификациями, которые имеются в грамматике на рис.5.9.

Обратите внимание, что в языке программирования отсутствуют какие бы то ни были особенности, заставляющие отдать предпочтение одному из методов грамматического разбора. Мы использовали один восходящий и один нисходящий метод для разбора одной и той же программы с использованием практически одинаковой грамматики. Возможно также одновременное использование этих методов. Некоторые компиляторы используют метод рекурсивного спуска для распознавания конструкций относительно высокого уровня (например, до уровня отдельных предложений языка), а потом переключаются на метод, аналогичный методу операторного предшествования для анализа таких конструкций, как, например, арифметические выражения. Дальнейшее обсуждение методов грамматического разбора содержится в работах Ахо [1977], Льюис [1976] и Грис [1971].


Последний раз редактировалось: Gudleifr (Сб Фев 13, 2021 2:04 pm), всего редактировалось 1 раз(а)
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Пт Фев 12, 2021 11:45 am

5.1.4. ГЕНЕРАЦИЯ КОДА

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

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

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

Генерируемый код, очевидно, зависит от того, на каком компьютере будет выполняться компилируемая программа. В этом разделе мы будем рассматривать генерацию объектного кода для машины УУМ/ДС. Для своих рабочих данных наши программы генерации кода будут использовать две структуры: список и стек. Данные, попадающие в список, выбираются в том же порядке, в котором они туда записывались, т.е. в соответствии со стратегией "первым пришел - первым ушел". Данные, попадающие в стек, выбираются из него в обратном порядке, т.е. в соответствии со стратегией "первым пришел - последним ушел". Переменная LISTCOUNT используется в качестве счетчика элементов, содержащихся в данный момент в списке. Программы генерации кода также используют спецификаторы лексем, описанные в разд.5.1.2 и обозначаемые S(лексема). Для лексемы id выражение S(id) является именем соответствующего идентификатора или указателем на него в таблице символов. Для лексемы int выражение S(int) является значением соответствующего целого числа, например #100.

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

Рис.5.12 иллюстрирует процесс генерации объектного кода для предложения READ в строке 9 программы рис.5.1. Для удобства дерево грамматического разбора этого предложения повторено на рис.5.12а. Это дерево можно получить многими различными методами грамматического разбора. Однако независимо от используемого метода на каждом шаге процесса грамматического разбора распознается самая левая подстрока входного текста, которая может быть интерпретирована в соответствии с каким-либо из правил грамматики. В методе операторного предшествования подобное распознавание осуществляется, когда подстрока входного текста редуцируется к некоторому нетерминальному символу <Ni>. В методе рекурсивного спуска распознавание происходит в тот момент, когда соответствующая процедура заканчивает свою работу с признаком успешного завершения. Таким образом, в процессе грамматического разбора всегда будет сначала распознан идентификатор VALUE в качестве нетерминального символа <id-list), а уже потом предложение в целом будет распознано в качестве нетерминального символа <read>.

Бек. Введение в системное программирование. 1988 - Страница 2 8825410
Рис.5.12. Генерация объектного кода для предложения READ.

На рис.5.12в символически изображен объектный код, который должен быть сгенерирован для предложения READ, Этот код представляет собой вызов подпрограммы XREAD, которая должна содержаться в стандартной библиотеке компилятора. Эта подпрограмма может быть вызвана также любой программой, желающей выполнить операцию чтения. Подпрограмма XREAD связывается с генерируемой программой связывающим загрузчиком или же редактором связей. (Компилятор должен включить в объектную программу достаточно информации для определения всех необходимых связей, используя, возможно, средства модификации, описанные в гл.2). Подобная техника обычно используется при компиляции предложений, выполняющих относительно сложные функции. Использование подпрограмм в этом случае позволяет избежать повторной генерации больших фрагментов программы, что существенно сокращает генерируемую программу.

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

На рис. 5.12б представлен набор программ, которые могут использоваться для генерации объектного кода. Первые две программы соответствуют двум альтернативам для нетерминального символа <id-list> в грамматическом правиле 6 на рис.5.2. В любом случае спецификатор лексемы S(id) для очередного идентификатора, являющегося элементом <id-list>, включается в список, используемый программами генерации кода. Соответствующая переменная LISTCOUNT увеличивается на единицу, чтобы отразить включение в список нового идентификатора. После того как нетерминальный символ <id-list> будет разобран, список будет содержать спецификаторы лексем для всех идентификаторов, содержащихся в <id-list>. Когда будет распознано предложение <read>, эти спецификаторы лексем будут удалены из списка и использованы для генерации объектного кода, соответствующего операции чтения READ.

Обратите внимание, что в процессе генерации дерева грамматического разбора, изображенного на рис.5.12а, вначале распознается <id-list>, а уже потом <read>. На каждом шаге грамматического разбора вызывается соответствующая программа генерации объектного кода. Вы должны внимательно проанализировать этот пример, чтобы убедиться в правильном понимании процесса генерации объектного кода, символически представленного на рис.5.12в с помощью программ генерации на рис.5.12б.

На рис.5.13 изображен процесс генерации объектного кода для предложения присваивания в строке 14 на рис.5.1. На рис.5.13а приведено дерево грамматического разбора этого предложения. Основная работа при грамматическом разборе этого предложения состоит в анализе нетерминального символа <ехр> в правой части оператора присваивания. Как мы видим, в процессе грамматического разбора идентификатор SUMSQ распознается сначала как <factor>, потом как <term>. Далее распознается целое число 100 как <factor>; затем фрагмент SUMSQ DIV 100 распознается как <term) и т.д. Эта цепочка шагов примерно совпадает с изображенной на рис.5.8б. Порядок распознавания фрагментов этого предложения совпадает с порядком, в котором должны выполняться соответствующие вычисления; сначала вычисляются подвыражения SUMSQ DIV 100 и MEAN*MEAN, а затем второй результат вычитается из первого.

Бек. Введение в системное программирование. 1988 - Страница 2 8825710

Бек. Введение в системное программирование. 1988 - Страница 2 8825810

Бек. Введение в системное программирование. 1988 - Страница 2 8825910
Рис.5.13. Генерация объектного кода для предложения присваивания.

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

<term>1 ::= <term>2 * <factor>

Индексы использованы здесь для различения двух вхождений нетерминального символа <term>. Наши программы генерации кода выполняют все арифметические операции с использованием регистра A, и мы заведомо должны сгенерировать в объектном коде операцию MUL. Результат этого умножения <term>1 - после операции MUL сохранится в регистре A. Если либо <term>2, либо <factor> уже находятся на регистре A (после выполнения предыдущих вычислений), генерация инструкции MUL - это все, что нам нужно. Иначе мы должны сгенерировать также инструкцию LDA, предшествующую инструкции MUL. В этом случае нам также надо предварительно сохранить значение регистра A, если оно понадобится в будущем.

Очевидно, необходимо отслеживать значение, помещенное в регистр А, после каждого фрагмента генерируемого объектного кода. Это можно осуществить за счет расширения понятия спецификатора лексемы на нетерминальные узлы дерева грамматического разбора. В только что рассмотренном примере СПЕЦИФИКАТОРУ УЗЛА S(<term>i) будет присвоено значение rA, указывающее на то, что результат этих вычислений содержится в регистре А. Переменная REGA используется для указания на самый высокий уровень узла дерева грамматического разбора, значение которого помещено в регистр A в сгенерированном до данного момента объектном коде (т.е. указатель на узел, спецификатор которого равен rA). Очевидно, в каждый момент процесса генерации объектного кода существует ровно один такой узел. Если же в регистре А нет значения, соответствующего некоторому узлу, то спецификатор этого узла аналогичен спецификатору лексемы: это либо указатель на переменную (в таблице символов), содержащую соответствующее значение, либо указатель на целую константу.

Чтобы проиллюстрировать приведенные соображения, рассмотрим программу генерации объектного кода на рис.5.13б, соответствующую правилу

<term>1 ::= <term>2 * <factor>

Если спецификатор узла какого-либо из операндов равен rA, то соответствующее значение уже содержится в регистре A и программа генерирует только одну инструкцию MUL. Адрес операнда для инструкции MUL содержится в спецификаторе узла другого операнда (значение которого не находится на регистре A). В противном случае вызывается процедура GETA. Эта процедура, изображенная на рис.5.13в, генерирует инструкцию LDA для загрузки значения, связанного со значением <term>2, в регистр A. Дополнительно перед инструкцией LDA процедура генерирует инструкцию STA для сохранения текущего значения регистра A, если только REGA не нуль (равенство REGA нулю означает, что это значение больше не понадобится). Это значение запоминается в некоторой РАБОЧЕЙ переменной. Рабочие переменные образуются в процессе генерации объектного кода (с именами Tl, T2, ...) по мере необходимости. Для используемых компилятором рабочих переменных будет отведено место в конце объектной программы. На узел дерева грамматического разбора, связанный со значением, содержащимся в регистре А, указывает переменная REGA. Спецификатор этого узла модифицируется, чтобы указывать на рабочую переменную, используемую для хранения этого значения.

После того как все необходимые инструкции сгенерированы, программа генерации кода устанавливает спецификатор S(<term>1) и переменную REGA таким образом, чтобы было видно, что значение, соответствующее <term1>, находится в настоящее время в регистре A. На этом процедура генерации кода для операции * завершается.

Программа генерации кода, соответствующая операции +, практически совпадает с только что рассмотренной программой для операции *. Программы для DIV и - также аналогичны, за исключением того, что для этих операций необходимо, чтобы на регистре A был установлен именно первый операнд. Программа генерации кода для операции присваивания <assing> состоит в загрузке присваиваемой величины на регистр A (с использованием GETA) и генерации инструкции STA. Обратите внимание, что переменная REGA потом обнуляется, поскольку код, соответствующий предложению присваивания, полностью сгенерирован и все промежуточные результаты больше не нужны.

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

На рис.5.13б представлено символическое изображение объектного кода, сгенерированного для предложения присваивания. Вы должны внимательно проследить генерацию этого кода, чтобы понять механизм работы программ на рис.5.13б и 5.13в. Вам надо также проверить, что эти коды действительно выполняют те вычисления, которые записаны в предложении исходной программы.

На рис.5.14 изображены другие программы генерации кода для грамматики рис.5.2. Программа для <prog-name> генерирует заголовок объектной программы, аналогичной результату работы директив ассемблера START и EXTREF. Она также генерирует инструкции для сохранения адреса возврата и перехода на первую выполняемую инструкцию компилируемой программы. После того как вся программа распознана, резервируется память для рабочих переменных (Ti). Затем все ссылки на эти переменные фиксируются в объектном коде с использованием процедуры обработки ссылок вперед, осуществляемой однопросмотровым ассемблером. Компилятор генерирует также модифицирующие записи, необходимые для описания внешних ссылок на библиотечные подпрограммы.

Бек. Введение в системное программирование. 1988 - Страница 2 8826110

Бек. Введение в системное программирование. 1988 - Страница 2 8826210
рис.5.14. Другие программы генерации объектного кода для грамматики на рис.5.2.

Генерация кода для предложения <for> состоит из нескольких шагов. После того как распознан нетерминальный символ <index-ехр>, генерируется код для инициализации индексной переменной цикла и инструкции проверки конца цикла. Часть информации также записывается в стек для дальнейшего использования. Далее независимо генерируются коды для каждого предложения, составляющего тело цикла. После того как вся конструкция <for> распознана, генерируется код, увеличивающий индексную переменную и осуществляющий возврат на начало цикла для проверки условий его завершения. Здесь программы генерации кода используют информацию, записанную в стек программой, соответствующей <index-ехр>. Использование стека для запоминания этой информации позволяет обрабатывать вложенные циклы <for>.

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

Бек. Введение в системное программирование. 1988 - Страница 2 8826310

Рис.5.15. Символическое представление объектного кода сгенерированного для программы на рис.5.1.
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Сб Фев 13, 2021 2:07 pm

5.2. МАШИННО-ЗАВИСИМЫЕ ОСОБЕННОСТИ КОМПИЛЯТОРОВ

В этом разделе мы кратко обсудим некоторые машинно-зависимые расширения базовой схемы компиляции, представленной в разд.5.1. Целью компилятора является трансляция программы, написанной на языке программирования высокого уровня, в машинные коды для некоторого компьютера. Большинство языков программирования высокого уровня построено так, чтобы быть относительно независимыми от тех машин, на которых они будут использоваться (хотя достигнутая степень такой независимости существенно различна для разных языков). Это означает, что процесс анализа синтаксиса программ, написанных на этих языках, должен быть также машинно-независимым. Таким образом, неудивительно, что машинно-зависимые особенности компилятора связаны с программами генерации и оптимизации объектного кода.

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

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

5.2.1. ПРОМЕЖУТОЧНАЯ ФОРМА ПРЕДСТАВЛЕНИЯ ПРОГРАММЫ

Существует много различных способов представления программы в некоторой промежуточной форме для анализа и оптимизации кода. См., например, Ахо [1977] и Грис [1971]. Метод, который мы обсудим в этом разделе, состоит в представлении выполняемых инструкций программы с помощью последовательности четверок (quadruples). Каждая четверка записывается в виде

операция, opl, op2, результат

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

SUM := SUM + VALUE

может быть представлено четверками следующим образом:

+ , SUM, VALUE, i1
:=, i1 , , SUM

Здесь i1 обозначает промежуточный результат (SUM + VALUE); вторая четверка присваивает это значение переменной SUM. Для обеспечения дополнительных возможностей оптимизации кода присваивание рассматривается как независимая операция (:=). Аналогично предложение

VARIANCE := SUMSQ DIV 100 - MEAN*MEAN

может быть представлено в виде последовательности четверок следующим образом:

DIV, SUMSQ, #100, i1
*, MEAN, MEAN, i2
-, i1, i2, i3
:=, i3, , VARIANCE

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

Обратите внимание, что четверки расположены в том порядке, в котором должны выполняться соответствующие инструкции объектного кода, что существенно облегчает анализ для оптимизации кода. Это означает также, что трансляция в машинные инструкции будет относительно простой.

На рис.5.16 изображена последовательность четверок, соответствующая исходной программе на рис.5.1. Обратите внимание, что предложения READ и WRITE представлены операцией CALL, за которой следует четверка PARAM, определяющая параметры операций READ и WRITE. Четверка PARAM будет, разумеется, при окончательной генерации машинного кода оттранслирована в список параметров, подобный изображенному на рис.5.15. Операция JGT в четверке (4) сравнивает значение двух своих операндов и осуществляет переход к четверке (15), если первый операнд больше второго. Операция J в четверке (14) осуществляет безусловный переход к четверке (4).

Бек. Введение в системное программирование. 1988 - Страница 2 8826610
Рис.5.16. Промежуточная форма представления программы на рис.5.1.

Для того чтобы увидеть соответствие между исходной программой и последовательностью четверок, вам надо сравнить рис.5.16 и 5.1. Вы можете также сравнить последовательность четверок на рис.5.16 с изображением объектного кода на рис.5.15.

5.2.2. МАШИННО-ЗАВИСИМАЯ ОПТИМИЗАЦИЯ КОДА

В этом разделе мы кратко опишем несколько различных путей осуществления машинно-зависимой оптимизации кода. Более подробная информация о методах оптимизации содержится во многих книгах, посвященных компиляторам, таких как Ахо [1977] и Грис [1971].

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

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

Рассмотрим в качестве примера последовательность четверок на рис.5.16. Переменная VALUE используется один раз в четверке 7 и дважды в четверке 9. Если бы у нас было достаточно регистров, то можно было бы обратиться за этой величиной к памяти только один раз. Ее значение осталось бы на регистре дли использования в командах, соответствующих четверке 9. Аналогично четверка 16 запоминает значение i5 в переменной MEAN. Если сохранить i5 на регистре, то значение этой переменной может быть использовано, когда в четверке 18 потребуется значение переменной MEAN. Использование регистров может сделать также ненужным большинство рабочих переменных. Рассмотрим, например, машинный код на рис.5.15, в котором для обработки шести из восьми промежуточных результатов (i1) на рис.5.16 достаточно воспользоваться только одним регистром (регистром А).

В действительности, конечно же, редко бывает доступно столько регистров, сколько бы нам хотелось. Проблема, таким образом, сводится к выбору регистра, переменная в котором должна быть заменена при необходимости использовать этот регистр для какой-либо другой цели. Один из возможных подходов состоит в просмотре программы вперед, чтобы определить, когда каждый из регистров будет повторно использоваться. Переменная, которая не потребуется в течении наибольшего времени, как раз и определит искомый регистр. Если регистр, ка который нужно записать новую информацию, содержит значение некоторой переменной, уже находящейся в памяти, то старое значение этого регистра может быть просто затерто. В противном случае его значение должно быть сохранено в одной из рабочих переменных. Это одна из функций, осуществляемая рассмотренной в разд.5.1.4 процедурой GETA, которая делает это с помощью рабочих переменных Ti.

При распределении регистров компилятор должен также учитывать операторы передачи управления. Например, четверка 1 на рис.5.16 присваивает переменной SUM значение 0. Это значение можно было бы сохранить на некотором регистре для дальнейшего использования. Когда переменная SUM повторно используется в качестве операнда в четверке 7, можно было бы взять это значение непосредственно из регистра. Однако подобное рассуждение может оказаться неправильным. Операция J в четверке 14 передает управление четверке 4. Если управление передастся четверке 7 именно таким образом, то значение переменной SUM окажется не занесенным на регистр. Это произойдет, например, если на этот регистр будет занесено значение i4 в четверке 12. Таким образом, наличие инструкций перехода порождает определенные трудности контроля за состоянием регистров.

Один из способов решения этой проблемы состоит в разбиении программы на ЛИНЕЙНЫЕ УЧАСТКИ. Линейным участком является последовательность четверок с одной точкой входа, находящейся в начале участка, и одной точкой выхода в конце линейного участка без каких-либо переходов внутри него. Другими словами, каждая четверка, на которую может быть передано управление в результате операции перехода, или же четверка, непосредственно следующая за четверкой передачи управления, начинает новый линейный участок. Поскольку вызов процедуры может оказать непредсказуемое влияние на содержание регистров, операция CALL обычно также рассматривается как начало линейного участка. Распределение и использование регистров внутри линейного участка могут быть осуществлены только что описанным методом. Однако, когда управление передается от одного линейного участка к другому, все находящиеся на регистрах значения упрятываются в рабочие переменные.

На рис.5.17 изображено разбиение четверок рис.5.16 на линейные участки. Участок А содержит четверки 1-3; участок В содержит четверку 4 и т.д. Рис.5.17 можно рассматривать также как структуру операторов передачи управления программы: стрелка от участка X к участку Y обозначает, что управление передается непосредственно от последней четверки участка X к первой четверке участка Y. (Внутри линейного участка управление, разумеется, передается последовательно от одной четверки к другой в порядке их расположения). Такого рода схема называется БЛОК-СХЕМОЙ программы. В рамках более сложных методов оптимизации кода блок-схема может стать объектом самостоятельного анализа с целью осуществления распределения регистров, сохраняющегося при переходе от одного линейного участка к другому.

Бек. Введение в системное программирование. 1988 - Страница 2 8826910
Рис.5.17. Линейные участки и блок-схема для последовательности четверок на рис.516.

Другая возможность оптимизации кода состоит в переупорядочивании четверок перед преобразованием их в машинный код. Рассмотрим, например, четверки на рис.5.18а. Они в основном совпадают с четверками 17-20 на рис.5.16 и соответствуют предложению 14 исходной программы на рис.5.1. На рис.5.18а изображен типичный машинный код, сгенерированный по этим четверкам с использованием одного регистра (регистра А). Этот код совпадает с кодом, изображенным на рис.5.15 для предложения 14.

Бек. Введение в системное программирование. 1988 - Страница 2 8827010
Рис.5.18. Переупорядочивание четверок для оптимизации кода.

Обратите внимание, что сначала вычисляется промежуточное значение i1 и запоминается в рабочей переменной T1. Далее вычисляется значение i2. В третьей четверке этой последовательности вычитается значение i2 из значения i1. Поскольку значение i2 только что вычислено, оно находится на регистре A. Однако это никак не удается использовать, поскольку для операции вычитания на регистре должен находиться первый операнд, а не второй. Следовательно, необходимо запомнить значение i2 в другой рабочей переменной (T2) и перед вычитанием загрузить значение переменной i1 из рабочей переменной T1 на регистр A.

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

Другая возможность машинно-зависимой оптимизации кода состоит в использовании специфических характеристик и инструкций, имеющихся на вычислительной машине. Например, на ней могут существовать специальные инструкции для организации циклов или способы адресации, позволяющие сгенерировать более эффективный объектный код. Компьютеры серии VAX содержат машинные инструкции высокого уровня, позволяющие выполнять такие сложные функции, как вызов процедуры и преобразование структур данных. Очевидно, что использование подобных особенностей, если это удается сделать, может существенно повысить эффективность выполнения объектной программы.

Некоторые машины, к ним, в частности, относятся определенные модели компьютеров серии CYBER, имеют центральный процессор, состоящий из нескольких функциональных устройств. Для подобных компьютеров порядок расположения машинных инструкций может повлиять на скорость выполнения программы. Последовательно расположенные инструкции, предназначенные для различных функциональных устройств, могут выполняться в некоторых случаях одновременно. Для того чтобы воспользоваться этой особенностью, оптимизирующий компилятор для подобных машин может переставить машинные инструкции объектного кода. Примеры и ссылки можно найти в Грис [1971].
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Сб Фев 13, 2021 2:13 pm

5.3. МАШИННО-НЕЗАВИСИМЫЕ ОСОБЕННОСТИ КОМПИЛЯТОРОВ

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

В разд.5.3.3 продолжается обсуждение методов оптимизации кода, начатое в разд.5.2.2. При этом мы сосредоточимся на машинно-независимых методах оптимизации машинного кода. В разд.5.3.4 обсуждаются проблемы, связанные с компиляцией блочно-структурированных языков программирования, и предлагаются некоторые пути решения этих проблем.

5.3.1. РАСПРЕДЕЛЕНИЕ ПАМЯТИ

В схеме компиляции, предложенной в разд.5.1 для всех переменных, определенных программистом, при обработке соответствующих декларативных операторов выделяются ячейки памяти в объектной программе. Рабочим переменным, включая такие, которые используются для хранения адреса возврата, также присваиваются фиксированные адреса внутри программы. Этот простой метод распределения памяти обычно называется статическим. Он часто используется для языков, подобных Фортрану, не допускающих рекурсивного вызова процедур или подпрограмм и не предоставляющих средств динамического распределения памяти во время выполнения программы.

Если же процедуры могут вызываться рекурсивно, как в Паскале, статическое распределение памяти не применимо. Рассмотрим, например, рис.5.19. На рис.5.19а программа MAIN вызывается операционной системой или загрузчиком (вызов первого уровня). Первое, что делает программа MAIN,- запоминает свой адрес возврата, находящийся на регистре L, в фиксированную ячейку RETADR внутри программы MAIN. На рис.5.19б программа MAIN вызывает процедуру SUB (вызов второго уровня). Адрес возврата из этого вызова запоминается в фиксированной ячейке внутри процедуры SUB. Если теперь процедура SUB вызовет рекурсивно саму себя, как это изображено на рис.5.19в, возникнет следующая проблема. Процедура SUB запомнит адрес возврата для вызова третьего уровня в ячейке RETADR из регистра L. Это действие приведет к уничтожению адреса возврата для вызова второго уровня. В результате окажется невозможным корректный возврат в программу MAIN.

Бек. Введение в системное программирование. 1988 - Страница 2 8827210
Рис.5.19. Рекурсивный вызов процедуры в случае использования статического распределения памяти.

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

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

Обычно области инициализации располагаются в стеке, причем текущая область инициализации - в вершине стека. Это изображено на рис.5.20. На рис.5.20а, соответствующем рис.5.19а, вызывается процедура MAIN; ее область инициализации записывается в стек. На базовый регистр В устанавливается начальный адрес этой текущей области инициализации. Первое слово области инициализации содержит обычно указатель PREV на предыдущую область инициализации в стеке. Поскольку эта область является первой, указатель имеет нулевое значение. Следующее слово области инициализации содержит указатель NEXT на первое неиспользуемое слово стека, начиная с которого будет образована следующая область инициализации. Третье слово содержит адрес возврата из вызова данного уровня; остальные слова содержат значения переменных, используемые процедурами.

Бек. Введение в системное программирование. 1988 - Страница 2 8827310
Рис.5.20. Рекурсивный вызов процедуры в случае использования автоматического распределения памяти.

На рис.5.20б процедура MAIN вызвала процедуру SUB. В вершине стека образована новая область инициализации, на начало которой указывает регистр B. Указатели PREV и NEXT в обоих областях инициализации принимают указанные на рисунке значения. На рис.5.20в процедура SUB вызывает рекурсивно саму себя. В результате образовывается новая область инициализации для очередного уровня вызова процедуры SUB. Обратите внимание, что адрес возврата и значения переменных для вызовов процедуры SUB различных уровней хранятся независимо друг от друга.

При возврате из процедуры в процедуру верхнего уровня текущая область инициализации, соответствующая данному уровню вызова, уничтожается. Указатель PREV в уничтожаемой области используется для установки предыдущей области инициализации в качестве текущей, и работа программы продолжается. На рис.5.20г изображено состояние стека после возврата из рекурсивного вызова процедуры SUB. Регистр B указывает на начало области инициализации предыдущего уровня вызова процедуры SUB. Адрес возврата и значения всех переменных в этой области инициализации в точности те же, что и были до рекурсивного вызова.

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

Компилятор должен также сгенерировать некоторый дополнительный код для управления областями инициализации. В начале каждой процедуры должен содержаться код, образующий новую область инициализации, связывающий ее с предыдущей областью и устанавливающий необходимые указатели, как это изображено на рис.5.20. Этот код часто называется ПРОЛОГОМ (prologue) процедуры. В конце процедуры должен содержаться код, уничтожающий текущую область инициализации и переставляющий соответствующим образом все указатели. Этот код часто называется ЭПИЛОГОМ (epilogue).

При использовании метода автоматического распределения памяти память для всех используемых в процедуре переменных выделяется в момент вызова процедуры. Другие методы динамического распределения памяти дают программисту возможность самому определить тот момент времени, когда некоторая память будет выделена. В ПЛ/1, например,

ALLOCATE(A)

в исходной программе транслируется в коды, которые выделяют намять для переменной А. Предложение

FREE(A)

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

Другой тип динамического распределения памяти используется в Паскале. Предложение

NEW(P)

выделяет память для переменной и устанавливает указатель P на только что образованную переменную. Тип вновь образованной переменной определяется описанием указателя P в программе. Образованная переменная используется в программе также с помощью указателя P. Предложение

DISPOSE(P)

освобождает память, отведенную под переменную, на которую ранее указывал P. Подобные средства доступны также в ПЛ/1, где они называются БАЗИРОВАННЫМ распределением памяти.

Переменная, образованная динамически с использованием оператора NEW, не занимает фиксированной ячейки в области инициализации и, следовательно, не может быть адресована непосредственно с использованием относительной адресации по базовому адресу. Доступ к таким переменным обычно осуществляется с помощью косвенной адресации через указатель на переменную P. Поскольку сам указатель занимает некоторую фиксированную ячейку в области инициализации, он может быть адресован обычным способом. Аналогичный метод применяется для управляемого распределения памяти в ПЛ/1. Область инициализации содержит указатель, который используется для указания на память, выделенную в результате последнего выполнения оператора ALLOCATE.

Пока мы не обсуждали механизм выделения новой памяти для переменной. Один из подходов состоит в том, что на операционную систему возлагается функция по распределению всей памяти. Операторы NEW или ALLOCATE транслируются в запрос к операционной системе для получения области памяти требуемого размера. Другой метод состоит в выделении необходимой памяти с помощью библиотечных процедур компилятора. В начале работы программы выделяется большой блок свободной памяти, которая далее распределяется библиотечными процедурами, использующими некоторый стандартный метод распределения памяти. Дальнейшее обсуждение и оценка методов распределения памяти содержится в Стендиш [1980].

Динамическое распределение памяти, рассмотренное в этом разделе, представляет собой другой пример отсроченного связывания. Связь адреса с соответствующей переменной осуществляется во время выполнения процедуры, а не во время компиляции или загрузки. Такое отсроченное связывание обеспечивает большую гибкость использования переменных и процедур. Этот метод, однако, требует больших накладных расходов, связанных с образованием областей инициализации, а также с использованием косвенной адресации. (Аналогичные соображения были высказаны в конце разд.3.4.2 в связи с динамическим связыванием).

5.3.2. СТРУКТУРИРОВАННЫЕ ПЕРЕМЕННЫЕ

В этом разделе мы кратко обсудим компиляцию программ, использующих структурированные переменные, такие как массивы, записи, строки и множества. Прежде всего мы сосредоточимся на распределении памяти для подобных переменных и на генерации кода для доступа к ним. Эти вопросы достаточно подробно обсуждаются в применении к массивам. Те же принципы, однако, могут быть использованы и для других типов структурированных переменных. Дальнейшие детали, связанные с этими проблемами, содержатся в ряде учебных пособий по компиляторам, таких как Ахо [1977] и Грис [1971].

Рассмотрим прежде всего описание массива в языке Паскаль:

А: ARRAY[1..10] OF INTEGER

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

ARRAY[t..u] OF INTEGER

мы должны выделить под этот массив u-t+1 слов памяти. Распределение памяти для многомерных массивов ненамного сложнее. Рассмотрим, например, двумерный массив

В: ARRAY[0..3, 1..6] OF INTEGER

Здесь первый индекс может принимать 4 различных значения (0-3), второй индекс может принимать 6 различных значений. Следовательно нам требуется 4*6 = 24 слова для хранения этого массива. В общем случае, если массив описан как

ARRAY[t1..u1, t2..u2] OF INTEGER

требуемое количество слов памяти определяется выражением

(u1-t1+1)*(u2-t2+1)

Для n-мерного массива требуемое количество слов определится в результате произведения n соответствующих множителей.

Рассмотрим теперь вопрос генерации кода для доступа к массиву. Для этого существенно знать соответствие между элементами массива и ячейками выделенной памяти. Для одномерных массивов это соответствие очевидно. Если для определенного ранее массива A первое слово будет содержать элемент A[1], то второе слово - А[2] и т.д. Для массивов большей размерности, однако, выбор необходимого соответствия не столь ясен.

На рис.5.21 изображены два способа хранения ранее определенного массива В. На рис.5.21а все элементы массива с одинаковым первым индексом располагаются в последовательных ячейках. Такой порядок расположения называется РАСПОЛОЖЕНИЕМ ПО СТРОКАМ. На рис.5.21б все элементы с одинаковым вторым индексом расположены вплотную друг к другу. Такое расположение называется РАСПОЛОЖЕНИЕМ ПО СТОЛБЦАМ. Другой способ описать эти соответствия состоит в том, чтобы последовательно перебирать все слова отведенной под массив памяти и рассматривать соответствующие изменения индексов. При расположении по строкам правый (второй) индекс изменяется быстрее. При расположении по столбцам быстрее изменяется левый индекс. Эти понятия могут быть обобщены на массивы, имеющие более двух индексов.

Бек. Введение в системное программирование. 1988 - Страница 2 8827810
Рис.5.21. Хранение массива В: ARRAY[0..3, 1..6]: а - по строкам и б - по столбцам.

Компиляторы для большинства языков высокого уровня располагают массивы по строкам; именно такого расположения мы будем придерживаться в последующих рассуждениях. Однако исторически сложилось так, что большинство компиляторов с Фортрана располагают свои массивы по столбцам.

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

Рассмотрим сначала одномерный массив

A: ARRAY[1..10] OF INTEGER

и предположим, что в некотором предложении имеется ссылка на элемент массива A[6]. Элементу A[6] предшествуют пять элементов массива; на машине УУМ каждый элемент занимает три байта. Таким образом, адрес элемента A[6] относительно начального адреса массива будет равен 5*3 = 15.

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

A: ARRAY[t..w] OF INTEGER

и каждый элемент массива занимает w байтов памяти. Если значение индекса есть s, то относительный адрес элемента массива A[s] определяется выражением

w*(s-1)

Генерация кода, осуществляющего подобные вычисления, изображена на рис.5.22а. Запись A[i2] в четверке 3 означает, что сгенерированный машинный код должен обращаться к массиву A с помощью индексной адресации, поместив значение i2 в индексный регистр.

Бек. Введение в системное программирование. 1988 - Страница 2 8828010
Рис.5.22. Генерация объектного кода для доступа к элементам массива.

Для многомерных массивов генерация кода зависит от способа хранения массива: по строкам или по столбцам. Мы будем предполагать, что массивы хранятся по строкам. Рис.5.21а иллюстрирует хранение массива

B: ARRAY[0..3, 1..6] OF INTEGER

по строкам. Рассмотрим сначала элемент массива B[2,5]. Если мы начнем с начала массива, то мы должны пропустить две полные строки (строки 0 и 1), прежде чем окажемся в начале строки 2 (которая начинается с элемента B[2,1]), Каждая такая строка содержит 6 элементов, две строки содержат 2*6 = 12 элементов массива. Мы должны также пропустить первые 4 элемента в строке 2, чтобы добраться до элемента B[2,5]. Вместе это составляет 16 элементов массива между его началом и элементом B[2,5]. Если каждый элемент занимает три байта, то относительный адрес внутри массива элемента B[2,5] будет равен 48.

В более общем случае предположим, что массив описан как

B: ARRAY[t1..u1, t2..u2] OS INTEGER

и мы хотим обратиться к элементу массива с индексами, имеющими значения s1 и s2. Относительный адрес элемента B[s1,s2] дается выражением

w*((s1-t1)*(u2-t2+1) + (s2-t2))

Рис.5.22б иллюстрирует процесс генерации кода для обращения к такому массиву. Вы должны внимательно изучить приведенную последовательность четверок, чтобы убедиться в том, что вы понимаете изложенные вычисления.

Приведенные выше методы и формулы легко могут быть обобщены на массивы большей размерности. Детали можно найти в Ахо [1977].

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

T(I:J-1, I+1:J)

Эта запись означает, что первый индекс изменяется от I до J-1, а второй индекс находится в пределах от I+1 до J, Здесь I и J - имена переменных, определенных в программе. Очевидно, что для такого массива память можно отвести, лишь используя какой-либо из методов динамического распределения памяти, В ПЛ/1 это можно осуществить с помощью оператора

ALLOCATE T

Предполагается, что переменным I и J уже присвоены некоторые значения.

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

Рассмотренные для массивов проблемы также возникают при компиляции других структурированных переменных (записей, строк и множеств). Компилятор должен выделить необходимую для размещения переменной память; запомнить информацию, характеризующую структуру переменной; сгенерировать, используя эту информацию, код для обращения к компонентам структурированной переменной; породить описатель структурированной переменной для тех случаев, когда необходимая информация отсутствует во время компиляции. Дальнейшее обсуждение этих проблем для различных специальных типов структурированных переменных можно найти в Ахо [1977] и Грис [1971].
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Сб Фев 13, 2021 2:18 pm

5.3.3. МАШИННО-НЕЗАВИСИМАЯ ОПТИМИЗАЦИЯ КОДА

В этом разделе мы обсудим некоторые важные методы машинно-независимой оптимизации кода. Так же как и в предыдущих разделах, мы не будем стремиться к детальному описанию какого-либо из этих методов. Вместо этого мы дадим словесное описание и проиллюстрируем основные понятия примерами. Алгоритмы и детали, касающиеся этих методов, можно найти в работах Ахо [1977] и Грис [1971]. Мы будем предполагать, что исходная программа уже оттранслирована в последовательность четверок так же, как в разд.5.2.1.

Одним из важных источников оптимизации кода является удаление ОБЩИХ ПОДВЫРАЖЕНИЙ, т.е. подвыражений, которые встречаются в нескольких местах программы и вычисляют одно и то же выражение. Рассмотрим, например, предложение на рис.5.23а. Терм 2*J является общим подвыражением. Оптимизирующий компилятор должен только один раз сгенерировать код, вычисляющий это умножение, и использовать его результат в обоих местах.

Бек. Введение в системное программирование. 1988 - Страница 2 8828310
Рис.5.23. Оптимизация объектного кода, связанная с общими подвыражениями и инвариантами цикла.

Общие подвыражения обычно обнаруживаются при анализе промежуточной формы представления программы. Подобная промежуточная форма изображена на рис.5.23б. Если мы исследуем эту последовательность четверок, то обнаружим, что четверки 5 и 12 совпадают, за исключением имени получаемого промежуточного результата. Обратите внимание также, что операнд J не меняет своего значения между четверками 5 и 12. Невозможно достичь четверки 12, не проходя предварительно четверку 5, поскольку они расположены на одном линейном участке. Таким образом, четверки 5 и 12 вычисляют одно и то же значение. Это означает, что мы можем удалить четверку 12 и заменить любые обращения к ее результату (i10) на обращение к переменной i3, которое является результатом четверки 5. Эта модификация позволяет избежать дублирования вычислений подвыражения 2*J, которое мы выделили как общее подвыражение при анализе исходной программы.

После замены i10 на i3 мы обнаружим, что четверки 6 и 12 также совпадают, за исключением имени результата. Следовательно, мы можем удалить четверку 13 и заменить переменную i11 всюду, где она используется, на переменную i4. Аналогично четверки 10 и 11 также могут быть удалены, поскольку они эквивалентны четверкам 3 и 4.

Результат применения этого метода изображен на рис.5.23в. Четверки на этом рисунке перенумерованы, но, чтобы легче было сравнивать с рис.5.23б, имена промежуточных результатов ij оставлены неизменными, за исключением вышеназванных замен в скобках. Обратите внимание, что общее количество четверок сокращено с 19 до 15. Поскольку операции во всех используемых здесь четверках займут, вероятно, примерно одинаковое время на обычном компьютере, то мы также сократим общее время выполнения программы.

Бек. Введение в системное программирование. 1988 - Страница 2 8828410
Рис.5.23. Продолжение.

Другим источником оптимизации кода является удаление ИНВАРИАНТОВ ЦИКЛА. Так называются подвыражения внутри цикла, результирующие значения которых не изменяются внутри цикла при переходе от одной итерации к другой. Таким образом, эти значения могут быть вычислены только один раз перед входом в тело цикла вместо того, чтобы вычислять их заново перед каждой итерацией. Поскольку для большинства программ основное время работы приходится на выполнение циклов, экономия времени от подобной оптимизации может быть весьма существенной. Мы предполагаем существование алгоритмов поиска циклов на основе анализа блок-схемы программы. Одним из примеров подобного алгоритма является метод построения блок-схемы программы, описанный в разд.5.2.2.

Примером инварианта цикла является вычисление выражения 2*J на рис.5.23а (см. четверку 5 на рис.5.23в). Результат вычисления этого выражения зависит только от операнда J, значение которого не изменяется во время выполнения цикла. Таким образом, мы можем поместить четверку 5 на рис.5.23в непосредственно перед началом выполнения цикла. Аналогичные соображения справедливы относительно четверок 6 и 7.

На рис.5.23г изображена последовательность четверок, являющихся результатом описанных модификаций. Общее количество четверок остается тем же, что и на рис.5.23в, но количество четверок внутри тела цикла уменьшилось с 14 до 11. Каждое выполнение предложения FOR на рис.5.23а вызывает 10-кратное выполнение тела цикла. Это означает, что общее количество операций, необходимых для выполнения FOR, сократилось со 141 до 114.

Наши модификации сократили общее количество операций, приходящееся на одно выполнение предложения FOR, co 181 (рис.5.23б) до 114 (рис.5.23г), что существенно уменьшило время выполнения программы. Существуют также и более тонкие методы обработки общих подвыражений и инвариантов цикла, чем описанные выше. Можно ожидать, что благодаря этим методам может быть получен еще более оптимизированный код. Примеры и обсуждение этих методов содержатся в работе Ахо [1977].

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

Tl := 2*J;
T2 :== T1-1;
FOR I := 1 ТО 10 DO
X[I,T2] :== V[I, T1];

В результате будет достигнута лишь частичная по сравнению с только что описанной оптимизация кода. Дальнейшая оптимизация связана с процессом вычисления относительного адреса индексированных переменных, на которые запись исходной программы не может повлиять. Например, оптимизацию, связанную с четверками 3, 4, 10 и 11 на рис.5.23б, нельзя осуществить путем каких-либо преобразований исходной программы. Следует заметить также, что исходные предложения на рис.5.23а предпочтительнее, поскольку они нагляднее, чем модифицированная версия программы, использующая переменные T1 и T2. Оптимизирующий компилятор должен предоставить программисту возможность писать исходную программу так, чтобы добиваться простоты и прозрачности, должен компилировать программу в машинные коды таким образом, чтобы добиться эффективности выполнения.

Еще один источник оптимизации кода состоит в замене менее эффективных операций на более эффективные. Рассмотрим в качестве примера фрагмент программы на Фортране, изображенной на рис.5.24а. Изображенный цикл DO порождает таблицу, содержащую первые 20 степеней двойки. При каждом проходе по телу цикла константа 2 возводится в степень I. На рис.5.24б изображено представление этих предложений в виде последовательности четверок. Возведение в степень представлено операцией ЕХР. На уровне машинного кода операция ЕХР представляет собой либо цикл, осуществляющий последовательность умножений, либо вызов подпрограммы, использующий логарифмы для получения результата.

Бек. Введение в системное программирование. 1988 - Страница 2 8828610
Рис.5.24. Оптимизация объектного кода за счет замены одних операций на другие, более эффективные.

Анализируя эту программу, мы можем обнаружить, что существует более эффективный путь выполнения этих вычислений. При каждом проходе по циклу величина I увеличивается на 1. Таким образом, величина 2**I для текущего прохода по циклу может быть получена путем умножения на 2 значения, полученного на предыдущем проходе. Очевидно, что этот метод вычисления выражения 2**I является существенно более эффективным, чем выполнение серии умножений или использование логарифмирования.

Подобные модификации могут быть осуществлены при вычислении относительного адреса элемента массива TABLE(I). Предположим, что каждый элемент массива занимает одно слово, тогда объектная программа для УУМ будет вычислять смещение по формуле 3*(I-1). Это вычисление осуществляется в четверках 3 и 4 на рис.5.24б. Таким образом, объектная программа будет выполнять одно умножение при каждом обращении к массиву.

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

На рис.5.24в приведена модифицированная по сравнению с рис.5.24б последовательность четверок, в которой осуществлены обе указанные выше возможности повышения эффективности. Алгоритм, осуществляющий подобные модификации, содержится в работе Грис [1971]. Как и в нашем предыдущем примере, подобную оптимизацию можно частично осуществить на уровне исходной программы. Однако оптимизировать вычисления индексов массива программист не может, поскольку он не может влиять на детали кода, генерируемого для обращения к массиву.

Существует также ряд других возможностей машинно-независимой оптимизации кода. Например, вычисления значения операндов, известных в момент компиляции, могут быть осуществлены непосредственно компилятором. Другие методы оптимизации включают в себя преобразование цикла в линейный участок (развертывание цикла) и слияния тел различных циклов. Детали этих и других методов оптимизации см. в Грис [1971] и Ахо [1977].

5.3.4. БЛОЧНО-СТРУКТУРИРОВАННЫЕ ЯЗЫКИ

В некоторых языках, подобных Алголу, программа разбивается на части, называемые БЛОКАМИ. Блок - это часть программы, в которой можно описать свои собственные идентификаторы. Определению блока также удовлетворяют такие программные единицы, как процедуры и функции в Паскале. В этом разделе мы обсудим некоторые вопросы, связанные с компиляцией и выполнением программ, написанных на таких блочно-структурированных языках.

На рис.5.25а изображена схема блочно-структурированной программы на Паскале. Каждой процедуре соответствует блок. В последующем обсуждении мы будем использовать термины ПРОЦЕДУРА и БЛОК как синонимы. Обратите внимание, что блоки могут вкладываться друг в друга. Например, процедуры B и D вложены в процедуру A, а процедура C вложена в процедуру B. Каждый блок может содержать, как это изображено на рисунке, описания переменных. В блоке допустимы также обращения к любым переменным, которые описаны в любом блоке, содержащем рассматриваемый блок, при условии что эти имена не переопределены во внутреннем блоке.

Бек. Введение в системное программирование. 1988 - Страница 2 8828910
Рис.5.25. Программа с вложенными блоками.

Рассмотрим, например, переменные X, Y, Z типа INTEGER, определенные в строке 2 процедуры A. Процедура B содержит описание переменных X и Y, имеющих тип REAL в строке 4, Внутри процедуры B использование имени X означает ссылку на переменную типа REAL, определенную внутри В. Однако использование имени Z означает обращение к переменной типа INTEGER, определенной в блоке A, поскольку имя Z не переопределено внутри B. Аналогично внутри процедуры C имя W связано с переменной, определенной внутри C; имена X и Y будут связаны с переменными, определенными в B, и имя Z связано с переменной, определенной в A. Переменные не могут использоваться вне того блока, внутри которого они определены. Например, именем W нельзя пользоваться вне процедуры B, а именем V нельзя пользоваться вне процедуры C.

При компиляции программ, написанных на блочно-структурированных языках, удобно нумеровать блоки, как это изображено на рис.5.25а. Как только распознано начало нового блока, этому блоку присваивается очередной номер. В результате компилятор может построить таблицу, описывающую блочную структуру программы так, как это изображено на рис.5.25б. В столбце "Уровень вложенности" содержится глубина вложенности блоков. Самый внешний блок имеет уровень вложенности 1, а все остальные блоки имеют уровень вложенности, на 1 больший, чем уровень вложенности охватывающего блока. Поскольку имя может быть определено более одного раза (в разных блоках), каждый элемент таблицы символов, содержащий идентификатор, должен содержать также номер того блока, в котором этот идентификатор описан. Описание идентификатора допустимо, если этот идентификатор не был ранее описан в том же блоке. В противном случае таблица символов содержала бы несколько элементов, соответствующих одному имени. Элементы таблицы символов, соответствующие описанию одного и того же имени в разных блоках, могут быть связаны между собой в таблице символов цепочками указателей.

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

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

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

Один из широко распространенных методов осуществления доступа к переменным, описанных в объемлющем блоке, использует структуру данных, которую мы назовем ДИСПЛЕЕМ {display). Дисплей содержит указатель на самую последнюю область инициализации текущего блока и на все блоки, объемлющие данный в исходной программе. Если в некотором блоке осуществляется обращение к переменной, описанной в некотором объемлющем блоке, то генерируется объектный код, который, используя дисплей, ищет область инициализации, содержащую нужную переменную.

Использование дисплея иллюстрируется на рис.5.26. Предполагается, что процедура A вызвана системой. Далее процедура A вызвала процедуру B, а процедура B вызвала процедуру C. Сложившаяся ситуация изображена на рис.5.26а. Стек содержит области инициализации, соответствующие вызовам процедур A, B и C. Дисплей содержит указатели на область инициализации процедуры C и на области инициализации объемлющих блоков (A и B).

Бек. Введение в системное программирование. 1988 - Страница 2 8829110
Рис.5.26. Использование дисплея для процедур, изображенных на рис.5.25.

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

Предположим теперь, что процедура C вызывает процедуру D. (Это допустимо, поскольку процедура D определена в процедуре А, содержащей С.) Получившееся в результате такого вызова содержимое стека в дисплее изображено на рис.5.26в. Область инициализации для процедуры B образована обычным образом и записана в стек. Обратите внимание, что дисплей содержит в этот момент только два указателя: на области инициализации процедур D и A. Это объясняется тем, что процедура D не может использовать переменных, описанных в B или C (за исключением случая, когда они переданы ей как параметры), даже несмотря на то, что она вызвана из C. В соответствии с правилами видимости для блочно-структурированных языков процедура D может использовать только переменные, описанные в D или в объемлющем D блоке исходной программы (которым в нашем случае является процедура A).

Сходная ситуация, изображенная на рис.5.26г, образуется, если теперь процедура D вызовет B. Процедура B может использовать только те переменные, которые описаны в B или в A, что и отражено в списке указателей, находящемся в дисплее. После того как процедура B завершится и передаст управление процедуре D, содержимое стека и дисплея опять окажется таким, как на рис.5.26в.

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

Компилятор для блочно-структурированного языка должен содержать в себе код, используемый в начале каждого блока для инициализации дисплея, соответствующего данному блоку, В конце блока он должен обращаться к коду, восстанавливающему предыдущее содержимое дисплея. Детали реализации содержатся в Ахо [1977].
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Вс Фев 14, 2021 12:05 pm

5.4. ВАРИАНТЫ ПОСТРОЕНИЯ КОМПИЛЯТОРОВ

В этом разделе мы рассмотрим некоторые возможные альтернативы построения компиляторов. Изложение будет по необходимости довольно кратким. Наша цель - ввести основные понятия и концепции, а не подробно обсуждать какие-либо вопросы.

В разд.5.1 была описана простая однопросмотровая схема компиляции. В разд.5.2 и 5.3 представлены особенности компиляторов, требующие для своей реализации более одного просмотра. В разд.5.4.1 мы кратко обсудим общую проблему разбиения процесса компиляции на отдельные просмотры и рассмотрим преимущества однопросмотровых и многопросмотровых вариантов построения компиляторов.

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

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

5.4.1. РАЗБИЕНИЕ НА ОТДЕЛЬНЫЕ ПРОСМОТРЫ

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

Не все языки допускают однопросмотровую компиляцию. Описания переменных в Паскале должны предшествовать операторам, использующим эти переменные. В Фортране переменные могут быть описаны только в начале программы; тип неописанных переменных определяется по умолчанию. Некоторые языки, в частности ПЛ/1, допускают появление описаний идентификаторов после их использования. Однопросмотровые компиляторы могут обрабатывать ссылки вперед в операторах перехода, используя приемы, сходные с описанными для однопросмотровых ассемблеров. Гораздо более серьезную проблему представляет собой использование элементов данных, описания которых еще не появились.

Рассмотрим, например, следующий оператор присваивания:

X := Y*Z

Если все переменные X, Y и Z целого типа, то объектным кодом этого оператора могут быть команда целочисленного умножения и команда запоминания результата. Если же одни переменные целого типа, а другие вещественного, то в объектном коде должны присутствовать как минимум одна команда преобразования типа и команда умножения с плавающей точкой. Очевидно, что компилятор не сможет скомпилировать правильный объектный код до тех пор, пока ему не станут известны типы всех переменных. Для некоторых сочетаний типов переменных оператор может оказаться вообще недопустимым. Таким образом, язык, разрешающий использование элементов данных до их описания, не допускает однопросмотровой компиляции.

Некоторые языки программирования по другим причинам требуют при компиляции более двух просмотров. Например, Хантер [1981] показал, что Алгол-68 требует как минимум трех просмотров.

Существует несколько факторов, которые должны учитываться при выборе однопросмотровой или многопросмотровой схемы компиляции (в предположении, что рассматриваемый язык в принципе допускает однопросмотровую компиляцию). Если важна скорость компиляции, то однопросмотровая схема может оказаться предпочтительней. Например, при счете студенческих задач большая часть времени, как правило, уходит на компиляцию. Полученные во время компиляции объектные модули используются лишь один-два раза; время счета студенческих задач обычно мало. Ускорение процесса компиляции в этих условиях может привести к существенному повышению эффективности использования ЭВМ и к сокращению времени между повторными выходами на машину.

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

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

Дальнейшее обсуждение вопроса о разбиении компиляции на отдельные фазы можно найти в работах Хантер [1981]1, Ахо [1977]. Примеры реальных компиляторов и детальное описание разбиения всей работы на отдельные этапы содержатся в книгах Грис [1977] и Ахо [1977].

5.4.2. ИНТЕРПРЕТАТОРЫ

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

Интерпретаторы обычно выполняют, подобно тому как это было описано для компиляторов, лексический и синтаксический анализ и транслируют исходную программу в свое внутреннее представление. При этом возможно использование самых различных внутренних представлений. Одно из них - это последовательности четверок, подобные описанным в разд.5.2. Чаще используется расширенная польская постфиксная запись (см. Грис [1971]). В качестве внутреннего представления можно использовать даже исходную форму записи программы, хотя обычно гораздо более эффективной является предварительная обработка исходной программы, предшествующая ее выполнению.

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

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

Существенное преимущество интерпретатора по сравнению с компилятором состоит в тех средствах отладки, которые легко могут быть в нем реализованы. Интерпретатор обычно имеет таблицу идентификаторов, номера строк и другую информацию об исходной программе. Она может быть использована во время выполнения программы для автоматической распечатки поименованных данных, трассировки операторов исходной программы с указанием номеров строк, в которых они содержатся, и т.д. Следовательно, особенно привлекательным является использование интерпретатора в учебном процессе, когда акцент делается на понимание и отладку программ. Детали, относящиеся к реализации отладочных средств в интерпретаторах, можно найти в работе Грис [1971].

Большинство языков программирования может достаточно успешно как интерпретироваться, так и компилироваться. Некоторые языки весьма хорошо приспособлены для интерпретации. Как мы уже видели, компиляторы обычно генерируют обращения к библиотечным программам для выполнения таких функций, как ввод-вывод и сложные операции преобразования данных. Для таких языков, как Снобол и АПЛ, большая часть скомпилированной программы состоит из обращений к. таким библиотечным программам. В подобных случаях интерпретатор может оказаться предпочтительней из-за большей скорости трансляции. Основное время выполнения программы будет занимать работа стандартных библиотечных программ независимо от того, используется ли компилятор или интерпретатор.

Особенности некоторых языков могут быть естественно реализованы только в режиме интерпретации. В языках АПЛ и Снобол тип переменной может меняться во время выполнения программы. В АПЛ переменные, доступные внутри функции или подпрограммы, определяются динамически последовательностью вызовов во время выполнения программы, а не статической вложенностью блоков исходной программы (см. рис.5.25 и 5.26, иллюстрирующие это различие). Языки, допускающие динамическое переопределение типов и областей действия имен, очень трудно эффективно компилировать. Их проще реализовать в режиме интерпретации, который позволяет легко связывать символические имена переменных с типами данных и ячейками памяти в ходе выполнения программы.

Дальнейшее обсуждение разработки и использования интерпретаторов имеется в работе Грис [1971].

5.4.3. КОМПИЛЯТОРЫ НА P-КОД

Основные идеи, положенные в основу компиляторов на псевдокод (P-код) и интерпретаторов, весьма схожи. В обоих случаях исходная программа анализируется и преобразуется во внутреннее представление, которое затем выполняется в режиме интерпретации. В случае компиляторов на P-код форма внутреннего представления является машинным языком некоторой гипотетической ЭВМ, часто называемой ПСЕВДОМАШИНОЙ.

Процесс использования компилятора на P-код изображен на рис.5.27. В результате компиляции получается объектная программа в P-кодах. Далее эта программа читается и выполняется интерпретатором P-кода.

Бек. Введение в системное программирование. 1988 - Страница 2 8829710
Рис.5.27. Компиляция и выполнение программы с использованием компилятора на P-код.

Основным достоинством такого подхода является легкая переносимость программного обеспечения на другие типы ЭВМ. Компилятору не нужно генерировать разные машинные коды для разных ЭВМ, поскольку объектные программы на псевдокоде могут выполняться на любой ЭВМ, имеющей интерпретатор P-кода. Сам компилятор тоже может быть перенесен на другие ЭВМ, если он написан на своем собственном входном языке. Для этого исходная программа компилятора должна быть скомпилирована на P-код, после чего она может интерпретироваться на других ЭВМ. Таким образом, компилятор на P-код может использоваться без каких-либо изменений для широкого класса ЭВМ, если для каждой из них написан свой интерпретатор P-кода. Написание этого интерпретатора хотя и достаточно сложно, но, конечно же, проще написания нового транслятора. Такой подход может быть использован для переноса и других типов системного программного обеспечения без его переписывания. В качестве примера можно привести описанную в разд.6.5 систему UCSD Pascal.

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

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

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

Наиболее широко известный компилятор на P-код - это UCSD Pascal компилятор, описанный в разд.5.5.2. Часто сами понятия псевдомашины и P-кода связывают только с этим компилятором. На самом деле эти понятия являются более общими и используются в ряде других программных систем, например в компиляторе на P-код, описанном в работе Нори [1981].

5.4.4. КОМПИЛЯТОРЫ КОМПИЛЯТОРОВ

Написание компиляторов обычно связано с большими усилиями и затратой большого количества времени. При создании некоторых его частей, особенно при разработке сканера и процессора грамматического разбора, можно большую часть работы проделать автоматически. КОМПИЛЯТОР КОМПИЛЯТОРОВ - это программное средство, используемое для облегчения разработки компиляторов. Такого рода средства часто также называют ГЕНЕРАТОРАМИ КОМПИЛЯТОРОВ или системами построения трансляторов.

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

Бек. Введение в системное программирование. 1988 - Страница 2 8829910
Рис.5.28. Автоматизация создания компилятора при использовании компилятора компиляторов.

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

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

Краткое описание одного из компилятора компиляторов (YACC) приведено в разд.5.5.4. Дальнейшее обсуждение и примеры компиляторов компиляторов содержатся в работах Грис [1971] и Хопгуд [1959].
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Вс Фев 14, 2021 12:11 pm

5.5. ПРИМЕРЫ РЕАЛИЗАЦИИ

В этом разделе мы кратко опишем устройство ряда компиляторов. В разд.5.5.1 описывается компилятор ETH Pascal (компилятор с Паскаля, разработанный в Высшей политехнической школе в Цюрихе), который широко распространен на машинах серии CYBER фирмы CDC. В разд.5.5.2 описывается компилятор UCSD Pascal (UCSD - University of California San Diego) - один из лучших компиляторов на P-код. Оба эти компилятора являются однопросмотровыми и практически не осуществляют оптимизацию объектного кода.

В разд.5.5.3 описан компилятор IBM FORTRAN H, предназначенный для использования на машинах серии System/370 фирмы IBM. Это многопросмотровый компилятор, позволяющий получить высокооптимизированный объектный код.

Разд.5.5.4 содержит описание компилятора компиляторов YACC, разработанного в Bell Laboratories для использования в рамках операционной системы UNIX. Мы также кратко опишем генератор сканеров LEX, обычно используемый вместе с YACC.

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

5.5.1. КОМПИЛЯТОР ETH PASCAL

[Использованы материалы из "The Zurich Implementation Amman", Pascal: The Language and its implementation, copyright 1981 by John Wiley and Sons, Перепечатано с разрешения]

Язык программирования Паскаль был создан Никлаусом Виртом во время его работы в Высшей политехнической школе (ETH) в Цюрихе в 1968-1970гг. Первый компилятор с Паскаля был написан в 1970г. Полученный опыт привел к появлению новой версии Паскаля в 1972г. и разработке для нее компилятора в 1972-1974гг. Этот компилятор используется в настоящее время более чем в 150 организациях, расположенных в разных частях света, и количество его пользователей непрерывно растет. Именно этот второй компилятор мы и опишем в данном разделе. Более подробное описание содержится в работе Амманн [1981]. Компилятор с Паскаля, разработанный в ETH в Цюрихе (далее просто "компилятор ETH"), является однопросмотровым компилятором, генерирующим коды для ЭВМ CDC 6000 и ЭВМ серии CYBER. Он является самокомпилятором [В нашей литературе этот термин редко употребляется, чаще говорят, о раскрутке или самораскрутке.- Прим. перев.] в том смысле, что сам он написан на том языке, который компилирует. Одно из удобств такого подхода состоит в возможности переноса компилятора на новую машину. Исходная версия компилятора, предназначенная для работы на машине A, модифицируется с тем, чтобы порождать объектный код, предназначенный для машины B. Если эту версию компилятора использовать для компиляции самого себя, то в результате мы получим компилятор, предназначенный для работы на машине В. Другое преимущество самокомпилятора состоит в образовании обратной связи, улучшающей процедуры генерации кода. Если компилятор будет модифицирован для получения более эффективного объектного кода и далее будет использован для компиляции самого себя, то в результате мы получим более эффективный компилятор. Подобные самокомпиляторы довольно широко используются. Так получилось, что два других описанных в разд.5.5 компилятора также являются самокомпиляторами.

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

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

Во время выполнения программы необходимая для хранения данных память выделяется динамически с использованием метода автоматического распределения памяти, описанного в разд.5.3.1. Области инициализации порождаются и записываются в стек при вызове каждой процедуры. Эти области связаны между собой в цепочку в порядке их появления в стеке, как это изображено на рис.5.20. Существует также отдельная цепочка, содержащая только те области, переменные которых доступны для использования в данный момент в соответствии с правилами видимости имен в Паскале. Эта цепочка используется для тех же целей, что и дисплей, который мы рассматривали в разд.5.3.4 (см. рис.5.26).

Чтобы улучшить генерируемый код, компилятор следит за содержимым всех 24 регистров ЭВМ серии CYBER во время выполнения программы. Если в процессе генерации кода требуется некоторый элемент данных или указатель на определенный адрес, то прежде всего проверяется содержимое этих регистров. Если требуемое значение или указатель уже находится на регистре, то не требуются никакие операции загрузки. Когда необходимо загрузить на регистр X некоторый элемент данных, компилятор ищет свободный регистр. Если в наличии нет ни одного свободного регистра, компилятор выбирает тот регистр, значение которого должно быть заменено. Этот выбор основан на времени, которое прошло с момента последнего использования содержимого регистра, а также на типе значения, содержащегося на регистре. Замена константы более предпочтительна, чем замена значения переменной, поскольку константу легче перезагрузить на регистр, когда это понадобится. Используемый метод выбора нужного регистра представляется весьма эффективным. Когда компилятор ETH был использован для компиляции самого себя, результирующий объектный код был примерно на 30 % меньше кода, порожденного предыдущей версией компилятора, который не отслеживал содержимое регистров.

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

5.5.2. КОМПИЛЯТОР UCSD PASCAL

Система UCSD Pascal представляет собой законченную систему программирования, предназначенную для небольших компьютеров и предоставляющую средства для разработки и выполнения программ. Программы, разработанные в рамках этой системы, могут работать на большом количестве машин различных марок. В этом разделе мы опишем компилятор UCSD Pascal. В разд.6.5 содержится краткое обсуждение всей системы UCSD Pascal. Компилятор UCSD Pascal был разработан на основе компилятора на P-код, разработанного в Цюрихе, который в свою очередь является модифицированной версией компилятора ETH, описанного в разд.5.5.1. Соответствующее описание содержится в работе Нори [1981]. Компилятор UCSD Pascal является однопросмотровым компилятором, написанным на Паскале, генерирующем P-код для псевдомашины (P-машины). Входным языком компилятора является несколько расширенный стандартный Паскаль.

P-машина для UCSD Pascal имеет стековую архитектуру и поддерживает ряд структур данных и команд высокого уровня. Регистры общего назначения на Р-машине отсутствуют. Для большинства арифметических, логических и других операций соответствующие аргументы находятся в стеке. Стек также используется для запоминания параметров и справочной информации о вызовах процедур и функций. Имеются несколько регистров специального назначения, которые используются операционной системой и интерпретатором Р-кода. Например, такими регистрами являются счетчик команд и указатель на текущую область инициализации.

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

Компилятор UCSD Pascal предоставляет программисту возможность непосредственно использовать команды P-кода внутри программы на Паскале. Эта возможность бывает полезна в некоторых специальных случаях (например, при разработке системных программ нижнего уровня). Имеется также возможность генерации непосредственно машинного кода для заданного компьютера, используя дополнительный этап в процессе компиляции. Генератор "местного кода" получает на входе законченную программу на P-коде, сгенерированную компилятором, и транслирует заданные процедуры этой программы из P-кода в объектный код заданного компьютера. Оттранслированная в машинные коды программа занимает больше памяти, чем соответствующий ей P-код; однако время работы оттранслированных процедур может уменьшиться в 10 и более раз, по сравнению со временем интерпретации P-кода.

Более подробное описание компилятора UCSD Pascal и P-машины можно найти в работах SofTech Microsystems [1983] и Овергаард [1980].

5.5.3. КОМПИЛЯТОР FORTRAN H ФИРМЫ IBM

Компилятор Fortran H был разработан для использования на машинах фирмы IBM серий 360 и 370. Входным языком компилятора является Фортран IV; на выходе компилятора формируется объектный модуль, который может быть обработан редактором связей системы. Одна из основных целей, которая ставилась при разработке этого компилятора,- получить эффективный объектный код. Пользователь может выбрать один из трех уровней оптимизации при каждом обращении к компилятору.

Компилятор Fortran H состоит из системного диспетчера, управляющего процессом компиляции, четырех логических фаз компиляции (обозначаемых как фазы 10, 15, 20 и 25) и фазы обработки ошибок (фаза 30). Конкретные действия, осуществляемые на каждой фазе компиляции, и требуемое число фаз зависят от выбранного уровня оптимизации. Компилятор представляет собой оверлейную программу, разбитую на 13 сегментов. Корневым сегментом оверлейной структуры является системный диспетчер. Все остальные сегменты представляют собой либо фазу целиком, либо логически завершенную часть какой-либо фазы.

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

Фаза 10 читает исходную программу и осуществляет лексический анализ. На этой фазе заполняются необходимые информационные таблицы для всех переменных, констант, номеров предложений, находящихся в исходной программе. Выходом фазы 10 является последовательность пар оператор-операнд в порядке их следования в исходной программе. В данном контексте термин ОПЕРАТОР включает в себя такие элементы, как скобки и запятые, в дополнение к обычным арифметическим, логическим и родственным им операциям. Под термином ОПЕРАНД понимаются переменные, константы и литералы. Кроме лексического анализа на фазе 10 осуществляются также печать листинга исходной программы и печать таблицы ссылок, если этого потребовал программист.

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

На фазе 20 реализуются различные методы оптимизации объектного кода. Выполняемые действия зависят от уровня оптимизации, выбранного программистом. Если оптимизации вообще не требуется, то на фазе 20 осуществляется выделение регистров для операндов четверок. При этом, однако, не делается попытки максимально использовать все имеющиеся регистры и не предпринимается попыток оставить значения операндов на регистрах для дальнейшего использования. Эти действия нарываются БАЗОВЫМ РАСПРЕДЕЛЕНИЕМ РЕГИСТРОВ и осуществляются за один просмотр программы.

Если требуется первый уровень оптимизации, то на фазе 20 осуществляется ПОЛНОЕ РАСПРЕДЕЛЕНИЕ РЕГИСТРОВ. Его назначение аналогично базовому распределению регистров, при этом, однако, более полно используются имеющиеся регистры и делается попытка сохранить значения операндов на регистрах для последующего использования. Делается попытка также сохранить на регистрах в течение всего времени выполнения объектной программы наиболее часто используемые операнды. Полное распределение регистров требует нескольких просмотров промежуточной формы представления программы. На фазе 20 осуществляется также оптимизация инструкций перехода, чтобы избежать ненужной загрузки регистров адресами переходов.

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

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

Дальнейшая информация о компиляторе Fortran H содержится в IBM [1972б].

5.5.4. КОМПИЛЯТОР КОМПИЛЯТОРОВ YACC

Компилятор компиляторов YACC (Yet Another Compiler-Compiler - еще один компилятор компиляторов) является генератором программ грамматического разбора, который используется в рамках операционной системы UNIX. Этот компилятор компиляторов использовался для разработки компиляторов с языков Паскаль, Ratfor, АПЛ, Си и ряда других языков программирования. Он также использовался для менее традиционных приложений, включающих, например, язык определения типов и систему управления документами. В этом разделе мы дадим краткое описание компилятора компиляторов YACC и программы LEX - генератора программ лексического анализа, связанного с YACC. Дальнейшая информация об этих программных средствах содержится в Джонсон [1980], Джонсон |[1975] и Леек [1975].

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

Подсистема LEX является генератором сканеров. Она может быть использована для порождения программ сканеров требуемого для YACC типа. Фрагмент спецификаций, подаваемый на вход системы LEX, изображен на рис.5.29а. Каждая строка левого столбца представляет собой шаблон, с которым должны сравниваться фрагменты входного потока. Если сравнение закончилось успешно, то вызывается программа соответствующей строки правого столбца. Эти программы написаны на языке программирования Си. Результатом работы этих программ обычно является очередная распознанная лексема. Кроме того, они заполняют необходимые таблицы и выполняют другие сходные действия.

Бек. Введение в системное программирование. 1988 - Страница 2 8830710
Рис.5.29. Пример входных спецификаций для LEX и YACC.

В примере на рис.5.29а первому шаблону не соответствуют никакие действия; его смысл состоит в том, чтобы удалить пробелы из входного потока. Программы, соответствующие следующим трем шаблонам, должны сформировать на выходе тип соответствующей лексемы: тип LET для ключевого слова let, MUL для оператора * и ASSIGN для оператора =. Как уже говорилось, внутренним представлением для LET, MUL и других лексем являются целые числа. Пятый шаблон определяет структуру распознаваемых идентификаторов. Первый символ должен находиться в диапазонах а-z или А-Z. За ним может следовать произвольное количество символов из диапазонов а-z, А-Z или 0-9. Символ * в этом шаблоне указывает на то, что допускается произвольное количество повторений терма, предшествующего *. Программа этого шаблона должна заполнить необходимые для описания найденного идентификатора строки соответствующих таблиц и сформировать на выходе тип лексемы ID.

В соответствии со спецификациями, приведенными на рис.5.29а, входная строка

let x = y*z

будет представлена в качестве последовательности лексем:

LET ID ASSIGN ID MUL ID

Обратите внимание, что выбирается именно первый совпавший шаблон, в результате чего ключевое слово let распознается как лексема LET, а не как идентификатор ID.

LEX может использоваться для создания весьма сложных сканеров. Однако для некоторых языков, таких как Фортран, лексические анализаторы все еще должны создаваться вручную.

В качестве входной информации генератору процессоров грамматического разбора системы YACC подается грамматика языка, для которого создается компилятор, и набор требуемых действий, соответствующих правилам используемой грамматики. Фрагмент таких входных спецификаций представлен на рис.5.29б. Первая строка представляет собой описание типов используемых лексем. Другие строки являются правилами грамматики. Грамматический процессор системы YACC вызывает связанную с каждым правилом семантическую программу, как только распознана соответствующая конструкция языка. Каждая такая программа может передать в другие программы некоторое значение, присвоив его переменной $$. Эти значения, порожденные ранее проработавшими семантическими программами или сканером, хранятся в переменных $1, $2, и т.д. Они определяют результаты работы программ, соответствующих компонентам правой части правил грамматики, упорядоченных слева направо.

Пример использования этих переменных приведен на рис.5.29б. Семантическая программа, связанная с правилом

expr : expr MUL expr

строит фрагмент дерева грамматического разбора входного предложения, используя порождающую функцию build. Построенный фрагмент дерева присваивается на выходе из семантической программы переменной $$. Аргументами порождающей функции являются оператор MUL и значения (являющиеся фрагментами дерева грамматического разбора), порожденные после распознавания операндов. Эти значения обозначены $1 и $3.

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

Порожденные системой YACC процессоры грамматического разбора используют метод грамматического разбора снизу вверх, называемый LALR(l). Этот метод годится для большого класса наиболее интересных грамматик. В частности, нет необходимости избегать левой рекурсии. Возможно использование даже неоднозначных грамматик, за счет введения дополнительных грамматических правил, устраняющих двусмысленность. Порожденные YACC процессоры имеют очень хорошую систему обнаружения и диагностики ошибок. Она позволяет повторно проанализировать фрагмент, в котором обнаружена ошибка, либо продолжить обработку входного потока, пропустив ошибочный фрагмент.
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Вс Фев 14, 2021 12:15 pm

УПРАЖНЕНИЯ

РАЗДЕЛ 5.1
1. Постройте по грамматике на рис.5.2 дерево грамматического разбора для следующих конструкций <id-list):
а) ALPHA
б) ALPHA,BETA,GAMMA

2. Постройте по грамматике на рис.5.2 дерево грамматического разбора для следующих конструкций <ехр>:
а) ALPHA + BETA
б) ALPHA - BETA * GAMMA
в) ALPHA DIV (BETA + GAMMA) - DELTA

3. Предположим, что правила 10 и 11 грамматики на рис.5.2 изменены на

<ехр> ::= <term> | <exp> * <term> | <exp> DIV <term>
<term> ::= <factor> | <term> + <factor> | <term> - <factor>

Постройте деревья грамматического разбора для конструкций <ехр> упр.2 в соответствии с такой модифицированной грамматикой. Каким образом изменения в грамматике повлияли на отношение предшествования для арифметических операторов?

4. Предположим, что правила 10 и 11 грамматики на рис.5.2, заменены на одно правило:

<ехр> ::= <factor> | <exp> + <factor> | <exp> - <factor> | <exp> * <factor> | <exp> DIV <factor>

Постройте деревья грамматического разбора для конструкций <ехр> упр.2 по такой грамматике. Какие при этом возникают проблемы?

5. Модифицируйте грамматику на рис.5.2 с тем, чтобы включить операцию возведения в степень в форме X^Y. He забудьте о том, что возведение в степень должно иметь более высокий приоритет, чем остальные арифметические операции.

6. Модифицируйте грамматику на рис.5.2 с тем, чтобы включить предложения вида

IF условие THEN предложение-1 ELSE предложение-2

где конструкция ELSE может быть опущена. Вы можете предположить, что условие имеет форму a < b, a > b или a = b. Здесь a и b - идентификаторы или целые числа. Вы можете не заботиться о вложенных структурах IF, т.е. предложение-1 и предложение-2 не могут быть IF предложениями.

7. Модифицируйте грамматику на рис.5.2 так, чтобы список вывода для оператора WRITE наряду с идентификаторами мог содержать заключенные в кавычки строки символов.

8. Составьте алгоритм, просматривающий входной поток и распознающий идентификаторы и операторы. Идентификатор может иметь длину до 10 символов. Он должен начинаться с буквы, а остальные символы, если они есть, должны быть буквами или цифрами. Необходимо распознавать следующие операторы: +, -, *, DIV и :=. Ваш алгоритм должен на выходе образовывать целое число, определяющее тип распознанной лексемы в соответствии с кодировкой, принятой на рис.5.5. Если будет обнаружена недопустимая комбинация символов, то алгоритм должен образовывать число -1.

9. Модифицируйте написанный вами в упр.8 сканер так, чтобы он наряду с идентификаторами распознавал и целые числа. Целые могут начинаться со знака (+ или -), но не могут начинаться с цифры 0.

10. Выберите какой-либо из знакомых вам языков программирования высокого уровня и напишите для него лексический сканер.

11. Осуществите грамматический разбор следующих предложений программы на рис.5.1, используя метод операторного предшествования и матрицу предшествования на рис.5.7:
а) предложение присваивания в строке 11;
б) описание в строке 3;
в) предложение FOR, начинающееся в строке 7.

12. Осуществите грамматический разбор всей программы на рис.5.1, Используя метод операторного предшествования и матрицу предшествования на рис.5.7.

13. Осуществите грамматический разбор предложения присваивания в строке 11 на рис.5.1, используя метод рекурсивного спуска и процедуры, приведенные на рис.5.11.

14. Напишите процедуры метода рекурсивного спуска, соответствующие правилам для <dec-list>, <dec> и <type> на рис.5.9. Используйте эти процедуры для разбора описаний в строке 3 на рис. 5.1.

15. Напишите процедуры метода рекурсивного спуска для других нетерминалов грамматики на рис.5.9. Осуществите грамматический разбор всей программы на рис.5.1 методом рекурсивного спуска.

16. Используйте программы на рис.5.12-5.14 для генерации кода, соответствующего следующим предложениям программы на рис.5.1:
а) предложение присваивания в строке 11;
б) предложение WRITE в строке 15;
в) предложение FOR, начинающееся в строке 7.

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

17. Используйте программы на рис.5.12-5.14 для генерации кода всей программы на рис.5.1.

18. Напишите процедуры генерации объектного кода для новых правил, которые вы добавили к грамматике для определения предложений IF.

19. Предположим, что грамматика на рис.5.2 расширена путем введения переменных с плавающей точкой (<type> REAL) в дополнение к целым переменным. Как при этом изменятся приведенные в тексте программы генерации кода? Считайте, что допустимы смешанные арифметические выражения, соответствующие обычным правилам Паскаля.

20. Приведенные в тексте программы генерации объектного кода используют непосредственную адресацию для обращения к целым константам, используемым в арифметических выражениях (например, число 100 в выражении SUM DIV 100). Каким образом такие константы могут обрабатываться компилятором для машины, не имеющей непосредственной адресации?

21. Какого типа ошибки в исходной программе могут быть обнаружены во время лексического анализа?

22. Какого типа ошибки в исходной программе могут быть обнаружены во время синтаксического анализа?

23. Какого типа ошибки в исходной программе могут быть обнаружены во время генерации кода?

24. В чем таблица символов, используемая компилятором, может отличатьоя от подобной таблицы ассемблера?

РАЗДЕЛ 5.2
1. Перепишите приведенные на рис.5.12 и 5.13 программы генерации объектного кода так, чтобы они порождали не объектный код, а четверки.

2. Напишите программы генерации объектного кода из четверок, полученных программами упр.1. (Наверное, вам для этого понадобится программа, сходная по функциям с процедурой GETA на рис.5.13).

3. Воспользуйтесь программами из упр.1 для получения четверок, соответствующих следующему фрагменту программы:

READ(X,Y);
Z := 3*X - 5*V + X*Y;

4. Используйте программы из упр.2 для получения объектного кода из четверок, полученных в упр.3.

5. Перепишите приведенные на рис.5.14 программы генерации объектного кода так, чтобы они порождали не объектный код, а четверки.

6. Используйте программы из упр.1 и 5 для получения четверок, соответствующих программе на рис.5.1.

7. Разбейте полученные в упр.6 четверки на линейные участки и постройте блок-схему программы.

8. Предположим, что вы должны сгенерировать объектный код для УУМ/ДС из четверок, полученных в упр.6. Предложите способ распределения регистров для оптимизации объектного кода, который бы использовал регистры S и T для хранения значений переменных и промежуточных результатов.

РАЗДЕЛ 5.3
1. Составьте алгоритм работы пролога процедуры в предположении, что область инициализации имеет представленный на рис.5.20 формат.

2. Составьте алгоритм работы эпилога процедуры в предположении, что область инициализации имеет представленный на рис.5.20 формат.

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

4. Предположим, что массив описан следующим образом:

C: ARRAY[5..20] OF INTEGER

Сгенерируйте четверки для предложения C[1]:= 0

5. Предположим, что массив описан следующим образомз
D: ARRAY[-10..10, 2..12] OF INTEGER

Сгенерируйте четверки для предложения D[I,J] := 0

6. Обобщите предложенные в разд.5.3.2 методы для хранения трехмерных массивов по строкам. Для массива, описанного в виде

E: ARRAY[1..5, 1..10, 0..8] OF INTEGER

сгенерируйте четверки для предложения: E[I,J,K] := 0.

7. Как надо изменить базовый адрес массива A, определенный на рис.5.22а, чтобы избежать необходимости вычитать 1 из значения индекса (четверка 1)?

8. Каким образом способ из упр.7 можно обобщить на двумерные массивы?

9. Пусть массив T описан как

T: ARRAY[1..5, 1..100] OF INTEGER

Оттранслируйте следующие предложения в четверки и исключите общие подвыражения.

Бек. Введение в системное программирование. 1988 - Страница 2 8831210

К := J-1;
FOR I := 1 ТО 5 DO
BEGIN
T[I,J] := K*K;
J := J+K;
T[I,J) := K*К-1;
END

10. Модифицируйте четверки из упр.9 для исключения инвариантов цикла.

11. Составьте алгоритм порождения необходимого дисплея при вызове процедуры. Ваш алгоритм может использовать предыдущее состояние дисплея (т.е. состояние до вызова процедуры), адрес области инициализации, порожденной для вызываемой процедуры, и уровень вложенности вызываемой процедуры.
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Пн Фев 15, 2021 12:35 pm

ГЛАВА 6. ОПЕРАЦИОННЫЕ СИСТЕМЫ

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

Способы разработки операционных систем и их назначение достаточно разнообразны. Одни, очень простые, предназначены для обеспечения работы единственного пользователя на персональной ЭВМ. Другие, крайне сложные системы, дают возможность одновременной работы многих пользователей, управляют высокоразвитыми аппаратными и программными средствами. В разд.6.1 рассмотрены основные свойства операционных систем, которые присущи практически любому их элементу. Из-за большого разнообразия операционных систем перечень этих свойств необычайно сжат. Он состоит лишь из нескольких общих функций, которые могут быть взяты едва ли не как определения термина операционная система.

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

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

6.1. ОСНОВНЫЕ ФУНКЦИИ ОПЕРАЦИОННЫХ СИСТЕМ

Ниже вкратце рассматриваются основные функции, обычно выполняемые всеми операционными системами. Главная задача операционной системы - упростить общение пользователей с ЭВМ [Не менее важной задачей операционной системы является создание удобного интерфейса не только с пользователем, но и с такими элементами системного программного обеспечения, как трансляторы, загрузчики, мониторы.- Прим. ред.]). Системное программное обеспечение является надстройкой над базовыми аппаратными средствами и делает работу пользователя с машиной более удобной. Например, обеспечивая максимальную производительность ЭВМ, операционная система осуществляет достаточно сложный процесс управления ее ресурсами, все нюансы которого скрыты от пользователя.

По сравнению с предыдущими главами приводимое ниже изложение основных свойств намного короче и носит более общий характер. Например, для ассемблеров, которые рассмотрены в гл.2, нам удалось найти общую структуру, не зависящую от машинной реализации. Однако операционные системы для персонального компьютера и для суперЭВМ с большим числом пользователей, за исключением основных подходов, будут сильно различаться.

Основные функции операционных систем могут быть изображены, как показано на рис.6.1. Взаимодействие с программистами, операторами и т.д. осуществляется через ИНТЕРФЕЙС ПОЛЬЗОВАТЕЛЯ, который поддерживается операционной системой. Именно его мы имеем в виду при ответе на вопрос: "Какого характера операционная система?". Если интерфейс предусматривает наличие некоторого языка управления, то, например, запуск программы на счет может быть осуществлен по команде RUN P. В разд.6.1.1 приводится терминология, связанная с операционными системами, и дается их классификация, основанная на предоставляемом ими интерфейсе пользователя. В разд. 6.1.2 коротко описываются некоторые возможные функции интерфейса пользователя.

Бек. Введение в системное программирование. 1988 - Страница 2 8831410
Рис.6.1. Основная концепция операционной системы.

Для выполнения часто встречающихся задач операционные системы предоставляют программам определенный набор услуг. Например, для чтения из файла некоторого набора данных программа P может использовать стандартную сервисную программу. Последняя вызывается командой типа read(f), с помощью которой задается и имя файла. Всю заботу об осуществлении ввода-вывода, производимого на машинном уровне, возьмет на себя операционная система.

Сервисные программы могут рассматриваться как часть ОПЕРАЦИОННОГО ОКРУЖЕНИЯ задач, находящихся в решении. О нем пойдет речь в разд.6.1.3. Более подробно некоторые сервисные функции и стандартные программы описаны в разд.6.2 и 6.3.

В данной главе предполагается, что функции операционной системы реализованы только программным обеспечением. Однако многие из них могут быть представлены ПРОГРАММНО-АППАРАТНЫМИ СРЕДСТВАМИ (firmware), состоящими из набора микропрограмм. Дополнительную информацию об этом можно получить в Дейтел [1984].

6.1.1. ТИПЫ ОПЕРАЦИОННЫХ СИСТЕМ

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

Один из способов классификации связан с количеством пользователей, одновременно обслуживаемых системой. Назовем ОДНОПРОГРАММНОЙ систему, которая обеспечивает работу одного пользователя. Это самый старый тип операционных систем. Сейчас его можно встретить на микрокомпьютерах и персональных ЭВМ. Вероятно, и на гипотетической машине УУМ из-за малого объема памяти и нехватки каналов (что сильно затрудняет обслуживание более одного пользователя) следует считать, что используется однопрограммная система.

МУЛЬТИПРОГРАММНАЯ СИСТЕМА позволяет одновременно выполнять несколько заданий пользователей, управляя при этом распределением процессора между ними. Для того чтобы задания не мешали друг другу, операционная система создает для них соответствующее операционное окружение. МУЛЬТИПРОЦЕССОРНАЯ СИСТЕМА схожа с мультипрограммной с той разницей, что в первой возможно использование более чем одного ЦП.

Основная цель мультипрограммирования - увеличение производительности вычислительной системы за счет разделения ее ресурсов между несколькими заданиями. Например, одно задание может занимать процессор, в то время как другое - ожидать завершения операции ввода-вывода (подробнее об этом см. в разд.6.2).

Другой способ классификации операционных систем основан на типе доступа, предоставляемого интерфейсом пользователя. В случае систем с ПАКЕТНОЙ ОБРАБОТКОЙ в качестве задания выступает последовательность управляющих операторов, записанная на машинных носителях (например, на перфокартах или диске). За исключением смены дисков или лент, осуществляемой операторами, всю заботу о считывании и выполнении заданий берут на себя операционные системы. Порядок, в котором выполняются задания, может быть выбран несколькими способами. Вопросы планирования рассматриваются в разд.6.3. ДИАЛОГОВЫЙ или ИНТЕРАКТИВНЫЙ доступ некоторого числа пользователей обеспечивается системами РАЗДЕЛЕНИЯ ВРЕМЕНИ. Операционная система исполняет директивы пользователей по мере того, как они вводятся, стараясь дать ответ на каждую команду пользователя за разумно короткое время. Для обработки внешних сигналов, поступающих, например, с различных датчиков, и быстрого ответа на них используются системы РЕАЛЬНОГО ВРЕМЕНИ. К ним относятся операционные системы, работающие на электронно-вычислительных машинах, управляющих процессами, в которых время является критическим параметром (например, ядерная реакция или полет космического корабля).

Вообще говоря, мультипрограммные системы пакетной обработки призваны сделать использование ЭВМ более эффективным. Основной задачей систем разделения времени должно считаться обеспечение хорошего времени ответа пользователям, работающим в диалоговом режиме. Возможно, при этом придется примириться с использованием машины с меньшей эффективностью. Системы реального времени должны обеспечивать ГАРАНТИРОВАННОЕ время ответа на внешние события, для которых время является критическим параметром. Довольно часто все эти функции реализуются в одной системе. Например, многие системы пакетной обработки нередко поддерживают интерактивный режим, а другие вдобавок осуществляют и обслуживание процессов реального времени.

Дополнительные сведения обо всех этих операционных системах, равно как и подробности об их создании, можно найти в Дейтел [1984].

6.1.2. ИНТЕРФЕЙС ПОЛЬЗОВАТЕЛЯ

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

В более сложных системах может существовать несколько различных языков общения с операционной системой. Непрофессиональным программистам предоставляется возможность работать с ЭВМ на простом ЯЗЫКЕ ДИРЕКТИВ {command language). Профессиональными программистами может применяться мощный и сложный язык, часто называемый ЯЗЫКОМ УПРАВЛЕНИЯ ЗАДАНИЯМИ (job control language). Кроме того, обычно существует специальный язык, при помощи которого осуществляется взаимодействие операторов и ЭВМ. Такой интерфейс оператора позволяет запускать и останавливать задания, выяснять их состояние и состояние системных ресурсов, управлять внешними действиями. Например, оператору может быть сообщено о необходимости установить ленту или диск.

Разработка интерфейса пользователя обычно не влечет за собой решения сложных технических задач. Между тем его создание чрезвычайно важно, так как он эксплуатируется большинством пользователей. В идеале интерфейс пользователя должен быть разработан таким образом, чтобы отвечать требованиям всевозможных типов пользователей и в то же время учитывать цели и задачи вычислительной системы. Дополнительные сведения об интерфейсе пользователя можно найти в работах Дейтел [1984] и Питерсон [1983].

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

6.1.3. ОПЕРАЦИОННОЕ ОКРУЖЕНИЕ

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

В качестве примера услуг, предоставляемых операционным окружением, рассмотрим функцию ввода-вывода. Почти все операционные системы имеют стандартные программы, помогающие в осуществлении таких операций. Предположим, что программа работает на УУМ под управлением однопрограммной системы. Чтобы прочитать байт без помощи операционной системы, программа должна содержать цикл, в котором анализируется состояние устройства и выполняется команда RD (см. рис.2.1). Обнаружение и исправление ошибок также возложено на саму программу.

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

Стандартная сервисная программа, подобная описанной выше, может восприниматься как расширение базовой машины. Типичная операционная система содержит много подобных программ. Вместе взятые, они составляют РАСШИРЕННУЮ МАШИНУ, которая и используется во время выполнения программы. Программам не нужно опускаться до уровня базовых аппаратных средств, поскольку все функции и возможности предоставляет расширенная машина. Она проще в использовании, чем реальная. Это касается, например, нюансов выполнения операций ввода-вывода. Есть и другие преимущества. Например, операции ввода-вывода на расширенной машине менее подвержены ошибкам, чем на реальной, так как за обнаружением и исправлением ошибок следит операционная система.

Иногда расширенную машину называют ВИРТУАЛЬНОЙ. Однако термин "виртуальная машина" может иметь и другой смысл. Это двоякое использование термина описывается в разд.6.4.

Операционное окружение мультипрограммных операционных систем содержит также программы, которые управляют ресурсами ЭВМ, выделяя их по необходимости заданиям пользователей. Например, оперативная память распределяется между заданиями, одновременно находящимися в решении, центральный процессор предоставляется заданиям согласно заранее выбранной стратегии. За исключением конкретных запросов операционной системе заданиям пользователя нет необходимости иметь дело с управлением ресурсами. Благодаря операционному окружению каждое задание выполняется как бы на отдельной расширенной машине, хотя в действительности базовая машина может быть распределена между несколькими пользователями.

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

Способ запроса связи с операционной системой при помощи обращения к фиксированной области памяти используется в микрокомпьютерах и персональных ЭВМ. Однако этот метод часто не удобен и является источником ошибок; кроме того, он может предоставить пользователю возможность обойти средства защиты, встроенные в операционную систему. В более развитых системах пользователи запрашивают функции операционных систем в основном при помощи специальных машинных команд, таких как ВЫЗОВ СУПЕРВИЗОРА (SVC - SuperVisor Call). Выполнение команды SVC вызывает прерывание, в результате которого управление передается сервисной стандартной программе операционной системы. Код, которым сопровождается команда SVC, определяет тип запроса. Обработка прерываний операционной системой рассматривается в разд.6.2.1.

Как правило, в машине любое прерывание вызывает перевод ЦП из РЕЖИМА ПОЛЬЗОВАТЕЛЯ в РЕЖИМ СУПЕРВИЗОРА. В режиме супервизора могут быть использованы все команды и средства машины. Многие части операционной системы работают в этом режиме. При этом в режиме пользователя недопустимо выполнение некоторых команд. К таким командам могут относиться, например, функции ввода-вывода, установка флагов защиты или переключение ЦП из одного режима в другой. Примеры таких команд будут рассмотрены ниже. Ограничения, накладываемые на использование ПРИВИЛЕГИРОВАННЫХ КОМАНД, заставляют программу пользователя обращаться к услугам операционного окружения. Таким образом, вместо непосредственного использования функций базового аппаратного обеспечения программы должны иметь дело с интерфейсом расширенной машины. Ограничения также не дают программам пользователей вмешиваться случайно или намеренно в функции управления ресурсами, осуществляемые операционной системой. Привилегированные команды, равно как и режим пользователя/супервизора (или эквивалентный ему), необходимы практически для всех систем, поддерживающих одновременную работу более чем одного пользователя.

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

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Пн Фев 15, 2021 12:41 pm

6.2. МАШИННО-ЗАВИСИМЫЕ СВОЙСТВА ОПЕРАЦИОННЫХ СИСТЕМ

Одной из наиболее важных функций операционной системы является управление ресурсами ЭВМ, на которой она работает. Многие ресурсы имеют непосредственное отношение к аппаратным устройствам, таким как центральная оперативная память, каналы ввода-вывода и ЦП. Таким образом, многие функции операционной системы тесно связаны с архитектурой машины.

Рассмотрим, например, машину УУМ. У нее маленькая оперативная память, отсутствуют каналы ввода-вывода, прерывания, нет команд вызова супервизора. Такая машина может быть удобна в качестве персональной ЭВМ; на ней нет смысла работать одновременно нескольким пользователям. Таким образом, операционная система для стандартной машины УУМ будет однопользовательской с простыми средствами общения с пользователем и минимальным набором функций операционного окружения. И если она предоставит какие-то простые возможности, то их вряд ли будет больше тех, что рассматривались в разд.6.1.

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

В данном разделе мы рассмотрим некоторые функции машинно-зависимых частей операционных систем. Для этого будет использована терминология ЭВМ УУМ/ДС; однако некоторые принципы могут быть легко перенесены на другие машины, у которых архитектурные особенности схожи с УУМ/ДС. Мы обсудим в ходе изложения ряд важных свойств аппаратных средств УУМ/ДС. Для упрощения ссылок все эти свойства резюмированы в приложении В.

Раздел 6.2.1 знакомит читателя с основными принципами прерываний и их обработки, используемыми на всем протяжении остальной части главы. В разд.6.2.2 обсуждаются вопросы, связанные с распределением ЦП между несколькими заданиями пользователей, работающими в мультипрограммном режиме. В разд.6.2.4 и 6.2.5 обсуждаются вопросы разделения центральной памяти между несколькими пользовательскими заданиями. В разд.6.2.4 дается представление о средствах управления реальной памятью, а в разд.6.2.5 - о важном понятии - виртуальной памяти.

6.2.1. ОБРАБОТКА ПРЕРЫВАНИЙ

ПРЕРЫВАНИЕ {interrupt) - это сигнал, заставляющий ЭВМ менять обычный порядок исполнения потока команд. Возникновение подобных сигналов обусловлено такими событиями, как завершение операций ввода-вывода, истечение заранее заданного интервала времени или попытка деления на нуль.

Рис.6.2 дает представление о последовательности событий, происходящих в ответ на прерывание. Предположим, что в момент поступления от некоторого источника сигнала прерывания программа A находится в решении. В результате управление автоматически передается на БЛОК ОБРАБОТКИ ПРЕРЫВАНИЙ (или БЛОК ОП, а также ОБРАБОТЧИК ПРЕРЫВАНИЙ), который обычно является частью операционной системы. Этот блок предназначен для выполнения некоторых действий в ответ на условие, вызвавшее прерывание. После завершения обработки управление может быть снова передано в ту точку программы A, где ее выполнение было прервано.

Бек. Введение в системное программирование. 1988 - Страница 2 8832210
Рис.6.2. Основная концепция обработки прерываний.

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

На рис.6.3 приведены четыре класса прерываний для ЭВМ УУМ/ДС. SVС-ПРЕРЫВАНИЕ (класс I) возникает при выполнении ЦП команды вызова супервизора. Эта команда используется, программами для вызова функций операционной системы. ПРОГРАММНОЕ ПРЕРЫВАНИЕ (класс II) возникает при появлении некоторой ситуации, такой как деление на нуль, или при попытке выполнить неправильную машинную команду и, возможно, в процессе работы программы. Приложение В содержит полный перечень условий, которые могут вызвать программное прерывание.

Бек. Введение в системное программирование. 1988 - Страница 2 88322a10
Рис.6.3. Типы прерываний в УУМ/ДС.

ПРЕРЫВАНИЕ ПО ТАЙМЕРУ (класс III) вызывается интервальным таймером ЦП. Этот таймер содержит регистр, которому может быть присвоено определенное начальное значение посредством привилегированной команды STI. Значение этого регистра автоматически уменьшается на 1 после использования каждой миллисекунды времени ЦП. Когда это значение становится равным нулю, происходит прерывание по таймеру. Подобный интервальный таймер используется операционной системой для определения времени, в течение которого программа пользователя может оставаться под управлением машины.

ПРЕРЫВАНИЕ ПО ВВОДУ-ВЫВОДУ (класс IV) вызывается каналами или устройствами ввода-вывода. Причиной многих таких прерываний является нормальное завершение некоторой операции ввода-вывода; однако они могут также оповещать о возникновении различных ошибочных ситуаций.

Когда происходит прерывание, состояние ЦП сохраняется, а управление передается стандартной программе обработки прерываний. В заключении рассмотрим метод для УУМ/ДС.

Как показано на рис.6.4, в машине УУМ/ДС для каждого класса прерываний имеется соответствующая ему рабочая область прерываний. Например, область, соответствующая прерыванию по таймеру, начинается с адреса памяти 160. Когда происходит прерывание по таймеру, содержимое всех регистров сохраняется в этой области (см. рис.6.4а). Затем из двух первых слов области заранее занесенные туда значения загружаются в слово состояния SW и счетчик команд PC. Загрузка и сохранение регистров осуществляются аппаратными средствами машины автоматически.

Бек. Введение в системное программирование. 1988 - Страница 2 8832310
Рис.6.4. Операции контекстного переключения, вызванные: а - прерыванием по таймеру и б - командой LPS 166.

Загрузка счетчика команд новым значением адреса автоматически вызывает передачу управления на соответствующую команду. Этот адрес, заранее сохраненный в рабочей области прерывания, представляет собой начальный адрес стандартной программы обработки прерываний по таймеру. Загрузка слова SW также вызывает определенные изменения в состоянии ЦП.

После выполнения в ответ на запрос на прерывание любого требуемого действия стандартная программа обработки прерываний выполняет команду загрузки состояния процессора (LPS - Load Processor Status), в результате чего управление передается прерванной программе (см. рис.6.4б). Команда LPS вызывает загрузку сохраненного содержимого SW, PC и других регистров из соответствующих слов области сохоанения, начиная с адреса, указанного в команде. Это приводит к восстановлению содержимого регистров и состояния ЦП, которые были в момент прерывания. Управление затем передается на команду, перед выполнением которой произошло прерывание. Сохранение и восстановление состояния ЦП и содержимого регистров часто называют операцией КОНТЕКСТНОГО ПЕРЕКЛЮЧЕНИЯ.

Бек. Введение в системное программирование. 1988 - Страница 2 8832410
Рис.6.4. Продолжение.

Слово состояния SW содержит часть информации, которая нужна для обработки прерываний. Мы говорим о содержимом SW в УУМ/ДС. Большинство ЭВМ имеет аналогичный регистр, часто называемый СЛОВОМ СОСТОЯНИЯ ПРОГРАММЫ или СЛОВОМ СОСТОЯНИЯ ПРОЦЕССОРА.

Бек. Введение в системное программирование. 1988 - Страница 2 8832610
Рис.6.5. Содержимое слова состояния УУМ/ДС.

На рис.6.5 показано содержимое SW. Первым битом (MODE) задается режим, в котором находится ЦП - пользовательский или супервизора.

Обычные программы выполняются в пользовательском режиме (MODE = 0). Когда происходит прерывание, новое загружаемое содержимое SW имеет MODE = 1, что автоматически переводит ЦП в режим супервизора, тем самым становится возможным использование привилегированных команд. Перед тем как значение SW будет сохранено, в поле ICODE автоматически устанавливается значение, указывающее на причину прерывания. В случае SVC-прерываний ICODE присваивается значение, заданное пользователем командой SVC. Он определяет тип сервисного запроса. При программном прерывании в ICODE отражен тип вызвавшего его условия, например деление на нуль. При прерывании по вводу-выводу в ICODE дан номер канала, породивший прерывание. Дальнейшую информацию о возможных значениях ICODE можно найти в приложении В.

В SW содержится также код состояния СС. Сохранение SW автоматически спасает значение кода состояния прерванного процесса. Об использовании полей IDLE и ID будет рассказано ниже в этой главе. Поле IDLE определяет, выполняет ли ЦП команды или простаивает. В ID содержится 4-битовое значение, идентифицирующее текущую выполняемую программу.

Оставшееся поле слова состояния (MASK) используется для контроля за разрешением прерываний. Это требуется для того, чтобы избежать потери сохраненной ранее информации о состоянии процесса. Предположим, например, что произошло прерывание по вводу-выводу. Значения SW, PC и других регистров будут сохранены в рабочей области прерывания ввода-вывода, о которой только что было рассказано, а ЦП начнет выполнение обработчика прерываний по вводу-выводу. Если до конца обработки первого прерывания произойдет еще одно, снова будет иметь место контекстное переключение. Однако на этот раз в качестве содержимого регистров, сохраняемого в рабочей области, окажутся значения, используемые обработчиком прерываний. Значения же, сохраненные при первом прерывании, будут утрачены, поэтому вернуть управление программе пользователя, которая в тот момент выполнялась, будет нельзя.

Чтобы избежать этого, нужно не допустить наступления прерываний определенного типа, пока первое из них не будет обработано. Это достигается использованием поля MASK слова состояния. В MASK каждый бит соответствует некоторому классу прерываний. Если какой-то бит установлен в 1, то прерывания соответствующего класса разрешены, если в 0, то запрещены. В последнем случае говорят, что они МАСКИРОВАНЫ (часто их также называют ЗАПРЕЩЕННЫМИ или ЗАКРЫТЫМИ). Однако маскированные прерывания не теряются, потому что сигнал, вызвавший прерывание, сохраняется аппаратурой. Временно задержанное таким способом прерывание называется ОТЛОЖЕННЫМ. Когда, вследствие того что MASK сброшена, прерывания соответствующего класса вновь разрешаются, сигнал опознается и происходит прерывание.

В УУМ/ДС маскирование прерываний находится под контролем операционной системы и зависит от значения MASK в SW, которое заранее сохраняется в рабочей области каждого прерывания. Можно запретить все прерывания, установив все биты MASK в 0. Однако в действительности поступать подобным образом нет необходимости.

Каждому классу прерываний на УУМ/ДС присвоен определенный ПРИОРИТЕТ ПРЕРЫВАНИЙ. Наивысший приоритет имеют SVC-прерывания (Класс I), затем идут программные (Класс II) и т.д. [Не во всех операционных системах приоритеты распределены так, как предлагает автор.- Прим. ред.]. Поле MASK в слове состояния устанавливается в соответствии с классом прерывания так, чтобы все прерывания с равным или более низким приоритетом были запрещены, а с более высоким разрешены. Например, слово состояния, загруженное в результате программного прерывания, будет иметь биты MASK, соответствующие прерываниям - программным, по таймеру и по вводу-выводу, установленными в 0; эти классы прерываний будут запрещены. Бит в MASK для SVC-прерываний будет установлен в 1, поэтому они будут разрешены. Когда прерывания по завершении работы обработчика прерываний открываются, среди ожидающих может оказаться более одного класса прерываний (например, по таймеру и по вводу-выводу). В этом случае первым опознается то, что имеет более высокий приоритет.

Если используется такая система приоритетов, то программа обработки прерываний сама может быть прервана. Каким образом происходит ВЛОЖЕННОЕ прерывание, показано на рис.6.6. По прерыванию по вводу-выводу состояние программы А, выполняющейся в этот момент на ЦП, сохраняется, а управление передается обработчику прерываний по вводу-выводу. Во время его работы происходит новое прерывание - уже по таймеру, в результате чего управление передается обработчику прерываний по таймеру. По завершении обработки этого прерывания при помощи команды LPS из рабочей области прерывания по таймеру восстанавливается состояние ЦП. В результате управление снова передается обработчику прерываний по вводу-выводу. Так как опять загружено старое значение MASK, прерывания по таймеру, которые были запрещены, снова открываются. Однако прерывания по вводу-выводу по-прежнему остаются закрытыми. После завершения обработки прерывания при помощи уже другой LPS восстанавливается состояние ЦП, которое было в момент первого прерывания. Теперь все прерывания открыты, потому что в слове состояния, используемом программой A, все биты MASK установлены в 1.

Бек. Введение в системное программирование. 1988 - Страница 2 8832810
Рис.6.6. Пример вложенного прерывания.

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

6.2.2. ПЛАНИРОВАНИЕ ПРОЦЕССОВ

ПРОЦЕССОМ (process), или ЗАДАНИЕМ (task), часто называется программа, находящаяся в решении. Другие возможные определения процесса можно найти в Дейтел [1984]. Для выполнения вычислительной работы операционная система выделяет процессам ЦП. В однопрограммной операционной системе присутствует только один пользовательский процесс. Однако в мультипрограммной системе на ресурсы может претендовать много независимых процессов. ПЛАНИРОВАНИЕ ПРОЦЕССОВ - это управление распределением ресурсов ЦП между различными конкурирующими процессами путем передачи им управления согласно некоторой стратегии планирования.

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

Процесс создается, когда выполнение задания пользователя начинается, и уничтожается, когда задание завершается. Во время своего существования процесс может находиться в трех состояниях. Процесс активен (running), когда он использует ЦП для выполнения своих команд. Процесс блокирован (blocked), если его выполнение может быть продолжено только после наступления некоторого ожидаемого им события. Например, процесс может быть блокирован, потому что ему требуется ждать завершения операции ввода-вывода. Процессы, которые не блокированы и не активны, называются находящимися в состоянии готовности (ready). Этим процессам будет передано управление после того, как текущий активный процесс его отдаст.

На рис.6.7 показаны возможные переходы из одного состояния в другое. В любой момент времени активным (т.е. использующим ЦП) может быть только один процесс. При передаче управления процессу пользователя операционная система устанавливает интервальный таймер. Тем самым задается квант времени, являющийся максимальным количеством времени ЦП, на которое процесс получает управление. Если это время истекает, процесс переводится из состояния активности в состояние готовности. После этого операционная система, согласно стратегии планирования, выбирает следующий процесс, находящийся в готовности, переводит его в состояние активности и передает ему управление. Выбор процесса и передачу на него управления часто называют ДИСПЕТЧЕРИЗАЦИЕЙ. Часть операционной системы, выполняющая эту функцию, называется ДИСПЕТЧЕРОМ (dispatcher).

Бек. Введение в системное программирование. 1988 - Страница 2 8833010
Рис.6.7. Изменение состояний процесса.

Может оказаться, что активный процесс, не использовав полностью предоставленного ему кванта времени, будет ожидать наступления некоторого события, например завершения операции ввода-вывода. В этом случае активный процесс блокируется, а какой-то новый процесс активизируется. Когда же ожидаемое событие наступает, соответствующий заблокированный процесс переводится в состояние готовности и может снова стать кандидатом на обслуживание. Операции ожидания события и оповещения о том, что событие наступило, реализуются при помощи сервисных запросов операционной системе (с использованием SVC) [Не обязательно с помощью SVC, но и по прерываниям!- Прим. ред.]. Механизм, чаще всего применяемый для установления соответствия между процессами и ожидаемыми ими событиями, рассматривается ниже в данном разделе.

Обычно до своего завершения процесс много раз пребывает в состоянии активности, готовности и блокировки. Для того чтобы это никак не повлияло на результаты вычислений, каждый раз, когда процесс теряет активность, его текущее состояние должно быть сохранено. Когда же процесс снова будет активизирован, это состояние должно быть восстановлено. Информация о состоянии каждого процесса хранится операционной системой в БЛОКЕ СОСТОЯНИЯ ПРОЦЕССА (PSB - Process Status Block). Блок состояния процесса создается, когда процесс входит в решение, и уничтожается, когда процесс завершается. Блок PSB содержит информацию о том, в каком состоянии процесс находился (активности, готовности или блокировки). В нем имеется область, используемая для сохранения машинных регистров (включая SW и PC) и другой всевозможной информации (например, о системных ресурсах, используемых процессом).

Общий вид алгоритма, используемого при диспетчеризации, показан на рис.6.8. При передаче управления от одного процесса другому прежде всего необходимо сохранить информацию о состоянии активного процесса. Если процесс был заблокирован, так как он использовал весь свой квант времени, то информация о состоянии может быть найдена в рабочей области прерывания по таймеру. Если процесс отдал управление (выполнив SVC-запрос), потому что ему требуется дождаться наступления некоторого события, информация о состоянии будет храниться в рабочей области SVC-прерываний. Конечно, может оказаться, что активных процессов нет. Это может случиться, например, если все процессы в системе находятся в заблокированном состоянии. Тогда информацию о состоянии сохранять не надо.

Бек. Введение в системное программирование. 1988 - Страница 2 8833110
Рис.6.8. Алгоритм диспетчеризации.

После сохранения состояния предыдущего активного процесса диспетчер выбирает для активизации новый процесс. Чтобы задать квант времени, выделяемый выбранному процессу, диспетчер устанавливает интервальный таймер, затем, используя команду LPS для загрузки информации о состоянии, хранимой в PSB данного процесса, осуществляет передачу управления. Если процесса, находящегося в состоянии готовности, нет, то диспетчер для перевода ЦП в состояние ПРОСТОЯ (idle), использует LPS, загружая слово состояния с IDLE=1 (рис.6.5).

Выбор следующего процесса для диспетчеризации осуществляется несколькими способами. В первом из них, называемом КРУГОВЫМ (robin round), все процессы считаются равноценными. Диспетчер циклически просматривает все PSB, выбирая следующий процесс из тех, что находятся в состоянии готовности. Каждому активизируемому процессу предоставляется одинаковый квант времени.

В более сложных методах диспетчеризации выбор процессов происходит по приоритетам. В некоторых системах приоритеты определены заранее в соответствии с характером заданий пользователей. Задачей таких систем является обеспечение для каждого класса заданий необходимого уровня сервиса. В других системах приоритеты могут назначаться самой операционной системой в целях увеличения производительности всей системы. Приоритеты могут меняться и динамически в зависимости от загрузки и производительности системы. Возможно, что наряду с использованием системы приоритетов различным процессам будут выделены различные кванты времени. Дальнейшее обсуждение этих и более сложных способов диспетчеризации может быть найдено в Дейтел [1984] и Лорин [1981].

Когда активный процесс достигает точки, в которой ему требуется ждать наступления некоторого события, он сообщает об этом операционной системе при помощи запроса на обслуживание WAIT (SVC 0). О наступлении события, которого могут ожидать и другие процессы, операционной системе сообщается посредством запроса SIGNAL (SVC 1). В разд.6.2.3 будут даны примеры использования WAIT и SIGNAL; здесь же мы коснемся того, как эти запросы соотносятся с функцией планирования процессов.

На рис.6.9 приводится последовательность действий, выполняемых операционной системой в ответ на подобные сервисные запросы. Событие, которое ожидалось или о наступлении которого было сообщено, определяется посредством задания адреса соответствующего БЛОКА СОСТОЯНИЯ СОБЫТИЯ (ESB - Event Status Block). Блок состояния события содержит битовый флаг ESBFLAG, в котором отмечается, произошло ли соответствующее событие или нет. Блок ESB содержит также указатель на ESBQUEUE - список процессов, ожидающих в этот момент наступления данного события. Дальнейшая информация о ESB, примерах их создания и использования приводится в разд.6.2.3.

Бек. Введение в системное программирование. 1988 - Страница 2 8833210
Рис.6.9. Алгоритмы обработки WAIT (SVC 0) и SIGNAL (SVC 1).

Запрос WAIT выдается активным процессом и говорит о том, что процесс не может продолжить работу до наступления события, соответствующего ESB. Таким образом, в алгоритме обработки WAIT первым делом исследуется ESBFLAG и, если событие уже наступило, управление возвращается запрашивающему процессу. Если событие еще не наступило, активный процесс переводится в состояние блокировки и заносится в ESBQUEUE.

Запрос SIGNAL выдается процессом, обнаружившим наступление некоторого события, соответствующего ESB. Следовательно, установкой ESBFLAG алгоритм для обработки SIGNAL отмечает, что событие наступило. Затем осуществляется сканирование ESBQUEUE, списка ожидающих этого события процессов. Каждый процесс в списке переводится из состояния блокировки в состояние готовности. Затем управление возвращается процессу, сделавшему запрос SIGNAL.

Если используемый метод диспетчеризации основан на приоритетах, для обработки SIGNAL применяется несколько другой алгоритм. В таких системах может случиться, что один или более процессов, перешедших в состояние готовности, имеют более высокий приоритет, чем текущий активный процесс. Учитывая это, алгоритм для обработки SIGNAL вместо прямой передачи управления активному процессу должен сначала вызвать диспетчер, который и передаст управление процессу с наивысшим приоритетом, находящемуся в текущий момент в состоянии готовности. Этот способ называется планированием процессов по приоритетам. Он позволяет процессу, перешедшему в состояние готовности, перехватить управление у процесса, активного в этот момент, но имеющего меньший приоритет, не ожидая истечения кванта времени этого процесса.
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Пн Фев 15, 2021 12:50 pm

6.2.3. ОБСЛУЖИВАНИЕ ВВОДА-ВЫВОДА

На типичной мини-ЭВМ, такой как УУМ, ввод-вывод осуществляется побайтно. Например, для чтения данных программа должна иметь цикл, в котором опрашивается состояние устройства ввода-вывода и выполняется ряд команд ЧТЕНИЯ ДАННЫХ. В подобных системах ЦП участвует в передаче и приеме каждого байта с устройства ввода-вывода. Пример программирования ввода-вывода такого рода показан на рис.2.1.

В более совершенных ЭВМ, таких как УУМ/ДС, для отслеживания всех деталей передачи данных и управления вводом-выводом используются каналы ввода-вывода. На рис.6.10 приведена типичная для УУМ/ДС конфигурация ввода-вывода. Пусть, например существует 16 каналов, к каждому из которых может быть подключено до 16 устройств. В номер, присвоенный устройству ввода-вывода, входит и номер канала, к которому оно подключено. Например, устройство, обозначенное 20-2F, подключено к Каналу 2.

Бек. Введение в системное программирование. 1988 - Страница 2 8833410
Рис. 6.10. Типичная конфигурация ввода-вывода для УУМ/ДС.

Последовательность операций, которые должен выполнить канал, задается КАНАЛЬНОЙ ПРОГРАММОЙ, состоящей из набора КАНАЛЬНЫХ КОМАНД. Для осуществления операции ввода-вывода ЦП выполняет команду ввода-вывода START I/O (SIO), в которой задается номер канала и начальный адрес канальной программы. Затем канал выполняет указанную операцию ввода-вывода без дальнейшего вмешательства ЦП. По завершении своей программы канал генерирует прерывание по вводу-выводу. Одновременно могут работать несколько каналов, каждый из которых выполняет свою собственную канальную программу; таким образом, в одно и то же время могут осуществляться несколько различных операций ввода-вывода. Каждый канал работает независимо от ЦП, поэтому, пока выполняются операции ввода, ЦП может продолжать вычисления.

Операционная система ЭВМ типа УУМ/ДС вовлекается в процесс ввода-вывода в нескольких случаях. Система должна принять запросы на ввод-вывод от пользовательских программ и сообщить им, когда операции будут выполнены. Она может также управлять работой каналов ввода-вывода и обрабатывать генерируемые ими прерывания. В оставшейся части раздела мы обсудим, как эти функции выполняются, и проиллюстрируем этот процесс несколькими примерами.

Программа на УУМ/ДС запрашивает операцию ввода-вывода посредством выполнения команды SVC 2, параметрами которой являются номер канала, начальный адрес канальной программы и адрес блока состояния события, используемый при оповещении о завершении операции ввода-вывода. Если программа должна ждать результаты операции ввода-вывода, то она выполняет команду SVC 0 (WAIT). Этой командой задается адрес блока состояния события, соответствующего ожидаемой операции ввода-вывода. Таким образом, выполнение операции ввода-вывода имеет вид

SVC 2 (запрос операции ввода-вывода)
.
.
.
SVC 0 (ожидание результата)

В некоторых случаях WAIT может следовать сразу за запросом операции ввода-вывода. Однако, так как вычисления и ввод-вывод могут идти одновременно, имеется возможность продолжить выполнение программы, пока ожидаются результаты операции ввода-вывода.

Более подробно это иллюстрируется программой на рис.6.11. Сначала программа загружает в регистры начальный адрес канальной программы, номер канала и адрес блока состояния события. После этого для запроса операции ввода-вывода программа выполняет команду SVC. Канальная программа для чтения, задаваемая в виде последовательности элементов данных, содержит две канальные команды. Первой командой задается операция чтения, которая должна быть выполнена на подсоединенном к каналу устройстве с номером 1; 256 байт данных должны быть размещены в памяти, начиная с адреса BUFIN. Вторая команда вызывает останов канала и генерирует прерывание по вводу-выводу. Блок состояния события состоит из 3-байтовой области данных. Первым битом ESB является флаг, используемый для индикации, наступило уже соответствующее событие или нет (0=нет, 1=да). Остальная часть ESB служит для сохранения указателя на очередь процессов, ожидающих это событие. Если ждущих процессов нет, то значение указателя равно нулю. Так, начальное значение ESB X'000000' показывает, что соответствующее событие еще не наступило, и что ожидающие его процессы отсутствуют. Дальнейшие подробности, касающиеся формата канальных команд УУМ/ДС и блока состояния события, можно найти в приложении В.

Бек. Введение в системное программирование. 1988 - Страница 2 8833610
Рис.6.11. Пример выполнения ввода-вывода с использованием запросов.

После выдачи запроса на ввод-вывод программа на рис.6.11 выполняет команду SVC 0. Регистр A содержит адрес ESB, соответствующего ожидаемому событию. Таким событием в данном случае является только что затребованная операция ввода-вывода. После завершения операции чтения программа заносит введенные данные в рабочую область. Затем, программа опять инициализирует ESB и запрашивает операцию ввода-вывода для чтения следующих 256 байт данных. Пока эта операция выполняется, программа может обрабатывать уже прочитанные данные, совмещая тем самым функции вычисления и ввода. По завершении обработки управление снова передается на начало основного цикла для ожидания конца следующей операции чтения.

Несколько более сложный пример приведен на рис.6.12. В этом случае программа копирует 4096 байт записей данных с устройства 22 на устройство 14. Имеются две канальные программы: одна для чтения и другая для записи, а также два блока состояния события. В основном цикле программы сначала выдается запрос на чтение, а затем ожидается завершение этого чтения и предыдущей записи. По окончании обеих операций программа создает последовательность вывода и выдает запрос на запись. Однако в этот момент ESB для операции записи имеет начальное значение X'800000', где первый бит равен 1. Этим отмечается, что соответствующее событие уже наступило и при вызове стандартной программы WAIT операционной системы управление будет сразу возвращено программе пользователя (рис.6.9).

Бек. Введение в системное программирование. 1988 - Страница 2 8833810
Рис.6.12. Программа, иллюстрирующая многократный запрос на ввод-вывод.

Операции ввода-вывода в программе на рис.6.12 выполняются независимо друг от друга, так как ими используются разные каналы. Одна операция может быть завершена раньше другой. Возможно также, что обе операции будут выполнены фактически одновременно. Программа в состоянии координировать взаимосвязанные операции ввода-вывода, так как им соответствуют разные ESB. На примере этой программы показано, как каналы могут использоваться для выполнения совмещенных операций ввода-вывода. Ниже такое совмещение будет рассмотрено подробнее.
В программах на рис.6.11 и 6.12 показано, как осуществляются запросы на ввод-вывод с точки зрения пользователя. Сейчас мы готовы обсудить, как они в действительности обрабатываются машиной и операционной системой. В аппаратуре УУМ/ДС предусмотрено наличие в памяти, соответствующей каналу ввода-вывода, РАБОЧЕЙ ОБЛАСТИ КАНАЛА. В ней содержатся стартовый адрес текущей канальной программы, если она имеется, и адрес ESB, соответствующего текущей операции. Результат работы операции ввода-вывода после ее завершения отображается находящимися в рабочей области канала флагами состояния, такими как нормальное завершение, ошибка ввода-вывода или устройство недоступно. Рабочая область программы содержит также указатель на очередь запросов на ввод-вывод для данного канала, которая поддерживается стандартными программами операционной системы. Дополнительные сведения о том, где располагаются рабочие области каналов УУМ/ДС и что в них находится, содержатся в приложении В.

На рис. 6.13 отмечены действия, предпринимаемые операционной системой в ответ на запрос ввода-вывода от программы пользователя. Если требуемый канал занят выполнением другой операции, операционная система ставит запрос в очередь к нему. В противном случае она запускает канал, а текущий запрос сохраняет в рабочей области канала. Возможно также, что управление будет возвращено процессу, затребовавшему ввод-вывод, и он, пока ведется обмен, сможет продолжить работу.

Бек. Введение в системное программирование. 1988 - Страница 2 8833910
Рис.6.13. Алгоритм обработки запроса на ввод-вывод (SVC 2).

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

Бек. Введение в системное программирование. 1988 - Страница 2 88339a10
Рис.6.14. Алгоритм обработки прерывания по вводу-выводу.

Если установлено, что операция ввода-вывода завершилась нормально, обработчик прерывания оповещает об этом операционную систему через заданный при запросе блок состояния события. Это может быть сделано как при помощи SVC-запроса, приводящего к вложенному прерыванию, так и непосредственно вызовом той части операционной системы, которая обрабатывает запросы SIGNAL. В любом случае окончание операции ввода-вывода отмечается в ESB. Каждый ожидающий этого процесс снова переводится в состояние готовности (см. разд.6.2.2). Затем обработчик прерываний по вводу-выводу просматривает очередь запросов к данному каналу и начинает выполнение следующего запроса (если он есть).

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

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

Выполнение всех функций обслуживания ввода-вывода и планирования процессов демонстрируется на рис.6.15. Два пользовательских процесса, обозначенные P1 и P2, выполняются одновременно. Это те же процессы, что показаны на рис.6.11 и 6.12. Мы считаем, чта кванты времени, выделяемые каждому процессу, достаточно большие; поэтому прерывание по таймеру до того, как процессу требуется отдать управление по какой-то другой причине, обычно не происходит. На диаграмме рис.6.15 показана последовательность действий, выполняемых центральным процессором, разделенным между процессами пользователей, частями операционной системы и двумя каналами ввода-вывода. Начало шкалы времени на диаграмме находится вверху. Величина промежутков на шкале не обязательно пропорциональна действительной длительности обозначаемого ими времени. Последовательные номера призваны помочь в использовании данного примера.

Бек. Введение в системное программирование. 1988 - Страница 2 8834110

Бек. Введение в системное программирование. 1988 - Страница 2 8834210
Рис.6.15. Пример управления вводом-выводом и функции планирования процессов.

В начале примера процессы P1 и P2 уже инициированы; P1 был активизирован первым. Оба канала ввода-вывода свободны. В момент (1) процесс P1, выполнив команду SVC, осуществляет свой первый запрос ввода-вывода. Этим вызывается прерывание, в результате которого управление передается блоку обработки SVC-прерываний. Чтобы облегчить дальнейшие ссылки, на диаграмме эта операция ввода-вывода помечена (а). В запросе ввода-вывода задан свободный в этот момент канал с номером 1. Поэтому после запуска блока обработки SVC-прерываний канальной программы управление возвращается процессу P1 (момент 2).

В (3) выполнением другой команды SVC (SVC 0) P1 выдает для операции (а) запрос WAIT. Управление еще раз передается блоку обработки SVC-прерываний. В ESB, заданном этим запросом, отмечено, что ожидаемое событие еще не наступило. Поэтому после перевода процесса P1 в состояние блокировки вызывается диспетчер (4), который передает управление процессу P2 (5). В момент (6) P2 выдает свой первый запрос ввода-вывода, предназначенный устройству 2 канала 2. Так как канал 2 свободен, запускается операция ввода-вывода, а управление возвращается к P2. В (8 ) P2 должна ждать завершения ввода-вывода; так как этого еще не произошло, P2 блокируется. В (9), как и раньше, вызывается диспетчер. Однако в этот момент оба процесса блокированы, поэтому диспетчер переводит ЦП в состояние простоя. Заметим, что оба канала ввода-вывода по-прежнему активны.

Центральный процессор простаивает до момента (11), в который канал 1 завершает операцию ввода-вывода. Канал генерирует прерывание, в результате которого ЦП выводится из состояния простоя, а управление передается обработчику прерываний по вводу-выводу. Определив, что операция завершилась нормально, обработчик прерываний выдает для соответствующего ESB запрос SIGNAL (SVC 1).Тем самым управление передается блоку обработки SVC-прерываний (12). Процесс P1, ожидающий изменения ESB, переводится в состояние готовности. Затем блок обработки SVC-прерываний возвращает управление обработчику прерываний по вводу-выводу (13). Диспетчер, вызванный в момент (14), в момент (15) передает управление процессу PL После завершения работы канала 2 в (16) выполняется аналогичная последовательность операций; в результате процесс P2 переводится в состояние готовности. Поскольку ЦП в момент прерывания не простаивал, управление P2 сразу не передается, а возвращается обработчиком прерываний по вводу-выводу процессу PL Процесс P2 не получит управления до тех пор, пока P1 не выдаст в (22) следующего запроса WAIT.

Читателю, чтобы убедиться, что он хорошо понимает тонкости передачи управления при различных прерываниях, следует внимательно просмотреть весь пример до конца. Для этого будет полезно обратиться к алгоритмам на рис.6.8, 6.9, 6.13 и 6.14. В особенности обратите внимание на разнообразие видов одновременной работы ЦП и выполнения операций ввода-вывода. Обеспечение гибкого следования заданий одно из основных преимуществ операционной системы с прерываниями.
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Пн Фев 15, 2021 12:58 pm

6.2.4. УПРАВЛЕНИЕ РЕАЛЬНОЙ ПАМЯТЬЮ

Любая операционная система, поддерживающая одновременную работу более одного пользователя, должна обладать механизмом разделения центральной памяти между совместно выполняющимися процессами. Многие мультипрограммные системы разбивают память на РАЗДЕЛЫ (partitions) с выделением каждому процессу своего раздела. Размер и расположение разделов могут быть либо заранее заданы (РАЗДЕЛИ ФИКСИРОВАННОГО РАЗМЕРА), либо назначаться динамически в процессе выполнения заданий (РАЗДЕЛЫ ПЕРЕМЕННОГО РАЗМЕРА).

Ниже рассматриваются некоторые способы управления разделением памяти. В примерах будет использована последовательность заданий, приведенная на рис.6.16. Предполагается, что УРОВЕНЬ МУЛЬТИПРОГРАММИРОВАНИЯ (т.е. количество одновременно выполняющихся заданий) ограничен только числом самих заданий, загружаемых в центральную память.

Бек. Введение в системное программирование. 1988 - Страница 2 8834410
Рис.6.16. Задания пользователей для примеров по распределению памяти.

На рис.6.17 показано разбиение памяти на разделы фиксированного размера. Полный объем доступной памяти ЭВМ предполагается равным 50000 байт; операционная система занимает первые 10000 байт. Заметим, что эти и другие размеры и адреса, используемые в данном разделе, даны в шестнадцатеричном виде. Память, не занятая операционной системой, состоит из четырех разделов. Раздел 1 начинается с адреса 10000 сразу за операционной системой и имеет длину 18000 байт. Остальные разделы следуют в таком порядке: Разделы 2 и 3 имеют по 10000 байт каждый, а Раздел 4 - длину 8000 байт.

Бек. Введение в системное программирование. 1988 - Страница 2 8834510
Рис.6.17. Распределение памяти для заданий рис.6.16 с использованием разделов фиксированного размера.

В простой схеме распределения с разделами фиксированного размера каждое входящее задание загружается в наименьший подходящий по объему раздел. Если размер раздела превосходит размер задания, то оставшаяся внутри раздела память не используется. Система, имея вначале пустыми все четыре раздела, первым делом загрузит Задание 1 в Раздел 2. Затем Задание 2 будет загружено в единственный достаточно большой для этого Раздел 1. Задания 3 и 4 загружаются в Разделы 3 и 4. После этого все разделы оказываются занятыми, поэтому больше заданий загрузить нельзя. Получившееся в итоге распределение памяти показано на рис.6.17а. Заштрихованные области на диаграмме соответствуют неиспользованной памяти.

Однажды загруженное в раздел задание остается там до конца своего выполнения. После того как задание завершится, занимаемый им раздел вновь становится доступным для использования. На рис.6.17б Задание 2 уже окончилось и в Раздел 1 было загружено Задание 5. На этом же рисунке показаны дальнейшие действия, осуществляемые по мере того, как оканчиваются остальные задания.

Заметим, что сами разделы и их расположение остаются фиксированными вне зависимости от размеров занимающих их заданий. Начальный выбор величины раздела в схеме с разделами фиксированного размера очень важен. Число больших разделов должно быть достаточным, чтобы длинные задания могли выполняться без слишком большой задержки. Однако если больших разделов слишком много, то при выполнении коротких заданий значительное количество памяти расходуется впустую. Использование разделов фиксированного размера наиболее эффективно, когда размеры большинства заданий находятся в пределах конкретных объемов, а распределение размеров заданий меняется не часто. Это позволяет эффективно использовать доступную память посредством выделения набора разделов ожидаемому множеству заданий.

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

Бек. Введение в системное программирование. 1988 - Страница 2 8834610
Рис.6.18. Распределение памяти для заданий рис. 6.16 с использованием разделов переменного размера.

Вначале вся память (кроме той, что отведена операционной системе) не распределена, так как заранее нет определенных разделов. Раздел для Задания 1 создается при его загрузке. Предположим, что этот раздел расположен непосредственно за операционной системой. Затем Заданию 2 отводится раздел, следующий сразу за Заданием 1, и т.д. Объем свободной памяти, оставшейся за Заданием 5, для загрузки очередного задания уже недостаточен.

Когда завершается Задание 2, его раздел освобождается, и новый Раздел отводится Заданию 6. Как показано на рис.6.18б, этот новый раздел занимает часть памяти, которая отводилась Заданию 2. Остаток от прежнего раздела Задания 2 остается свободным. Теперь имеются две несмежные свободные области памяти; однако ни одна из них не велика настолько, чтобы вместить еще одно задание. На рис.6.18в и г показано, как происходит освобождение и распределение памяти по мере того, как оканчиваются другие задания.

При использовании разделов переменного размера нет надобности выбирать размер раздела заранее. Однако операционной системе, которая отслеживает, какие области памяти уже распределены, а какие свободны, приходится проделывать большую работу. Обычно это делается системой при помощи поддерживаемого ею связного списка свободных областей. Этот список просматривается при выделении нового раздела, который размещается либо в первой (ПЕРВОЕ ПОДХОДЯЩЕЕ РАЗМЕЩЕНИЕ), либо в наименьшей (НАИБОЛЕЕ ПОДХОДЯЩЕЕ РАЗМЕЩЕНИЕ) подходящей для него свободной области. Когда раздел освобождается, отведенная ему память объединяется со всеми смежными свободными областями и заносится в список. Подробное обсуждение и сравнение алгоритмов распределения памяти можно найти в Стендиш [1980].

Вне зависимости от используемого способа создания разделов необходимо, чтобы операционная система и аппаратные средства обеспечивали ЗАЩИТУ ПАМЯТИ. При выполнении задания в одном разделе недопустимо, чтобы оно изменяло ячейки памяти другого раздела или операционной системы. В некоторых системах разрешено чтение данных из любой области памяти, а запись - только внутри отведенного заданию раздела. В других системах и чтение, и запись допускаются только в пределах собственного раздела задания.

Для эффективной защиты памяти необходима и аппаратная поддержка. Можно, например, ввести пару граничных регистров, в которых будут содержаться начальный и конечный адреса раздела задания. Эти регистры не доступны непосредственна программам пользователя, и могут быть использованы, только если ЦП находится в режиме супервизора. Операционная система устанавливает граничные регистры, когда пользовательскому заданию назначается раздел. Во время операций контекстного переключения, аналогичных тем, что вызваны командой LPS или прерыванием, содержимое этих регистров автоматически сохраняется. Таким образом, граничные регистры всегда содержат адреса начала и конца раздела, отведенного текущему активному процессу. При любом обращении к памяти аппаратура автоматически сверяет адрес, по которому идет обращение, с граничными регистрами. Если адрес находится вне раздела текущего задания, обращение к памяти не производится и генерируется программное прерывание.

В УУМ/ДС используется и другой способ защиты памяти. Каждому 800-байтовому (в шестнадцатеричной системе счисления) блоку памяти ставится в соответствие 4-битовый ключ защиты памяти. Эти ключи могут быть установлены операционной системой при помощи привилегированной команды SSK (Set Storage Key - Установи ключ памяти). Каждый пользовательский процесс имеет поставленный ему в соответствие 4-битовый ИДЕНТИФИКАТОР ПРОЦЕССА, который хранится в поле ID слова состояния SW. Когда заданию выделяется некоторый раздел, операционная система присваивает ключам всех блоков памяти внутри раздела значение идентификатора процесса этого задания. При каждом обращении пользовательской программы к памяти аппаратура автоматически сравнивает идентификатор процесса из SW с ключом защиты адресуемого блока памяти. Если значения этих двух полей не совпадают, то обращения к памяти не происходит и генерируется программное прерывание. Однако если ЦП находится в режиме супервизора, то этой проверки не производится; операционная система может обращаться к любой ячейке памяти.

Единой проблемой для всех общецелевых способов динамического распределения является ФРАГМЕНТАЦИЯ ПАМЯТИ. Фрагментация имеет место, когда доступная свободная память разбита на несколько несмежных блоков, каждый из которых слишком мал для использования. Рассмотрим, например, рис.6.18в. Чтобы разместить Задание 7, в целом имеется более чем достаточно свободной памяти; однако из-за того, что нет ни одного свободного блока достаточно большого размера, оно не может быть загружено.

На рис.6.19 показано одно из возможных решений этой проблемы: использование перемещаемых разделов (relocatable partitions). После окончания каждого задания оставшиеся разделы передвигаются как можно дальше к одному концу памяти В результате этого вся доступная свободная память собирается в один общий блок, который больше подходит для распределения новых разделов. Как показано на рис.6.19, этот способ может привести к более эффективному использованию памяти по сравнению с тем, что достигается с помощью неперемещаемых разделов. Однако копирование заданий из одного места памяти в другое может потребовать значительного количества времени. Этот недостаток часто перевешивает преимущества усовершенствованного использования памяти.

Бек. Введение в системное программирование. 1988 - Страница 2 8834910
Рис.6.19. Распределение памяти для заданий рис.6.16 с использованием перемещаемых разделов.

При работе с перемещаемыми разделами возникают проблемы и с переместимостью программ. Рассмотрим, например, программу, приведенную на рис.6.20а. Команда STA имеет расширенный формат, поэтому она в оттранслированном виде содержит истинный адрес 08108. При использовании методов, описанных в гл.2 и 3, это адресное поле будет отмечено ассемблером. Чтобы получить истинный адрес, по которому загружена программа, значение адресного поля модифицируется загрузчиком. Например, если начальная загрузка P3 осуществлена по истинному адресу 2E000, то адресное поле команды STA, как показано на рис.6.20б, будет изменено на 36108.

Бек. Введение в системное программирование. 1988 - Страница 2 8835010
Рис.6.20. Использование регистра перемещений при вычислении адреса.

Предположим теперь, что раздел, содержащий P3, будет начинаться не с адреса 2E000, как на рис.6.19а, а с адреса 1A000, как на рис.6.19б. Тогда адрес в команде STA будет неверным. Действительно, он будет указывать на ячейку памяти, которая является частью раздела, отведенного другому заданию. Аналогичная проблема возникнет, если P3 загрузит в базовый регистр адрес некоторой своей части или создаст структуру данных, использующую указатель адреса. После начальной загрузки программы все перемещаемые значения определены ассемблером, что упрощает перемещение программы. Однако во время своего выполнения программа может использовать регистры и ячейки памяти произвольным образом. В общем случае операционная система не в состоянии определить, какие значения являются адресами, а какие другими типами данных. Таким образом, перемещение программы во время выполнения осуществить намного сложнее, чем после начальной загрузки.

Применение перемещаемых разделов на практике требует некоторой аппаратной поддержки. Один из обычно используемых для этого способов показан на рис.6.20в. В этом случае существует специальный регистр перемещения, устанавливаемый операционной системой и содержащий начальный адрес текущей активной программы. Во время операций контекстного переключения содержимое этого регистра автоматически сохраняется или восстанавливается, а при перемещении программы на новое место его значение изменяется операционной системой. При любом обращении программы пользователя к памяти значение, содержащееся в регистре перемещения, автоматически складывается с адресом. Например, команда STA на рис.6.20в обращается к адресу 08108; однако, учитывая регистр перемещения, истинный адрес, к которому осуществляется обращение, равен 36108. Если начало программы P3 было перенесено на адрес 1A000, как показано на рис.6.20г, то операционная система изменит значение регистра перемещений для P3 на 1А000. Таким образом, адрес, к которому действительно обращается команда STA, будет равен 22108.

Важно понять, что регистр перемещения управляется операционной системой; пользовательской программе он не доступен. Таким образом, этот регистр в корне отличается от задаваемых программно базовых регистров, имеющихся в УУМ/ДС, System/370 и многих других ЭВМ. Регистр перемещения автоматически включается каждый раз, когда программа обращается к какой-нибудь области памяти; этим достигается тот же эффект, что и при фактической загрузке каждой программы с истинного адреса 00000. И в самом деле, на многих ЭВМ для программы пользователя не существует прямого способа определить ее реальное месторасположение в памяти. Таким образом, этот тип перемещений применим к адресам, находящимся в указателях структур данных, к значениям в базовых регистрах, равно как и к адресам в командах. Заметим также, что подобное автоматическое перемещение, выполняемое аппаратурой, полностью исключает необходимость перемещения программ загрузчиком.

В этом разделе были рассмотрены методы распределения программных разделов с заранее определенными размерами. В некоторых операционных системах программам пользователей во время их выполнения разрешается динамически запрашивать дополнительную память. Дополнительно выделяемая память не обязательно должна быть смежной с первоначально назначенным разделом. Подобное динамическое распределение памяти обычно осуществляется методами, схожими с теми, что использовались при управлении памятью для структур данных. Хорошее изложение этих методов можно найти в Стендиш [1980].
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Пн Фев 15, 2021 1:03 pm

6.2.5. УПРАВЛЕНИЕ ВИРТУАЛЬНОЙ ПАМЯТЬЮ

ВИРТУАЛЬНЫМ (virtual) называется ресурс, который пользовательской программе представляется обладающим свойствами отличными от тех, что он в действительности имеет. Рассмотрим, например, рис.6.21. Программе разрешено использовать большую ВИРТУАЛЬНУЮ ПАМЯТЬ, называемую иногда ВИРТУАЛЬНЫМ АДРЕСНЫМ ПРОСТРАНСТВОМ. Его объем может даже превосходить всю доступную реальную память на ЭВМ. Содержимое виртуальной памяти, используемой программой, хранится на некотором внешнем устройстве (внешней памяти). По необходимости части этой виртуальной памяти отображаются в реальную память. Ни о внешней памяти, ни о ее отображении в реальную память программа ничего не знает. Она написана так, как будто бы виртуальная память существует в действительности.

Бек. Введение в системное программирование. 1988 - Страница 2 8835210
Рис.6.21. Основная концепция виртуальной памяти.

В этом разделе мы рассмотрим один из общих методов реализации виртуальной памяти - РАЗМЕЩЕНИЕ СТРАНИЦ ПО ЗАПРОСУ (demand paging). Ссылки на литературу, содержащую описания других видов виртуальной памяти, приведены в конце раздела.

В системе с размещением страниц по запросу виртуальная память процесса разделена на страницы некоторой фиксированной длины. Реальная память ЭВМ разделена на страничные кадры (page frames) той же длины, что и страницы. Любая страница любого процесса потенциально может быть загружена в любой страничный кадр реальной памяти. Отображение страниц в страничные кадры описывается ТАБЛИЦЕЙ ОТОБРАЖЕНИЯ СТРАНИЦ (PMT - Page Map Table); для каждого процесса в системе имеется одна PMT. Таблица отображения страниц используется аппаратурой для преобразования адресов виртуальной памяти программы в соответствующие адреса реальной памяти. Процесс преобразования похож на использование регистра перемещений, рассмотренного в предыдущем разделе. Однако здесь для каждой страницы в PMT имеется свое значение. Такой перевод виртуальных адресов в реальные называется ДИНАМИЧЕСКИМ ПРЕОБРАЗОВАНИЕМ АДРЕСОВ.

Рассмотренные понятия иллюстрируются программой на рис.6.22. Программа разбита на страницы длиной 1000 (шестнадцатеричный) байт. Виртуальные адреса с 0000 по 0FFF принадлежат Странице 0, адреса с 1000 по 1FFF - Странице 1 и т.д. Когда начинается выполнение программы, операционная система загружает Страницу 0, содержащую первую исполняемую команду, в некоторый страничный кадр реальной памяти. Остальные страницы загружаются в память по мере надобности.

Бек. Введение в системное программирование. 1988 - Страница 2 8835310
Рис.6.22. Программа, иллюстрирующая размещение страниц по запросу.

Бек. Введение в системное программирование. 1988 - Страница 2 8835410
Рис.6.22. Продолжение.

Процессы динамического преобразования адресов и загрузки страниц иллюстрируются рис.6.23а. Страница 0 программы загружена в страничный кадр 1D (т.е. в адреса реальной памяти с 1D000 по 1DFFF). Рассмотрим сначала команду JEQ, находящуюся по виртуальному адресу 0103. Адрес операнда этой команды - виртуальный адрес 0420. Для простоты в примере использован формат команды, допускающий прямую адресацию. Адрес операнда 0420 находится на Странице 0 и имеет смещение 420 от ее начала. В таблице отображения страниц показано, что Страница 0 программы загружена в страничный кадр 1D (начинающийся с адреса 1D000). Таким образом, реальный адрес, вычисленный при динамическом преобразовании адресов, равен 1D420.

Бек. Введение в системное программирование. 1988 - Страница 2 8835510
Рис.6.23. Примеры динамического преобразования адресов и размещения страниц по запросу.

Рассмотрим далее команду LDA с виртуальным адресом 0420. Ее операнд расположен по виртуальному адресу 6FFA (Страница 6, смещение FFA). Однако эта страница в реальную память еще не загружена, поэтому аппаратные средства динамического преобразования адресов вычислить реальный адрес не могут. Вместо этого они генерируют специальный тип программного прерывания, называемого ПРЕРЫВАНИЕМ ПО ОТСУТСТВИЮ СТРАНИЦЫ или СТРАНИЧНЫМ ПРЕРЫВАНИЕМ (рис.6.23б). В ответ на это стандартная программа обработки прерываний, которая будет рассмотрена позже, загружает требуемую страницу в некоторый страничный кадр. Предположим, что его номер - 29. После этого заново выполняется команда, вызвавшая прерывание. На этот раз, как показано на рис.6.23в, динамическое преобразование адреса проходит успешно.

Остальные страницы программы загружаются в случае необходимости подобным же образом. Предположим, что на рис.6.22 показаны все команды перехода (JUMP) в программе, равно как и все команды, операнды которых находятся на других страницах. Когда управление передается от последней команды на Странице 0 первой на Странице 1, операция выборки команды вызывает страничное прерывание, в результате которого загружается Страница 1. Команда STA с виртуальным адресом 1840 вызывает загрузку Страницы 8. Затем в результате выполнения цикла выборки команд загружается Страница 2, так же как перед этим Страница 1. Рассмотрим теперь две команды JUMP с адресами 2020 и 2024. Осуществление первого из этих переходов (т.е. условие "меньше чем " истинно) вызывает загрузку Страницы 4; иначе же Страница 4 остается незагруженной. В последнем случае команда безусловного перехода с адресом 2024 передает управление на ячейку Страницы 1 (которая уже загружена). После передачи управления на Страницу 4 выполняется команда STA с адресом 4A20. Ею задается адрес 6FFA операнда с индексное адресацией. Предположим, что содержимое индексного регистра равно 8. Тогда целевой адрес равен 7002 и выполнение команды приводит к загрузке Страницы 7.

На рис.6.24 показана ситуация, возникшая после только что описанных событий. По таблице отображение страниц можно судить, что Страницы 0, 1, 2, 4, 6, 7 и 8 загружены; в ней даны также соответствующие номера страничных кадров. Для каждой программы в системе имеется аналогичная таблица. Заметим, что в ней указаны также и страницы, которые с момента загрузки были модифицированы (здесь - Страницы 7 и 8 ). Эта информация используется программой обработки страничных прерываний при выталкивании страницы из памяти.

Бек. Введение в системное программирование. 1988 - Страница 2 8835710
Рис.6.24. Отображение виртуальное-реальное с использованием таблиц отображения страниц.

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

Бек. Введение в системное программирование. 1988 - Страница 2 8835810
Рис.6.25. Алгоритмы динамического преобразования адресов и обработки страничных прерываний.

На рис.6.25б показана программа обработки прерывания, вызванного отсутствием страницы. Операционная система содержит таблицу, описывающую состояние всех страничных кадров, На первом шаге обработки прерывания таблица просматривается в поисках свободного страничного кадра. Если он найден, требуемая страница может быть загружена немедленно. В противном случае страница, находящаяся в памяти, должна быть вытолкнута, чтобы освободить место для той, которую требуется загрузить. Если выталкиваемая страница с момента загрузки была модифицирована, то ее новая версия должна быть переписана во ВНЕШНЮЮ ПАМЯТЬ (backing store), Если нет, то она может быть просто уничтожена.

Программа обработки страничных прерываний, показанная на рис.6.25б, требует выполнения по крайней мере одной операции ввода-вывода. Поэтому для ее работы нужно намного больше времени, чем для любого другого обработчика прерываний из рассмотренных нами выше. Часто бывает нежелательно закрывать прерывания на длительное время. Ясно, например, что предпочтительнее во время страничного обмена дать возможность использовать ЦП другим программам. Для управления подобным мультипрограммированием требуется, чтобы прерывания были разрешены. Поэтому при прерывании по отсутствию страниц сначала определяется, какое действие требуется выполнить, и сохраняется информация о состоянии прерванного процесса, а затем в течение оставшегося времени обработки разрешается функционирование системы прерываний.

Сложнее производится выбор обработчиком прерываний страничного кадра для записи требуемой страницы. Кадр ПОМЕЧАЕТСЯ как занятый, чтобы он не был выбран в следующий раз. Для того чтобы информация о состоянии не была уничтожена во время следующего прерывания, обработчик прерываний берет ее из рабочей области прерывания программы и сохраняет в некотором месте, отведенном данному процессу. Затем, загрузив слово состояния, отличающееся от текущего SW тем, что в нем установлены в "1" все биты MASK, обработчик прерываний включает систему прерываний. Остальная часть программы обработки прерываний функционирует так же, как и пользовательский процесс: для инициирования операции ввода-вывода и ожидания результатов их выполнения она делает SVC-запросы. После завершения операции обмена страниц обработчик прерываний, используя сохраненную информацию о состоянии, снова передает управление на команду, вызвавшую прерывание.

В алгоритме, приведенном на рис.6.25б, не учтены некоторые важные моменты. Самый очевидный из них - какую страницу вытолкнуть из памяти. В некоторых системах хранятся записи, фиксирующие последнее обращение к каждой странице; выталкивается та, что дольше не использовалась. Это так называемый МЕТОД ВЫТАЛКИВАНИЯ ДОЛЬШЕ ВСЕГО НЕ ИСПОЛЬЗОВАВШЕЙСЯ СТРАНИЦЫ (LRU - Least Recently Used). Так как накладные расходы при таком способе хранения записей могут быть высокими, для LRU используются простейшие виды аппроксимации. В других системах делаются попытки определить множество наиболее часто используемых страниц опрашиваемого процесса (так называемое РАБОЧЕЕ МНОЖЕСТВО ПРОЦЕССА). В подобных системах выталкивание страницы производятся таким образом, чтобы каждый процесс всегда имел в памяти свое рабочее множество. Обсуждение и развитие различных стратегий выталкивания страниц можно найти в Дейтел [1984].

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

Системы с размещением страниц по запросу избегают нерационального использования памяти в основном при помощи фрагментации, часто связанной со способами организации разделов. Память можно экономить и другими способами. Например, стараются не загружать те части программы, которые в течение определенного времени выполнения не используются. Однако системы с размещением страниц по запросу имеют и другие уязвимые места. Например, предположим, что обращение к слову в центральной памяти занимает 1мкс, а считывание страницы из внешней памяти - в среднем 10мс (10000мкс). Предположим также, что в среднем для всех заданий в системе только 1 из 100 обращений к виртуальной памяти вызывает страничное прерывание. Даже при такой очевидно малой величине страничных прерываний система хорошо работать не будет. На каждые 100 обращений (требующих 100мкс) система потратит 10000мкс на считывание страниц из внешней памяти. Таким образом, вычислительная система тратит приблизительно 99% своего времени на свопинг страниц и только 1% на полезную работу. Этот полный разлад в работе называется ПРОБУКСОВКОЙ (thrashing).

Во избежание пробуксовки в только что описанной операции необходимо, чтобы число страничных прерываний было намного меньше (возможно, порядка одного прерывания на каждые 10000 обращений к памяти). На первый взгляд может показаться, что для достижения приемлемой производительности все программные страницы должны находиться в памяти. Однако это необязательно. Благодаря свойству, называемому локальностью ссылок, которое можно наблюдать в реальных программах, обращения к памяти не распределены случайно по всему виртуальному адресному пространству программы. Вместо этого они имеют тенденцию к группированию в адресном пространстве, как показано на рис.6.26а. Это происходит из-за таких общих свойств программ, как последовательное выполнение команды, компактное кодирование циклов, последовательная обработка структур данных и т.п.

Бек. Введение в системное программирование. 1988 - Страница 2 8836110
Рис.6.26. а - Локализованные обращения к памяти (каждая точка - это ячейка памяти, к которой было осуществлено обращение в данный момент времени в процессе выполнения программы); б - Эффект локализации ссылок в зависимости от числа страничных прерываний.

Благодаря локальности ссылок можно достичь приемлемо малого числа страничных прерываний, не храня все адресное пространство программы в реальной памяти. На рис. 6.26б для гипотетической программы величина страничных прерываний дана как функция от числа страниц программы, содержащихся в памяти. Масштаб этой кривой для разных программ сильно варьируется, однако ее поведение типично для многих реальных программ. Часто, как и в данном примере, существует критическая точка W. Если в памяти содержится меньше W страниц, наблюдается пробуксовка. Если в памяти находятся W страниц или больше, то производительность будет удовлетворительной. Значение этой критической точки W и является размером рабочего множества страниц программы, о котором говорилось ранее. Дальнейшее обсуждение результатов, связанных с размерами рабочего множества и пробуксовкой, можно найти в Дейтел [1984] и Лорин [1981].

Размещение страниц по запросу дает еще один пример ОТЛОЖЕННОЙ СВЯЗИ (delayed binding): соответствие между адресами виртуальной и реальной памяти не устанавливается до момента обращения к памяти. Такая задержка требует дополнительных расходов (на динамическое преобразование адресов, считывание страниц и т.п.). Однако для программиста она может оказаться более удобной, а также обеспечить эффективное использование реальной памяти. Можно сравнить эти замечания с теми, что были сделаны в предыдущих примерах по отложенным связям (разд.3.4.3, 5.3.1 и 5.4.2).

Выше была рассмотрена реализация виртуальной памяти при помощи размещения страниц по запросу. Другой тип виртуальной памяти можно реализовать посредством СЕГМЕНТНОЙ ОРГАНИЗАЦИИ памяти (СЕГМЕНТАЦИИ). В системе с сегментной организацией виртуальной памяти адрес состоит из номера сегмента и величины смещения внутри него. Принципы отображения и динамического преобразования адресов аналогичны ранее рассмотренным. Однако сегменты могут иметь произвольную длину (в отличие от страниц, длина которых в каждой системе фиксирована). Кроме того, сегменты обычно соответствуют логическим блокам программы, таким как процедуры или области данных (в отличие от страниц, которые обычно являются результатом произвольного разбиения адресного пространства). Это дает возможность связать такие атрибуты защиты, как только чтение или только выполнение, с конкретными сегментами. Возможно также разделение сегментов между различными заданиями пользователей. Сегментная организация памяти часто используется вместе с размещением страниц по запросу. При этом имеет место двухуровневая процедура отображения и преобразования адресов. Дальнейшую информацию по сегментной организации памяти и ее реализации можно найти в Дейтел [1984], Лорин [1981] и Мэдник [1974].
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Вт Фев 16, 2021 3:25 pm

6.3. МАШИННО-НЕЗАВИСИМЫЕ СВОЙСТВА ОПЕРАЦИОННЫХ СИСТЕМ

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

В разд.6.2.3 были рассмотрены возможные способы управления операциями ввода-вывода. В разд.6.3.1 изучается аналогичный вопрос для более высокого уровня: управление логическими файлами и работа с ними. В разд 6.3.2 обсуждается проблема планирования, т.е. выбора из заданий пользователей кандидатов на обслуживание, производимое на более низком уровне, о чем говорилось ранее.

В разд.6.3.3 рассматривается основной объект распределения ресурсов, осуществляемого операционной системой, и возникающие при этом сложности. Наконец, в разд.6.3.4 вводятся такие важные понятия, как защита и безопасность операционных систем.

6.3.1. РАБОТА С ФАЙЛАМИ

Данный раздел посвящен некоторым функциям, выполняемым типичными операционными системами в процессе управления и работы с файлами. В большинстве систем программы пользователей имеют возможность запрашивать ввод-вывод при помощи средств, описанных в разд.6.2 (канальных программ и SVC-запросов). Однако это достаточно неудобно, так как программисту должны быть известны коды канальных команд и их форматы, а программе пользователя - номера каналов, устройств и, возможно, реальный адрес требуемой записи, если используется устройство с прямым доступом (например, диск). Кроме того, нужно отслеживать окончание ввода-вывода, выполнять буферизацию и объединение в блоки, о чем будет сказано ниже.

Функция управления файлами, осуществляемая операционной системой, является промежуточным звеном между программой пользователя и супервизором ввода-вывода (рис.6.27), Программа пользователя на логическом уровне с помощью имен файлов, ключей и т.п. делает запросы, например "прочитать следующую запись из файла F". Программа управления файлами реализует метод доступа, транслируя логические запросы в физические запросы на ввод-вывод (т.е. канальные программы), и передает их супервизору, действующему при управлении операцией ввода-вывода (разд.6.2.3).

Бек. Введение в системное программирование. 1988 - Страница 2 8836410
Рис.6.27. Использование программы управления файлами для ввода-вывода.

Чтобы перевести логические программные запросы в канальные программы, система управления файлами должна иметь информацию о расположении и структуре файлов. Эту информацию она получает из структуры данных, называемой КАТАЛОГОМ, и ФАЙЛОВЫХ ИНФОРМАЦИОННЫХ ТАБЛИЦ. В действительности термины, используемые для обозначения подобных структур, так же как и их форматы и содержание, в разных операционных системах различны. Каталог устанавливает соответствие логических имен файлов с их физическим местонахождением и может предоставить о файлах некоторую общую информацию, В файловой информационной таблице содержится дополнительная информация, например об организации файлов, длине записи и форматах, о способе индексирования, если оно имеется. Чтобы начать работу с файлом, система просматривает каталог и определяет местонахождение соответствующей файловой информационной таблицы. Система также может создать буфера для размещения в них блоков файлов, которые будут прочитаны или записаны. Эта процедура называется ОТКРЫТИЕМ файла. После окончания работы с файлом буферные и другие рабочие области и указатели уничтожаются. Такая процедура называется ЗАКРЫТИЕМ файла.

Одной из наиболее важных функций управления файлами является автоматическое выполнение операций БУФЕРИЗАЦИИ и ОБЪЕДИНЕНИЯ В БЛОКИ читаемых и записываемых файлов. На рис.6.28 эти операции иллюстрируются последовательно вводимым файлом. Предполагается, что чтение записей осуществляется программой пользователя последовательно с начала файла и до его конца. Логически файл состоит из записей длиной 1024 байт; физически, однако, файл образован 8192-байтовыми блоками, каждый из которых содержит 8 логических записей. Этот вид объединения записей в блоки обычно осуществляется конкретными типами запоминающих устройств в целях экономии времени обработки и пространства памяти. Подробнее об этом говорится в Лумис [1983].

Бек. Введение в системное программирование. 1988 - Страница 2 8836510
Рис.6.28. Блокирование и буферизация последовательного файла.

На рис.6.28а показана ситуация, сложившаяся после того, как файл был открыт и программа пользователя осуществила свой первый запрос на чтение записи. Система управления файлами уже выдала запрос на ввод-вывод для чтения в буфер B1 первого блока файла и должна ждать завершения этой операции, прежде чем сможет передать требуемую запись пользователю. На рис.6.28б первый блок прочитан. Он находится в буфере B1 и содержит логические записи с номерами от 1 до 8. Теперь система управления файлами может передать требуемую запись программе пользователя, для чего указатель P устанавливается на первую логическую запись. Для чтения в буфер B2 второго блока файла система управления файлами выдает второй физический запрос на ввод-вывод.

Когда программа пользователя в следующий раз делает запрос на чтение записи, уже нет необходимости ждать каких-либо действий по вводу-выводу. Система просто передвигает указатель P на логическую запись 2 и возвращает управление пользователю. Эта операция показана на рис.6.28в. Заметим, что физическая операция ввода-вывода по чтению второго блока в буфер B2 все еще выполняется. Те же действия повторяются и для других логических записей первого блока (рис.6.28г).

Если программа пользователя осуществляет запрос на чтение 9-й записи до завершения операции ввода-вывода для блока, системе управления файлами снова приходится заставлять программу ждать. После того как второй блок прочитан, указатель P переводится на первую запись буфера B2. После этого система управления файлами выдает запрос на ввод-вывод для чтения в буфер B1 третьего блока и процесс продолжается подобно тому, как было описано выше. Заметим, что использование двух буферных областей позволяет совмещать работу с одним блоком и чтение другого. Подобный способ, часто называемый ДВОЙНОЙ БУФЕРИЗАЦИЕЙ, широко используется при обмене последовательными файлами.

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

Программы управления файлами выполняют также много других функций: распределение пространства на внешних запоминающих устройствах, реализацию правил управления доступом к файлам и их использованием. Дальнейшее обсуждение этих вопросов можно найти в Дейтел [1984] и Мэдник [1974].

6.3.2. ПЛАНИРОВАНИЕ ЗАДАНИЙ

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

На рис.6.29а показана типичная двухуровневая система планирования в мультипрограммной системе. Задания, поступающие в систему, помещаются в ОЧЕРЕДЬ ВХОДНЫХ ЗАДАНИЙ, из которой они затем выбираются ПЛАНИРОВЩИКОМ ЗАДАНИЙ. Выбранные задания становятся активными; это означает, что они начинают принимать участие в операции планирования процессов, описанной в разд.6.2.2. Двухуровневая процедура вводится в целях ограничения УРОВНЯ МУЛЬТИПРОГРАММИРОВАНИЯ, т.е. числа заданий пользователей, между которыми происходит разделение ЦП и других системных ресурсов. Это необходимо для поддержания эффективной работы мультипрограммной системы. Если система пытается выполнять одновременно слишком много заданий, перекрытие при управлении ресурсами становится слишком большим, а количество доступных каждому заданию ресурсов - слишком малым. В результате производительность системы снижается.

Бек. Введение в системное программирование. 1988 - Страница 2 8836710
Рис.6.29. а - Двухуровневая система планирования и б - трехуровневая система планирования.

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

Если входная очередь не пуста, уровень мультипрограммирования достаточно просто можно увеличить, обратившись к планировщику заданий. Труднее бывает его уменьшить, например для того, чтобы остановить пробуксовку. Это обычно достигается при помощи процедуры трехуровневого планирования заданий, которая показана на рис.6.29б. Планировщик заданий и планировщик процессов (т. е. диспетчер) работают, как и ранее. Однако существует также ПЛАНИРОВЩИК ПРОМЕЖУТОЧНОГО УРОВНЯ, управляющий производительностью системы и, если требуется, регулирующий уровень мультипрограммирования. Если он слишком высок, промежуточный планировщик понижает его, ПРИОСТАНАВЛИВАЯ или СВЕРТЫВАЯ одно или несколько заданий. Если уровень мультипрограммирования слишком низок, промежуточный планировщик возобновляет работу приостановленных заданий или обращается к планировщику заданий для того, чтобы активизировать новые. Промежуточные планировщики могут использоваться и для регулирования приоритета диспетчеризации активных заданий, основываясь на наблюдении за ними в процессе выполнения.

Вся система планирования обычно базируется на системе приоритетов, предназначенных для достижения определенных целей. Первой целью может быть, например, получение максимальной ПРОПУСКНОЙ СПОСОБНОСТИ системы, т.е. выполнение наибольшей вычислительной работы за кратчайшее время. Ясно, что для этого требуется эффективное использование всех системных ресурсов. Другая задача - получение наименьшего среднего ВРЕМЕНИ ПРОХОЖДЕНИЯ, т.е. времени между загрузкой задания пользователем и завершением его выполнения. С системами разделения времени связана задача минимизации предполагаемого ВРЕМЕНИ ОТВЕТА (времени между нажатием клавиши ENTER на терминале и получением на это ответа системы).

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

Первые две задачи, упомянутые выше,- высокая пропускная способность и малое среднее время прохождения и время ответа - обычно принимаются в качестве желательных свойств системы. К сожалению, они часто несовместимы. Рассмотрим, например, систему разделения времени, работающую с большим числом терминалов. Мы можем решить, что обеспечить лучшее время ответа нужно посредством более быстрого переключения управления между различными терминалами пользователей. Для этого при диспетчеризации каждому процессу можно выделять меньший квант времени. Однако это приводит к увеличению частоты операций контекстного переключения, что заставляет операционную систему чаще принимать решения по распределению ЦП и других ресурсов. Это означает, что накладные расходы операционной системы будут выше, а время, предоставляемое заданиям пользователей,- меньше, поэтому пропускная способность в целом уменьшится.

С другой стороны, рассмотрим однопрограммную систему пакетной обработки. Каким образом в этой системе выполняются два задания, показано на рис.6.30а. Обратите внимание на промежутки времени (они показаны пробелами на горизонтальных линиях, соответствующих Заданиям 1 и 2), когда ЦП простаивал. Если оба задания запущены в момент времени 0, то время прохождения Задания 1 (T1) равно 2мин., а время прохождения Задания 2 (T2) - 5мин. Среднее время прохождения Tср равно 3.5мин.

Бек. Введение в системное программирование. 1988 - Страница 2 8836910
Рис.6.30. Сравнение времени прохождения и пропускной способности для а - однопрограммной системы, б - для мультипрограммной системы.

Рассмотрим теперь мультипрограммную систему, в которой одновременно выполняются два задания, как это показано на рис.6.30б. Заметьте, что разделение ЦП между заданиями происходит таким образом, что полное время простоя уменьшается; это феномен, похожий на тот, что показан на рис.6.15. Из-за меньшего времени простоя два задания завершаются быстрее: за 4.5мин. вместо 5мин. Это означает, что пропускная способность системы стала больше: тот же самый объем работы был сделан за меньшее время. Однако среднее время прохождения задания стало хуже: 4.4мин. вместо 3.5мин.

Как правило, чаще всего используются две стратегии планирования заданий: ПЕРВЫМ ПРИШЕЛ - ПЕРВЫМ ОБСЛУЖЕН (FCFS - First Come - First Served) [Такая стратегия обслуживания часто называется FIFO.- Прим. ред.] и КРАТЧАЙШЕЕ ЗАДАНИЕ - ПЕРВЫМ (SJF - Shortest Job First). Стратегия FCFS стремится обслуживать все задания одинаково, минимизируя тем самым время прохождения; SJF обеспечивает снижение среднего времени прохождения, так как короткие задания могут вынужденно ожидать обслуживания в течение долгого времени. Примеры этих свойств и обсуждение других стратегий планирования можно найти в Дейтел [1984], Лорин [1981], Мэдник [1974].
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Вт Фев 16, 2021 3:29 pm

6.3.3. РАСПРЕДЕЛЕНИЕ РЕСУРСОВ

В разд.6.2 обсуждалось, каким образом операционная система может управлять такими ресурсами, как центральная память, каналы ввода-вывода и ЦП. Эти ресурсы используются всеми заданиями пользователей; распределение осуществляется системой автоматически. Ниже будет рассмотрена более общая функция распределения ресурсов, выполняемая многими операционными системами. Ее можно использовать для управления распределением таких ресурсов пользователя, как файлы и структуры данных. На рис.6.31а при помощи двух программ иллюстрируется необходимость применения этой функции. Обе программы используют последовательный стек, созданный какой-то третьей программой. Внешние переменные STACK и TOP содержат соответственно базовый адрес стека и относительный адрес элемента данных, находящегося в его вершине. Предполагается, что внешние ссылки на переменные STACK и TOP обрабатываются методами, аналогичными рассмотренным в гл.3. Программа P1 добавляет данные в стек; для этого она увеличивает предыдущее значение TOP на 3, заносит содержимое регистра A в вершину стека, а затем запоминает новое значение TOP (строки 24-27). Программа P2 изымает данные; для этого она заносит значение из вершины стека в регистр A и уменьшает затем значение TOP на 3 (строки 37-40). Для простоты мы не использовали команду, требуемую для обработки пустого стека и переполнения.

Бек. Введение в системное программирование. 1988 - Страница 2 8837110
Рис.6.31. Управление ресурсами и использование сервисных запросов операционной системе.

Если процессы P1 и P2 выполняются одновременно, то они могут работать как правильно, так и неправильно. Предположим, например, что текущее значение TOP равно 12. Если выполнятся команды с номерами 24-27 программы P1, то в байты 15-17 стека ею будет занесено новое данное, а значение TOP станет равно 15. Если затем будут выполнены команды 37-40 программы P2, то только что добавленное P1 данное будет считано, а значение TOP снова станет равным 12. Так выглядит правильная работа P1 и P2; оба процесса выполняют требуемые операции над стеком, не влияя друг на друга. Правильная последовательность работы будет и тогда, когда сначала P2 выполнит строки 37-40, а затем P1 - строки 24-27. Теперь предположим, что после выполнения строки 24 у P1 кончается отведенный ей квант времени. Происшедшее в результате этого прерывание от таймера вызовет сохранение значений всех регистров; сохраненное значение регистра X будет равно 12. Предположим теперь, что диспетчер передает управление P2, которая затем выполняет строки 37-40. Так как значение TOP. программой P1 еще не было изменено, P2 возьмет данное из байтов 12-14 стека и сделает значение TOP равным 9. Когда P1 снова получит управление ЦП, регистр X все еще будет содержать значение 12. Таким образом, в результате выполнения строк 25-27 в байты 15-17 стека будет добавлено новое данное, а значение TOP установлено в 15.

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

Избежать ситуаций такого рода можно, монополизировав управление стеком на время выполнения операций модификации каждой из программ. На рис.6.31б показано типичное решение проблемы, при котором используются запросы на обслуживание операционной системе. Выполнением команды SVC 3 программа P1 запрашивает монопольное управление стеком. Требуемый ресурс задается регистром T, указывающим на логическое имя (определяемое пользователем), присвоенное стеку. После добавления в стек нового данного и изменения TOP P1 отказывается от монопольного управления, выполнив команду SVC 4. Программа P2 осуществляет аналогичную последовательность действий.

Бек. Введение в системное программирование. 1988 - Страница 2 8837210
Рис.6.31. Продолжение.

Операционная система в ответ на запрос управления ресурсом проверяет, назначен ли он какому-нибудь другому процессу или нет. Если ресурс свободен, то управление передается запрашивающему процессу. Если ресурс занят, то система переводит запрашивающий процесс в заблокированное состояние, в котором он находится до тех пор, пока ресурс не станет доступным. Пусть, например, в данный момент свободен ресурс STACK1. Если P1 запросит его (строка 23), то управление операционной системой будет возвращено непосредственно этой программе. Как и раньше, предположим, что сразу после выполнения строки 24 заканчивается квант времени, выделенный P1. Затем управление ЦП переходит к P2; однако STACK1 остается занятым P1. Таким образом, после запроса P2 ресурса STACK1 в строке 36 программа будет переведена в состояние блокировки. В конце концов P1 снова получит управление и завершит работу со стеком. Когда P1 освободит STACK1 (строка 28 ), он будет передан P2, которая из состояния блокировки перейдет в состояние готовности и после получения управления ЦП сможет выполнить операцию модификации. Читателю следует внимательно просмотреть последовательность этих событий, чтобы увидеть, как с помощью данного метода удается избежать возникновения описанных выше проблем.

К сожалению, использование операций запроса и отказа может привести к проблемам другого рода. Рассмотрим, например, программы на рис.6.32. Первой управление ресурсом RES1 запрашивает P3; позже она запрашивает ресурс RES2. Программа P4 использует те же два ресурса, однако RES2 она запрашивает перед RES1.

Бек. Введение в системное программирование. 1988 - Страница 2 8837410
Рис.6.32. Запрос ресурсов, приводящий к потенциальной самоблокировке,

Предположим, что P3 после запроса получает управление ресурсом RES1; при этом отведенный ей квант времени кончается до того, как она сможет затребовать RES2. Затем может рыть активизирована P4. Предположим, что P4 запрашивает и получает управление ресурсом RES2. Такая последовательность событий ведет к ситуации, в которой дальше не смогут выполняться ни P3, ни P4. Действительно, программа P4, дойдя до строки 4, затребует управление ресурсом RES1, после чего она будет переведена в состояние блокировки, так как RES1 отдан P3. Аналогично P3, дойдя до строки 4, затребует управление RES2 и тоже будет переведена в состояние блокировки, так как RES2 отдан P4. Поскольку свободных ресурсов нет, ни один процесс не сможет продолжить работу.

Такая ситуация является примером взаимоблокировки (deadlock); она характеризуется тем, что в некотором наборе процессов каждый отдельный процесс постоянно заблокирован, так как необходимые ресурсы заняты другими процессами. Взаимоблокировка снимается освобождением ресурса за счет удаления из решения одного или нескольких вовлеченных в нее заданий. Существует ряд методов, позволяющих избежать взаимоблокировки. Например, система может потребовать, чтобы процесс запрашивал все свои ресурсы одновременно или делал это в определенном порядке (как то: RES1 перед RES2). К сожалению, при этом из-за продолжительной занятости ресурсов снизится работоспособность всей системы. Описание методов нахождения и предотвращения взаимоблокировок можно найти в Дейтел [1984].

Выше рассмотрены частные случаи общих проблем взаимоисключения и синхронизации процессов. Их обсуждение и способы разрешения можно найти в Дейтел [1984] и Холт [1978].

6.3.4. ЗАЩИТА

Во всякой операционной системе, обслуживающей одного или нескольких пользователей, должны существовать средства, обеспечивающие защиту отдельного пользователя от возможных несанкционированных действий других. Например, пользователь должен иметь возможность создавать файлы, которые не могут быть прочитаны или изменены другими пользователями. В целом проблема безопасности и защиты достаточно сложна. В данном разделе будут коротко рассмотрены основные функции защиты, выполняемые типичной операционной системой. Они должны быть совмещены с безопасностью на физическом уровне, с административными процедурами и другими способами контроля, осуществляемыми для обеспечения эффективной защиты. Дополнительную информацию по защите, безопасности и секретности можно найти в Дейтел [1984], Деннинг [1983] и Фернандес [1981].

В большинстве многопользовательских операционных систем существует некоторый вид КОНТРОЛЯ ДОСТУПА или АВТОРИЗАЦИИ; чаще всего они реализуются на основе МАТРИЦЫ ДОСТУПА, аналогичной той, что показана на рис.6.33. В этом примере пользователи обозначены u1, u2 и u3. Права пользователя по отношению к некоторому объекту (файлу или программе) задаются строками в матрице доступа. Так, пользователь u1 может читать файлы f1 и f2, писать в файл f2, выполнять программу p2. Однако ему запрещен какой-либо доступ к файлу f3 или программе р1. Права на доступ к вновь созданному объекту обычно определяются его создателем. В некоторых системах пользователям, обладающим определенными правами на доступ, разрешено передавать их другим пользователям. Доступ к контролируемым объектам осуществляется при помощи запросов на обслуживание операционной системе. Каждому пользователю, авторизация которого произведена неверно, в доступе будет отказано.

Бек. Введение в системное программирование. 1988 - Страница 2 8837610
Рис.6.33. Пример матрицы доступа,

В действительности матрица доступа достаточно разрежена. Большинство пользователей имеет доступ к относительно малому числу объектов системы. По этой причине информация, связанная с правами на доступ, часто хранится в виде СПИСКА АВТОРИЗАЦИИ для каждого объекта (т.е. списка авторизованных пользователей) или списка возможностей (т.е. списка объектов, к которым возможен доступ). Дальнейшую информацию по реализации модели матрицы доступа можно найти в Деннинг [1983].

Эффективность только что описанного метода зависит, очевидно, от правильной идентификации пользователей. С тем чтобы применить верные правила контроля за доступом, операционная система должна иметь возможность распознавать каждого пользователя. Один из наиболее часто используемых способов идентификации - системные ПАРОЛИ. В большинстве систем пользователю для запуска задания необходимо ввести секретный пароль или зарегистрироваться с помощью интерактивного терминала. Иногда этот способ применяется шире и подтверждение пароля требуется для доступа к конкретным файлам, программам и т.п. В некоторых системах имеется главная таблица паролей, используемая операционной системой для проверки паролей, получаемых от пользователей; однако она является потенциально уязвимым местом всей системы защиты. Во избежание этого во многих операционных системах таблица  хранится в зашифрованном виде. Более подробно об этом и других подобных методах можно узнать в Деннинг [1983].

Так как информация иногда должна находиться вне средств защиты, система идентификации и авторизации пользователей не всегда позволяет разрешить все проблемы, связанные с безопасностью. Рассмотрим, например, рис.6.34а. Идентификация и авторизация пользователя за интерактивным терминалом произведены правильно. Мы предполагаем, что вычислительная система осуществляет соответствующий контроль доступа и имеет средства обеспечения безопасности на физическом уровне. Предполагается также, что и сам терминал на физическом уровне защищен. Однако линию связи между вычислительной системой и терминалом защитить бывает сложно или вообще невозможно. (Примером может служить проблема предотвращения подслушивания, когда терминал связан с ЭВМ через обычную телефонную сеть). Это означает, что во время передачи уязвимой является любая информация из файла F, которая передается терминалу. Аналогично можно узнать все пароли, посылаемые пользователем, а следовательно, получить доступ к защищенным объектам.

Бек. Введение в системное программирование. 1988 - Страница 2 8837710
Рис.6.34. Использование кодирования данных для их защиты во время передачи.

Обычно проблемы такого рода решаются с помощью ШИФРОВКИ ДАННЫХ. Информация, которая должна быть передана по незащищенной линии связи, зашифровывается (кодируется), когда она еще находится в пределах средств защиты. Переданная информация расшифровывается (декодируется) уже под защитой соответствующих средств получателя. Прослушивающий не сможет понять передаваемую зашифрованную информацию. Операции шифровки и дешифровки одинаково успешно могут выполняться как аппаратурой, так и программным обеспечением. Способов шифровки существует много. Для более подробного ознакомления с этими методами смотрите Деннинг [1983].

Конечно, эффективность любой системы защиты всецело зависит от правильности и защищенности ее самой. В рассмотренной нами системе информация по контролю за доступом должна быть защищена от неавторизованных изменений. Должен существовать и механизм, обеспечивающий невозможность доступа пользователей к защищенным объектам, минуя систему, безопасности. В этой связи часто бывают полезны такие аппаратные средства, как режимы пользователя/супервизора, привилегированные инструкции и механизм защиты памяти. Очень важно быть уверенным и в том, что часть операционной системы, связанная с правилами защиты (ЯДРО БЕЗОПАСНОСТИ) функционирует правильно. Дальнейшее обсуждение этих вопросов может быть найдено в Дейтел [1984], где есть и обзор слабых мест в средствах обеспечения безопасности операционных систем, и интересные и поучительные случаи по вскрытию защиты системы.
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Вт Фев 16, 2021 3:35 pm

6.4. СПОСОБЫ ПОСТРОЕНИЯ ОПЕРАЦИОННЫХ СИСТЕМ

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

6.4.1. ИЕРАРХИЧЕСКАЯ СТРУКТУРА

Многие реально существующие операционные системы разработаны и реализованы на основе иерархической структуры. Примеры подобной структуры приведены на рис.6.35. В этом случае основным принципом, как видно из рис.6.35а, является обобщение идеи расширенной машины, приведенной на рис.6.1. Каждый слой, или УРОВЕНЬ, в структуре может использовать функции, предоставляемые ему более низким уровнем, как если бы они являлись частью реальной машины. Так, Уровень 0, часто называемый ЯДРОМ (kernel) операционной системы, имеет дело непосредственно с аппаратурой; Уровень 1 имеет дело с интерфейсом, предоставляемым уровнем 0, и т.д. Программы пользователя имеют дело с интерфейсом самого высокого уровня (в данном случае Уровня 3), который представляет собой интерфейс пользователя, рассмотренный в разд.6.1.

Бек. Введение в системное программирование. 1988 - Страница 2 8837910
Рис.6.35. Пример системы с иерархической структурой.

На рис. 6.35б показаны функции, соответствующие уровням рассматриваемой структуры. Их расположение определяется взаимоотношениями между выполняемыми ими операциями. В общем случае функции каждого уровня могут обращаться лишь к функциям того же или более низкого уровня; таким образом, не должно быть внешних вызовов. В нашем примере программы управления файлами (Уровень 3) для распределения буферов должны использовать менеджер памяти (Уровень 2), а для чтения и записи блоков - супервизор ввода-вывода. Если управление памятью производится при помощи размещения страниц по запросу, то менеджер памяти для передачи страниц между реальной памятью и вспомогательной тоже должен вызывать супервизор ввода-вывода. Все уровни системы используют предоставляемые Уровнем 0 функции планирования процессов и управления ресурсами.

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

На рис.6.35б показано типичное расположение функций, однако различные системы существенно отличаются друг от друга. Рассмотрим, например, программы обработки прерываний. Во многих системах ОБРАБОТЧИКИ ПРЕРЫВАНИЙ ПЕРВОГО УРОВНЯ (FLIH - First-Level Interrupt Handlers) размещены в ядре (Уровень 0). После начальной обработки прерываний FLIH может передать управление программе более высокого уровня; это исключение из правил, согласно которым внешние вызовы недопустимы. Так, например, FLIH в случае прерывания по отсутствию страницы может сохранить информацию о состоянии, разрешить другие прерывания, а затем передать управление программе Уровня 2 (см. рис.6.25в).

В некоторых операционных системах иерархическое распределение функций в особых случаях сделано более сложным. Информацию об этом и о других примерах иерархических структур можно найти в Лорин [1981] и Питерсон [1983].

Иерархические системы различаются также правилами передачи управления с одного уровня на другой. В СТРОГОЙ ИЕРАРХИИ каждый уровень может обращаться только к уровню, находящемуся непосредственно под ним. Так, Уровень 3 может связываться только с Уровнем 2. Если программе управления файлами в нашем примере необходимо вызвать супервизор ввода-вывода, то этот запрос будет передан с Уровня 2 на Уровень 1. Преимущество этого подхода заключается в простоте использования: каждый уровень взаимодействует только с одним интерфейсом. Однако такое ограничение может привести к потере эффективности, так как возрастает число вызовов, которые нужно сделать для достижения внутреннего уровня, В ПРОЗРАЧНОЙ ИЕРАРХИИ (transparent hierarchy) каждый уровень может связываться непосредственно с интерфейсом любого более низкого уровня. Так, например, программа пользователя может обращаться к программам управления файлами Уровня 3 или непосредственно вызывать функции супервизора ввода-вывода Уровня 1.

Дальнейшее обсуждение иерархических структур операционных систем можно найти в Питерсон [1983], Лорин [1981] и Мэдник [1974].

6.4.2. ВИРТУАЛЬНЫЕ МАШИНЫ

Чтобы перед пользователями создать иллюзию того, что они работают на автономных ВИРТУАЛЬНЫХ МАШИНАХ, концепцию иерархической структуры, рассмотренную в предыдущем разделе, можно расширить. Подход, связанный с применением виртуальных машин, делает возможной одновременную работу различных операционных систем на одной и той же реальной машине. Таким образом, виртуальные машины мы можем рассматривать как перенесение принципов мультипрограммирования на самый нижний уровень операционной системы.

Рассмотрим, например, рис.6.36. Операционная система ОС1 - мультипрограммная, поддерживающая одновременную работу трех пользователей; операционная система ОС2 - одно-программная; ОСЗ - операционная система, которая в этот момент тестируется. Есть также программа пользователя (Пользователь5), предназначенная для работы в режиме супервизора в качестве автономной программы, не находящейся под управлением операционной системы.

Бек. Введение в системное программирование. 1988 - Страница 2 8838110
Рис.6.36. Операционная система с виртуальными машинами и многими пользователями.

Все три операционные системы плюс автономный пользователь фактически работают на одной реальной машине. Однако они имеют дело не с ней, а с монитором виртуальной машины (МВМ), благодаря которому у каждого пользователя создается впечатление, что он работает на автономной машине. Таким образом, в то время как обычные пользователи обслуживаются традиционным способом, имеется возможность отлаживать новые операционные системы, а также разрешить пользователям в особых случаях работать в режиме супервизора [Автор идеализирует положение дел. В режиме виртуальных машин можно отлаживать только те части операционных систем, которые не затрагивают аппаратных управляющих регистров и других средств аппаратуры, используемых монитором ОС.- Прим. ред.]. Если тестируемая система или автономный пользователь разрушит систему, то это отразится лишь на их виртуальных машинах. Другие пользователи реальной машины смогут спокойно продолжать свою работу.

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

Бек. Введение в системное программирование. 1988 - Страница 2 8838210
Рис.6.37. Виртуальная машина как расширение понятия иерархической структуры.

Каждый непосредственный пользователь виртуальной машины, например ОС1 или Пользователь5 на рис.6.36, фактически работает не в режиме супервизора, а в пользовательском режиме. Когда такой пользователь пытается выполнить привилегированную команду, такую как SIO, STI или LPS, происходит программное прерывание. По нему управление передается монитору виртуальной машины. МВМ имитирует (с учетом виртуальности машины) выполнение требуемой команды, а затем возвращает управление пользователю. Монитор виртуальной машины активизируется и по прерыванию на реальной машине. Он определяет, какая из виртуальных машин должна быть задействована, и производит соответствующие изменения ее состояния.

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

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

Дальнейшее обсуждение операционных систем виртуальных машин можно найти в Дейтел [1984] и Питерсон [1983].

6.4.3. МУЛЬТИПРОЦЕССОРНЫЕ СИСТЕМЫ

До сих пор при обсуждении операционных систем мы имели дело с машинами, в состав которых входил один центральный процессор (ЦП). В этом разделе будут рассмотрены некоторые возможные пути проектирования мультипроцессорных операционных систем.

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

На рис.6.38 показана простейшая форма мультипроцессорной организации. Каждый процессор имеет свою собственную память, устройства ввода-вывода и другие ресурсы. Вдобавок на каждом процессоре функционирует его собственная операционная система, Процессоры соединяются линиями связи. Посылка по ним запроса является единственным способом получения доступа одного процессора к ресурсам другого. Подобная организация, часто называемая мультипроцессорной системой со СЛАБО СВЯЗАННЫМИ ПРОЦЕССОРАМИ, очень похожа на сеть из однопроцессорных систем. Такие системы часто используются, если среди различных процессоров существует специализация функций. Например, некоторые системы разделения времени для осуществления всех деталей управления связью с терминалами пользователей имеют периферийный процессор. Главный процессор выполняет всю текущую вычислительную работу, связываясь с периферийным процессором всякий раз, когда требуется произвести обмен с терминалами. Операционная система, работающая на каждом процессоре мультипроцессорной системы со слабо связанными процессорами, очень похожа на те, что мы рассматривали при обсуждении однопроцессорных систем. Единственной действительно новой функцией является управление сообщениями, посылаемыми по линиям связи.

Бек. Введение в системное программирование. 1988 - Страница 2 8838410
Рис.6.38. Мультипроцессорные системы со слабо связанными процессорами.

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

На рис.6.39 демонстрируются две разновидности мультипроцессорной системы с сильно связанными процессорами. На рис.6.39а показана система, реализующая принцип ГЛАВНЫЙ-ПОДЧИНЕННЫЙ. Это означает, что все управление ресурсами и другими функциями операционной системы осуществляется одним главным процессором. На него полностью возложено управление работой всех подчиненных процессоров, выполняющих задания пользователей. Процессоры взаимодействуют либо непосредственно через линии связи, либо через рабочие области совместно используемой памяти. В мультипроцессорных системах, реализованных по принципу главный-подчиненный, процессоры могут совместно использовать такие ресурсы, как память и файлы данных. Однако программы и структуры данных, составляющих саму операционную систему, не разделяются; они используются только главным процессором. Таким образом, такой тип операционной системы также несколько похож на рассмотренные нами однопроцессорные системы.

Бек. Введение в системное программирование. 1988 - Страница 2 8838510
Рис.6.39. Мультипроцессорные системы с сильно связанными процессорами с обработкой: а - типа главный-подчиненный и б - симметричной.

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

В симметричных мультипроцессорных системах различные части операционной системы могут выполняться одновременно разными процессорами. Из-за этого разработка подобных систем может оказаться значительно сложнее и труднее по сравнению с рассмотренными выше. Например, все процессоры должны иметь доступ ко всем структурам данных [Под структурами данных здесь имеются в виду файлы, массивы и т.п.- Прим. ред.], используемым операционной системой. Однако процессоры имеют дело с этими структурами данных независимо друг от друга, и, если два процессора одновременно пытаются изменить одну и ту же структуру, могут возникнуть осложнения (см. разд.6.3.3). Симметричные мультипроцессорные системы должны иметь средства, с помощью которых осуществляется управление доступом к структурам данных и к критическим таблицам операционной системы. Для решения этой задачи не достаточно операций запроса и отказа, рассмотренных в разд. 6.3.3, так как два различных процессора могут выполнить операции запроса одновременно. Поэтому обычно требуется, чтобы аппаратура обладала особыми свойствами, позволяющими одному процессору захватывать управление критическим ресурсом, блокируя на один шаг все другие процессоры. Обсуждение подобных средств можно найти в Питерсон [1983] и Лорин [1981].

Дальнейшую информацию по операционным системам для мультипроцессоров можно найти в Дейтел [1984], Лорин [1981] и Мэдник [1974].
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Вт Фев 16, 2021 3:40 pm

6.5. ПРИМЕРЫ РЕАЛИЗАЦИЙ

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

Первые два примера - операционные системы, не привязанные к какой-нибудь одной машине. В разд.6.5.1 рассматривается небольшая переносимая однопользовательская система UCSD Pascal, функционирующая на мини-ЭВМ. В разд. 6.5.2 описывается UNIX, более сложная операционная система, которая также может быть реализована и на разных ЭВМ.

Другие примеры - системы, разработанные для определенных семейств ЭВМ. Разд.6.5.3 посвящен NOS (tm), функционирующей на ЭВМ серии CDC CYBER; это пример мультипроцессорной операционной системы. В разд.6.5.4 рассматривается система VAX/VMS (tm), в которой используются некоторые интересные методы управления виртуальной памятью. В разд.6.5.5 рассматривается VM/370 фирмы IBM, операционная система, на базе которой могут быть реализованы многочисленные виртуальные машины.

6.5.1. СИСТЕМА UCSD PASCAL

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

Одной из наиболее интересных частей системы UCSD Pascal является ее интерфейс с пользователями, имеющий древовидную структуру. На каждом уровне дерева системой выдается СТРОКА-ПОДСКАЗКА (prompt line), часто называемая (меню). Она содержит список команд, которые может ввести пользователь. По каждой из них выбирается соответствующая ветвь, и пользователь переводится на более низкий уровень дерева. Затем выдается новая строка-подсказка, содержащая команды, которые можно ввести уже на этом уровне. На самом нижнем уровне система запрашивает у пользователя при необходимости дополнительную информацию и выполняет команду.

Например, когда система загружена впервые, ею выдается строка-подсказка вида

Команды: E(dit,R(un,F(ile,C(omp,L(ink,X(ecute,A(ssem,D(ebug.

По команде E пользователь переводится на один уровень ниже и попадает в Редактор (Editor), где выдается новая строка-подсказка. Она может иметь вид

Редактор: A(djst,C(py,D(lete,F(ind,I(nsrt,J(mp,R(eplace,Q(uit,X(chng,Z(ap.

(Реальные строки-подсказки в зависимости от версий системы могут немного различаться). По команде I система запрашивает у пользователя вводимый текст. По команде Q пользователь выходит из Редактора и возвращается к корню дерева {начальной строке-подсказке).

На самом внешнем уровне имеющиеся команды обеспечивают простой доступ к наиболее важным функциям системы. Редактор позволяет пользователю ввести текст и отредактировать любой заданный текстовой или системный РАБОЧИЙ ФАЙЛ. В программе Файлер имеются команды для управления файлами на диске. Это, например, команды выдачи справочной информации, копирования и создания новых файлов. Компилятор, ассемблер и Редактор связей - это системные программы, выполняющие функции, аналогичные тем, что были рассмотрены ранее.

По команде R(un внешнего уровня система начинает выполнение программы из текущего рабочего файла. По X(ecute система запрашивает имя файла и выполняет содержащуюся в нем программу. По D(ebug программа из текущего файла выполняется под управлением системного Отладчика, который вызывается в случае появления ошибки или по достижении контрольной точки, заданной пользователем.

В системе имеется также ряд вспомогательных программ. К ним относятся, например, такие, посредством которых производится настройка системы на конкретную аппаратную конфигурацию, создаются самозагружающиеся программы, выполняются операции форматирования диска, не предусмотренные в Файлере. Вспомогательные программы запускаются на внешнем уровне при помощи команды X(ecute. Операционной системой UCSD Pascal программы пользователей обеспечиваются набором лишь основных сервисных функций. В соответствии с нуждами программ, написанных на Паскале, в системе реализовано динамическое распределение памяти; тем самым имеется возможность в случае надобности переназначить содержимое основной памяти. Поддержка параллельности работы минимальна. Большая часть активности системы, включая операции ввода-вывода, синхронизована. Это означает, например, что операция READ приводит к тому, что программе пользователя приходится ждать завершения физической операции ввода-вывода; в это время ни одно другое задание не может быть выполнено.

Дальнейшую информацию по системе UCSD Pascal можно найти в SofTech Microsystems [1980] и 1983].

6.5.2. UNIX

[ По работе D.M.Ritchie and К.Thompson, "The UNIX Time-Sharing System". Communications of the ACM, Vol.17, No.7. Copyright 1974, Association for Computing Machinery, Inc.]

Операционная система UNIX первоначально была разработана в Bell Laboratories для семейства ЭВМ DEC PDP-11. С тех пор она была реализована на многих других машинах, от больших ЭВМ до микропроцессоров. Сейчас существует несколько версий UNIX. Ниже будут рассмотрены некоторые присущие всем им наиболее интересные свойства системы.

Интерфейс пользователя в системе UNIX реализован программой, называемой ОБОЛОЧКОЙ (shell). Это интерпретатор командных строк, воспринимающий строки, вводимые пользователем, в качестве запросов на выполнение других программ. Сначала по каждой команде вызывается соответствующая программа. По ее окончании оболочка также завершает работу и ожидает от пользователя следующую команду. Или же оболочка может запросить команду сразу после запуска требуемой программы, не дожидаясь ее завершения. В функции оболочки входят также замена параметров, выполнение команд, основанных на сравнении символьных строк, а также передача управления в пределах последовательности команд.

Стандартная оболочка, рассмотренная выше, предназначена для того, чтобы предоставить пользователю доступ ко всем возможностям системы UNIX. Иногда бывает желательно предусмотреть особый интерфейс пользователя, что можно легко достичь, применив нестандартный вариант оболочки. Имя реализующей ее программы может содержаться в пользовательском разделе файла паролей. Эта программа может интерпретировать команды таким образом, чтобы обеспечивался интерфейс, приспособленный к требованиям особого пользователя. Например, для пользователей системы секретарского редактирования при помощи файла паролей может быть задано, чтобы вместо стандартной оболочки была использована программа редактирования текстов. Таким образом, пользователи могут начать работу сразу же после входа в систему. Оболочка может помочь и в обеспечении защиты системы: пользователям индивидуальной командной оболочки может быть запрещено вызывать не предусмотренные для них программы системы UNIX.

В системе UNIX процессам разрешено создавать другие процессы, независимо претендующие на системные ресурсы. Системный вызов fork приводит к разбиению процесса на два выполняющихся независимо: РОДИТЕЛЬСКИЙ процесс и ПОРОЖДЕННЫЙ процесс. В UNIX имеются средства, обеспечивающие синхронизацию процессов, при помощи которых родительский процесс может ожидать завершения работы порожденного процесса, а также получать информацию о его состоянии.

Процессы в UNIX могут связываться непосредственно друг с другом через логические межпроцессные каналы, называемые ТРАНСПОРТЕРАМИ (pipes). Для передачи сообщений между процессами используются обыкновенные запросы на чтение и запись. Ни процессу, ни тем более простому файлу не требуется знать, что в этом принимают участие транспортеры. Синхронизация процессов и буферизация сообщений автоматически обрабатываются системой. Несколько процессов при помощи транспортеров могут связываться в линейную структуру и образовывать КОНВЕЙЕР (pipeline) у в котором выход одного процесса является входом другого.

UNIX поддерживает иерархическую файловую систему. Для файлов пользователя имеются каталоги с древовидной структурой; система тоже имеет несколько каталогов для своих нужд. Один и тот же файл может фигурировать в нескольких различных каталогах, возможно, и под разными именами. Все такие файлы имеют одинаковый статус. Иными словами, файл не принадлежит какому-то одному определенному каталогу. Файлы могут быть и не включены ни в один каталог.

Самое необычное свойство файловой системы UNIX - существование СПЕЦИАЛЬНЫХ ФАЙЛОВ. Каждому устройству ввода-вывода, поддерживаемому UNIX, соответствует по крайней мере один такой файл. Специальные файлы читаются и пишутся так же, как и обычные, содержащиеся на диске. Однако запрос на чтение и запись специальных файлов вызывает запуск соответствующего устройства. Это делает файлы ввода-вывода и устройства ввода-вывода очень похожими. Например, устройства являются объектом того же самого механизма защиты, что и обычные файлы.

Дальнейшую информацию и ссылки, касающиеся системы UNIX, можно найти в Баурн [1983], Дейтел [1984] и Ритчи [1978].

6.5.3. NOS

NOS - операционная система, предназначенная для использования на ЭВМ серии CDC CYBER и 6000. Это мультипроцессорные машины, имеющие один или два ЦЕНТРАЛЬНЫХ ПРОЦЕССОРА (ЦП) и до 20 ПЕРИФЕРИЙНЫХ ПРОЦЕССОРОВ (ПП). ЦП и ПП работают с общей центральной памятью; таким образом, NOS является примером мультипроцессорной системы с сильно связанными процессорами. Однако к каналам и устройствам ввода-вывода ЦП не имеет прямого доступа. Этот доступ обеспечивается периферийными процессорами. Используя специальные аппаратные команды, ПП могут запускать и останавливать ЦП.

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

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

Каждому заданию пользователя в центральной памяти отведена определенная область. Машины, на которых функционирует NOS, имеют аппаратно реализованные регистры перемещений, аналогичные тем, что были рассмотрены в разд.6.2.4; поэтому существует возможность перемещать задания во время их выполнения. NOS получает от этого двойное преимущество. Во-первых, заданиям разрешается через запросы на обслуживание операционной системе увеличивать или уменьшать количество выделенной им центральной памяти. Чтобы удовлетворить эти запросы, NOS перераспределяет содержимое центральной памяти. Во-вторых, NOS может временно приостановить выполнение некоторого задания с тем, чтобы сделать центральную память доступной процессу с более высоким приоритетом. Это называется СВЕРТЫВАНИЕМ задания. Когда же задание вновь будет развернуто и продолжит выполнение, то не обязательно, чтобы оно размещалось в той же самой области центральной памяти, что и раньше.

Первые 64 слова области памяти, выделенной заданию, зарезервированы под связь с операционной системой. Чтобы осуществить запрос на обслуживание операционной системе, программа пользователя заносит его код в фиксированное место области связи. Программа MTR периферийного процессора непрерывно сканирует эти области, проверяя наличие кода запроса. Когда он обнаружен, MTR активизирует CPUMTR для того, чтобы тот назначил ПП для запрашиваемого действия.

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

Если программа желает продолжать работу во время выполнения сервисной функции, то она запрашивает функцию без заказа автоматического перевызова. Если программа должна ждать завершения затребованного сервиса, то она делает специальный запрос на периодический перевызов, в результате чего она на некоторое время переводится в заблокированное состояние. По истечении этого времени программа снова активизируется и проверяет, может ли она продолжить работу или нет. Если сервисная функция еще не завершена, то программа вновь делает запрос на ПЕРИОДИЧЕСКИЙ перевызов. Аналогичный технический прием может быть использован для осуществления нескольких запросов на обслуживание, например одновременного выполнения несвязанных друг с другом операций ввода-вывода.

Дополнительную информацию по NOS можно найти в CDC [1981 и 1983].

6.5.4. VAX/VMS

VAX/VMS - это многоцелевая операционная система, предназначенная для использования на ЭВМ серии VAX. Под ее управлением можно работать в трех режимах: пакетном, разделения времени и реального времени. Неотъемлемой частью VAX является виртуальная память, а управление ею - одним из наиболее интересных свойств операционной системы VAX/VMS.

Вся виртуальная память VAX/VMS состоит из 2**32 байт и делится на АДРЕСНОЕ ПРОСТРАНСТВО СИСТЕМЫ и АДРЕСНОЕ ПРОСТРАНСТВО ПРОЦЕССОВ размером 2**31 байт каждый. Адресное пространство системы распределяется между всеми процессами, однако каждый процесс пользователя имеет и свое собственное адресное пространство. Оно в свою очередь делится на программную область и область управления. В первой из них находится программа, выполняемая процессом в текущий момент. Вторая отведена под системные стеки и прочую информацию, используемую операционной системой.

Виртуальная память состоит из 512-байтовых страниц. Существует СИСТЕМНАЯ ТАБЛИЦА страниц, которая содержит по одному входу для каждой страницы адресного пространства системы. По нему можно судить, загружен ли соответствующий ей страничный кадр или нет. Начальный адрес и длина таблицы задаются в двух специальных регистрах. Адресное пространство процессов описывается двумя таблицами страниц: одной для программной области, другой для области управления. Таблицы располагаются в адресном пространстве системы и сами могут участвовать в страничных операциях. Программы управления памятью также используют таблицу, содержащую информацию о состоянии всех страничных кадров в памяти, всех виртуальных страниц в системе и об их размещении.

По страничному прерыванию активизируется системная программа, называемая pager. Она выбирает подходящий страничный кадр и переписывает требуемую виртуальную страницу в память. Для каждого процесса число страниц, одновременно находящихся в памяти, ограничено. Когда достигается их предельное число, pager выбирает страницу для выталкивания из памяти. Максимальное число страниц для каждого процесса устанавливается динамически и зависит от частоты происходящие в нем страничных прерываний.

Измененные страницы, выталкиваемые из памяти, не переносятся во внешнюю память сразу. Вместо этого они заносятся в так называемый СПИСОК ИЗМЕНЕННЫХ СТРАНИЦ. Чтобы минимизировать накладные расходы, связанные со страничным обменом, pager пытается записывать страницы кластерами (т.е. по несколько страниц за одну операцию записи).

Если страница из этого списка запрашивается до того, как она в действительности записана, то для ее восстановления не требуется выполнения какого-либо физического ввода-вывода. Аналогично неизмененные страницы, выбранные для откачки заносятся в СПИСОК СВОБОДНЫХ СТРАНИЦ. Если входящая в него страница потребуется до того, как страничный кадр был заново использован, она может быть перевызвана без выполнения физического ввода-вывода.

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

Функция планирования процессов в VAX/VMS основана на 32-уровневой системе приоритетов. Нижние 16 уровней зарезервированы для обычных процессов; остальные - для процессов реального времени. Каждый процесс в системе определен БЛОКОМ УПРАВЛЕНИЯ ПРОЦЕССА и своим заголовком. Блок управления процесса содержит кластеры ФЛАГОВ СОБЫТИЙ, которые могут быть использованы процессами для связи и координации их действий.

Для синхронизации взаимодействующих процессов при использовании ими разделяемых ресурсов в VAX/VMS имеются средства БЛОКИРОВКИ. Существует шесть различных режимов блокировки, каждый из которых обеспечивает свой уровень разделения.

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

Дальнейшую информацию по VAX/VMS можно найти в Дейтел [1984] и DEC [1982].

6.5.5. VM/370

[Из "VM/370 - A Study of Multiplicity and Usefulness", by L.H.Seawright and R.A.MacKinnon, IBM Systems Journal, Vol.18, No.1, copyright 1979 International Business Machines Corporation]

VM/370 - операционная система для IBM 370, поддерживающая работу многих виртуальных машин. Каждому пользователю кажется, что он имеет дело с самой System/370, включая и каналы ввода-вывода.

Система VM/370 состоит из двух основных компонентов: УПРАВЛЯЮЩЕЙ ПРОГРАММЫ (УП) и ДИАЛОГОВОЙ МОНИТОРНОЙ СИСТЕМЫ (ДМС). УП является распорядителем ресурсов системы и выполняет те же функции, что и мониторы виртуальных машин, рассмотренные в разд.6.4.2. В УП также имеются команды, помогающие в управлении и отладке операционной системы на виртуальной машине. Они позволяют выполнять такие действия, как чтение содержимого виртуальной памяти и установка контрольных точек. В сущности, это те же отладочные функции, которые системный программист может выполнить на пульте реального ЦП.

На виртуальных машинах под управлением УП могут функционировать все операционные системы, обычно используемые на ЭВМ серии 370. Включая, конечно, и саму УП. Возможность выполнения УП на виртуальной машине под управлением другой УП делает возможной поддержку виртуального окружения УМ/370. Это особенно полезно при отладке частей УП или постановке новых версий УП в системе.

Диалоговый монитор системы (ДМС) обеспечивает интерактивную поддержку единственного пользователя на одном терминале. Работа со многими терминалами осуществляется благодаря тому, что УП имеют возможность поддерживать функционирование многих виртуальных машин с ДМС на каждой из них. ДМС разработан специально для VM/370 и в своей работе прямо зависит от УП. В отличии от других операционных систем для IBM 370 функционировать самостоятельно на реальной машине он не может. Однако под управлением УП ДМС работает эффективнее, чем другие системы.

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

Когда имеешь дело с использованием операционных систем, поддерживающих работу виртуальных машин, приходится принимать во внимание производительность всей системы. Издержки, возникающие в результате функционирования монитора виртуальной машины, обычно уменьшают пропускную способность системы и увеличивают время ответа. В VM/370 имеются средства измерения производительности, с помощью которых во время выполнения УП можно получить информацию об использовании ресурсов, а также осуществить сбор результатов измерений для дальнейшего анализа.

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

Другие усовершенствования в программном обеспечении более специфичны и подразумевают изменения в операционных системах, на которых реализовано виртуальное окружение. Эти изменения позволяют операционной системе виртуальной машины узнать, что она работает под управлением VM/370, и связаться непосредственно с УП. Тем самым система не должна больше выполнять операции, которые при ее работе на виртуальной машине были бы излишни. Эти изменения, называемые в совокупности рукопожатием, повышают эффективность и увеличивают производительность виртуальной машины.

В определенных моделях System/370 и соответствующих процессорах имеются специальные средства, предназначенные для использования с VM/370. Они позволяют аппаратно управлять некоторыми наиболее часто выполняемыми УП функциями. В результате достигается значительное увеличение производительности виртуальной машины.

Дальнейшую информацию по VM/370 и соответствующие ссылки можно найти в Дейтел [1984] и Сирайт [1979].
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Вт Фев 16, 2021 3:42 pm

УПРАЖНЕНИЯ

РАЗДЕЛ 6.2
1. В разд. 6.2.1 мы предполагали, что если происходит какое-то прерывание, то все другие прерывания равного или более низкого приоритетов запрещаются. Будет ли схема, рассмотренная в тексте, работоспособной, если мы просто запретим все прерывания того же класса?

2. Предположим, что обработка прерываний определенного класса необычайно сложна. Может оказаться нежелательным в течение всего времени обработки оставлять другие прерывания закрытыми. Предложите метод обслуживания прерываний, при котором большую часть времени прерывания могут оставаться открытыми.

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

4. Предположим, что время работы задания пользователя на ЦП ограничено. Его предельная величина для разных заданий различна. Какая часть операционной системы должна следить за тем, чтобы лимит времени не был превышен, и как это может быть сделано?

5. На УУМ/ДС установкой бита IDLE в 1 ЦП переводится в состояние простоя. Нужно ли, чтобы подобная возможность была реализована на ЭВМ, поддерживающей мультипрограммную работу?

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

7. Как вы ответите на вопросы упр.6, если операционная система поддерживает еще и работу в режиме реального времени?

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

9. Вместо одного списка всех заданий с информацией об их состоянии в некоторых системах существует несколько списков (т.е. список готовых заданий, список блокированных заданий и т.д.). Каковы преимущества и недостатки такого подхода?

10. В примере на рис.6.15 считалось, что прерывания по таймеру отсутствуют. Предположим, что квант времени, назначенный процессу P1, истекает в интервале между моментами (2) и (3). Перерисуйте диаграмму До момента (10) так, чтобы были отображены все возможные последовательности событий, которые могут произойти после прерывания по таймеру.

11. Перерисуйте диаграмму на рис.6.15 с учетом того, что процесс P2 имеет более высокий приоритет диспетчеризации, чем процесс P1, и что используется приоритетное планирование процессов.

12. Предположим, что в мультипрограммном режиме выполняются два задания. Задание A много времени расходует на выполнение на ЦП и мало - на ввод-вывод. Задание B занимает мало времени ЦП, а операций ввода-вывода осуществляет много. Какому из этих заданий следует присвоить больший приоритет диспетчеризации для увеличения производительности всей системы?

13. Предположим, что супервизор ввода-вывода имеет возможность выбирать запросы из очереди к каналу, основанной на системе приоритетов. Какому из двух заданий упр.12 следует дать в этом случае больший приоритет на ввод-вывод?

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

15. Какие вы выберете число и размер разделов для системы управления памятью с разделами фиксированного размера?

16. Какие преимущества и недостатки имеет подход к защите памяти с использованием граничных регистров по сравнению с защитой по ключам?

17. Часто в мультипрограммной системе операция ввода-вывода для одного задания выполняется в то время, пока другой работает на ЦП. Как обеспечить защиту памяти для такой операции ввода-вывода? Как, например, можно предотвратить чтение данных одним заданием из раздела данных другого?

18. Предположим, что в некоторой машине имеются флаговые биты, при помощи которых отражается тип каждого данного, сохраняемого в памяти или регистре (например, целый, символьный, число с плавающей точкой, команда, адрес). Как эта информация может быть использована для реализации в машине перемещаемых разделов?

19. Нужны ли аппаратные средства защиты памяти на машине, где управление памятью осуществляется при помощи размещения страниц по запросу?

20. Нужен ли на машине из упр.19 перемещаемый загрузчик?

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

22. Какие методы может использовать программист для увеличения локальности ссылок в программе? Какие технические приемы программирования и какие структуры данных могут привести к ухудшению локальности ссылок?

23. Как будет выглядеть диаграмма на рис.6.26б для программы, у которой отсутствует локальность ссылок (т.е. программа, в которой каждое обращение к памяти не зависит от предыдущего)? Как будет выглядеть диаграмма для программы с идеальной локальностью ссылок (т.е. та, у которой каждое обращение осуществляется к той же странице, что и предыдущее)?

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

25. Придумайте алгоритмы работы обработчиков прерываний четырех уровней в мультипрограммной операционной системе с размещением страниц по запросу, работающей на УУМ/ДС.

26. Почему у прерываний по таймеру более низкий приоритет на, УУМ/ДС, чем у SVC-прерываний (см. разд.6.2.1)?

27. Почему прерывание по вводу-выводу имеют более низкий приоритет на УУМ/ДС, чем SVC-прерывания?

РАЗДЕЛ 6.3
1. Придумайте алгоритм работы программы управления файлами, которая выполняет операции блокирования и буферизации, демонстрируемые на рис.6.28.

2. Когда может быть выгодно при работе с последовательным файлом использовать более двух буферов?

3. Нарисуйте диаграмму изменения состояний, аналогичную приведенной на рис.6.7, для трехуровневой процедуры планирования, показанной на рис.6.29б.

4. Может ли быть, чтобы время прохождения задания в мультипрограммной операционной системе было меньше времени его прохождения в однопрограммной системе на той же машине?

5. Опишите алгоритмы работы и структуры данных программ операционной системы, реализующие функции запроса и отказа, рассмотренные в разд.6.3.

6. Предположим, что в системе блок состояния событий для каждого ресурса определен таким образом, что "событие наступило" эквивалентно в логическом смысле "ресурс свободен". Могут ли функции запроса и отказа быть реализованы с использованием операций WAIT и SIGNAL, рассмотренных в разд.6.2.2?

7. Рассмотрим две программы, приведенное на рис.6.31. Вместо использования операций запроса и отказа мы можем запретить прерывание по таймеру в промежутке между строками 24 и 27 программы P1 и строками 37 и 40 программы P2. (Прерывания могут быть закрыты и открыты при помощи запросов на обслуживание операционной системе). Может ли это быть практическим решением проблемы, рассмотренной в тексте?

8. Как операционная система может определить, что произошла взаимоблокировка?
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Ср Фев 17, 2021 1:06 pm

ГЛАВА 7. ДРУГИЕ КОМПОНЕНТЫ СИСТЕМНОГО ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ

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

Раздел 7.1 посвящен описанию задач и функций обобщенных систем управления базами данных (СУДБ) и их взаимодействию с операционными системами. В разд.7.2 дается краткое описание диалоговых систем редактирования текстов, обсуждаются заложенные в них основные идеи.

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

7.1. СИСТЕМЫ УПРАВЛЕНИЯ БАЗАМИ ДАННЫХ

В этом разделе будут рассмотрены основные задачи и функции обобщенной системы управления базами данных (СУБД). СУБД нас будет интересовать главным образом с точки зрения пользователя. Мы рассмотрим также, как связаны между собой СУБД и другие компоненты программного обеспечения системы. Сравнительный анализ теоретических и практических реализаций системы управления базами данных можно найти у Уидерхолда [1983], Дейта [1981], Ульмана [1980] и других авторов.

В разд.7.1.1 обсуждаются проблемы, решение которых привело к созданию систем управления базами данных, а также вводится понятие СУБД. В разд.7.1.2 в общих чертах рассказывается о том, что представляют собой эти системы с точки зрения пользователя. В разд.7.1.3 рассматривается вопрос о связи СУБД с другими компонентами системного программного обеспечения, в особенности с операционными системами.

7.1.1. ОСНОВНЫЕ КОНЦЕПЦИИ СУБД

Разработка систем управления базами данных в большой степени явилась результатом решения двух основных проблем, возникших при работе с обычными системами файловой обработки: ИЗБЫТОЧНОСТИ и ЗАВИСИМОСТИ данных. Рассмотрим, например, рис.7.1, на котором показаны в упрощенном виде несколько систем файловой обработки для некоего гипотетического университета. Сюда входят: система регистрации, позволяющая следить за составом студентов на курсах, система финансовой помощи, контролирующая расход стипендиального фонда, система платежных ведомостей для всего университета, включая профессорско-преподавательский состав и студентов, работающих по найму.

Бек. Введение в системное программирование. 1988 - Страница 2 8840110
Рис.7.1. Раздельные системы файловой обработки.

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

Использование автономных систем файловой обработки, аналогичных тем, что показаны на рис.7.1, часто приводит к значительной ИЗБЫТОЧНОСТИ данных, т.е. к дублированию элементов данных в различных файлах. Например, число курсов, на которых числится студент, может храниться как в одном из файлов системы регистрации, так и в файле системы финансовой помощи. А если к тому же студент работает в университете по найму или получает стипендию, то его фамилия и адрес могут появиться в трех различных местах.

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

Одно из возможных решений проблемы избыточности показано на рис.7.2а. Информация для всех файловых систем собрана в объединенную БАЗУ ДАННЫХ для всего университета, В ней может содержаться только одна копия каждого логического элемента данных, поэтому избыточность исключается. Сама база данных состоит из набора файлов. Каждый из них может использоваться различными прикладными программами, которым требуется один и тот же элемент данных.

Бек. Введение в системное программирование. 1988 - Страница 2 8840210[
Рис.7.2. Зависимые от данных программы, использующие объединенную базу данных. (То, что требует изменения при добавлении новой системы, заштриховано).

Хотя только что описанный подход решает проблему избыточности данных, он может спровоцировать и другие трудности. Прикладные программы, имеющие дело непосредственно с физическими устройствами, ЗАВИСЯТ ОТ ДАННЫХ. Речь идет об их зависимости от таких характеристик, как формат записи, организация файла и порядок следования. При изменении характеристик приходится модифицировать и прикладные программы.

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

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

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

Решить только что рассмотренные проблемы можно, сделав так, чтобы прикладные программы не зависели от таких деталей, как организация файлов и формат записей. Как это можно сделать, показано на рис.7.3а. Программы пользователей имеют дело не с самими файлами из базы данных, а делают запросы СИСТЕМЕ УПРАВЛЕНИЯ БАЗОЙ ДАННЫХ (СУБД) на логическом уровне без учета того, как данные хранятся в файле. Например, программа может запросить текущую регистрационную информацию по определенному студенту. Посредством обращения к ОПИСАНИЮ ОТОБРАЖЕНИЯ ДАННЫХ СУБД определит нужные физические файлы и порядок доступа к ним. После чтения файлов содержащаяся в них информация будет приведена к нужному прикладной программе виду. Об этом более подробно говорится в разд.7.1.2 и 7.1.3.

Бек. Введение в системное программирование. 1988 - Страница 2 8840410
Рис.7.3. Независимые от данных программы, использующие систему управления базами данных. (То, что требует изменения при добавлении новой системы, заштриховано).

НЕЗАВИСИМОСТЬ ДАННЫХ, полученная с помощью только что описанного подхода, означает, что можно менять структуры файлов, не трогая при этом прикладные программы. Рассмотрим, например, рис.7.3б. Пусть, как и раньше, добавляется новая консультационная система; в базу данных включен новый файл, а один уже существующий модифицирован (на рисунке он заштрихован). Эти изменения повлекли за собой корректировку описания отображения данных, однако сами прикладные программы остались без изменений. Как и раньше, по логическому запросу будет выполнен ряд операций над файлами базы данных. Однако прикладные программы об этом не узнают, потому что они имеют дело только с логическими элементами информации, а не с тем, как информация хранится в файлах.

Независимость данных, обеспечиваемая системой управления базами данных, важна и по другим причинам. При желании в любой момент могут быть заменены технические средства, на которых реализована база данных. Например, вся она или же ее часть может быть перенесена на запоминающее устройство другого типа. Файлы могут быть реорганизованы, пересортированы в различные последовательности или проиндексированы другим набором ключей. Обычно это делает АДМИНИСТРАТОР БАЗЫ ДАННЫХ, который старается так организовать и хранить данные, чтобы база данных использовалась с большей эффективностью. Все этим изменения можно сделать, не оказывая влияния на прикладные программы. На самом деле программы вообще не в состоянии определить, были ли внесены такие изменения.

Из-за того что каждое обращение к базе данных осуществляется при помощи описания отображения данных, использование СУБД приводит к большим накладным расходам системы, чем при выполнении программ, зависящих от данных. Однако выгода, получаемая от независимости данных и сокращения их избыточности, обычно значительнее дополнительных накладных расходов. Это особенно справедливо в случаях, когда содержимое и структура базы данных являются предметом периодических изменений при добавлении новых приложений.

7.1.2. УРОВНИ ОПИСАНИЯ ДАННЫХ

Как видно из предыдущего раздела, использование системы управления базами данных делает прикладные программы независимыми от физического способа их хранения. Если в обычных файловых системах программист имеет дело с описаниями способов организации, создания последовательностей и индексирования файлов, то при работе с СУБД программисту все эти детали обычно не известны. Но, даже если он с ними и знаком, он не смог бы этим воспользоваться, поскольку физический носитель базы данных может быть заменен в любое время. Таким образом, прикладной программист должен иметь представление о данных на логическом уровне, не связанном никак со структурой файлов и другими вопросами физического хранения.

Информация, хранимая СУБД, в зависимости от нужд пользователя может быть представлена по-разному. Наиболее общим представлением является полное логическое описание базы данных, называемое СХЕМОЙ. На рис.7.4 показан пример такой схемы. Она состоит из набора логических записей базы данных, таких как СТУДЕНТ и КУРС. Некоторые из них соединены линиями, отражающими возможные отношения между записями данных типов. Например, отношением "зачислен-на" задается, какой студент на какой курс зачислен. Предполагается, что это пример базы данных для гипотетического университета. Элементы данных в логических записях дают представление о типе содержащейся в них информации. В реальной базе данных, возможно, будет больше типов логических записей, а в каждой из них намного больше элементов данных.

Бек. Введение в системное программирование. 1988 - Страница 2 8840610
Рис.7.4. Схема для простой базы данных университета.

Системы управления базами данных сильно различаются в зависимости от типа записей и отношений, которые могут содержаться в схеме. Например, в некоторых системах требуется, чтобы логическое представление базы данных имело иерархическую или древовидную структуру, в то время как в других допускается наличие более общих типов отношений между записями. В некоторых системах явные связи между записями вообще запрещены. В этом случае отношения выражаются неявно через значения соответствующих элементов данных. Обсуждение подобных МОДЕЛЕЙ ДАННЫХ выходит за рамки нашей книги. Дальнейшую же информацию и ссылки можно найти в Дейт [1981].

Схема дает полное описание логической структуры и содержимого базы данных. Однако большинство прикладных программистов используют лишь небольшую толику этой информации; конкретная программа обычно имеет дело только с несколькими типами записей и отношений. Описание данных необходимое прикладной программе, дается ПОДСХЕМОЙ. Обычно у одной схемы существует много различных подсхем. Каждая из них дает представление о базе данных, соответствующей нуждам программ, которые используют эту подсхему.

На рис.7.5 показаны три подсхемы, соответствующие различным частям схемы, приведенной на рис.7.4. Подсхема а может быть использована программой, формирующей распечатку списка студентов по их специализации. Программе, использующей подсхему а, база данных кажется состоящей из некоторого числа записей КАТЕГОРИЯ-ДИПЛОМА, каждая из которых связана с набором записей СТУДЕНТ (одной для каждого студента данной специализации). Подсхема б может быть использована программой, печатающей списки групп каждого преподавателя. Представление базы данных в этом случае имеет трехуровневую структуру. Каждая запись, соответствующая преподавателю, связана с набором записей, соответствующих курсам, на которых он (или она) преподает; каждая запись, соответствующая курсу, в свою очередь связана с набором записей списка студентов, числящихся на курсах. Подсхема в будет использоваться программой, обрабатывающей платежные ведомости всего университета. Каждая логическая запись в этой подсхеме содержит информацию, необходимую для выдачи служащему зарплаты.

Бек. Введение в системное программирование. 1988 - Страница 2 8840710
Рис.7.5. Три возможные подсхемы, соответствующие схеме на рис.7.4.

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

В подсхеме б прикладные программы используют имена записей, отличные от тех, что содержатся в схеме. Возможно также использование иных имен и для других элементарных данных. Запись ПРОФ.-ПРЕП.-СОСТАВ в подсхеме б содержит только часть, а не всю информацию, соответствующую записи ПРОФ.-ПРЕП.-СОСТАВ в схеме. Запись ДИСЦИПЛИНА в подсхеме содержит всю информацию записи КУРС в схеме. Запись СТУДЕНТ-КУРСА в подсхеме включает информацию из записи СТУДЕНТ в схеме. Однако в запись СТУДЕНТ-КУРСА входит также и информация о специализации студента, которая содержится в записи КАТЕГОРИЯ-ДИПЛОМА, логически связанной с записью СТУДЕНТ отношением "специализируется-в".

Подсхема в состоит из единственной логической записи типа ОПЛАТА, данные в которой могут быть сформированы при помощи трех различных записей схемы. Информация, касающаяся оклада и налогов, может быть взята из записи ПЛАТЕЖНАЯ-ВЕДОМОСТЬ, принадлежащей схеме. Такая информация, как фамилия служащего и его адрес, извлекается с использованием отношений "студент-по-найму" и "преподаватель-по-найму" из записей либо СТУДЕНТ, либо ПРОФ.-ПРЕП.-СОСТАВ, какая больше для этого подходит. Поле данных в ОПЛАТА, обозначенное С/П, показывает, к кому запись имеет отношение: к студенту, работающему по найму, или к преподавателю.

Подсхема дает прикладной программе наиболее отвечающее ее нуждам представление о базе данных. За переводом информации, содержащейся в базе данных, в форму, заданную подсхемой, следит СУБД (разд.7.1.3). В результате писать прикладную программу проще и легче, потому что программисту не нужно использовать элементы данных и отношения, не относящиеся к самой прикладной программе. Кроме того, подсхема помогает в обеспечении защиты данных: программа не имеет возможности обращаться к элементам данных, не описанным в ее подсхеме.

Итак, мы рассмотрели три различных уровня описания данных в системе управления базами данных: подсхемы, схему и описание отображения данных. Для создания базы данных на каждом уровне СУБД применяются так называемые ЯЗЫКИ ОПИСАНИЯ ДАННЫХ. Подсхемы, используемые прикладными программистами, реализованы на удобном для них ЯЗЫКЕ ОПИСАНИЯ ПОДСХЕМ. Часто эти языки появляются в результате расширения возможностей описания данных в используемом языке программирования. Подсхемы, однако, создаются и поддерживаются администратором базы данных. Определяя подсхему, он должен быть уверен, что представление данных в подсхеме может быть получено из схемы и содержит только те элементы данных, которые разрешено использовать данной прикладной программе.

Сама схема и описание отображения физических данных обычно используются только администратором базы данных. Во многих системах ЯЗЫК ОПИСАНИЯ СХЕМ тесно связан с языком описания подсхем. Поскольку схема непосредственно прикладными программами не используется, возможно также использование и более общего языка. На язык ОПИСАНИЯ ФИЗИЧЕСКИХ ДАННЫХ влияют типы логических структур, поддерживаемых схемой, а также типы файлов и запоминающих устройств, поддерживаемых СУБД.

Дальнейшее обсуждение и примеры языков описания данных можно найти в Дейт [1981].

7.1.3. ИСПОЛЬЗОВАНИЕ СУБД

В двух предыдущих разделах мы ввели основные понятия и терминологию по системам управления базами данных. В этом разделе мы завершим описание СУБД, для чего рассмотрим их взаимодействие с пользователями и связь СУБД с другими компонентами системного программного обеспечения.

Два основных способа взаимодействия пользователя с СУБД показаны на рис.7.6. Пользователь может написать исходную программу обычным способом, используя общецелевые языки программирования, такие как Кобол, ПЛ/1 или ассемблер. Однако вместо имеющихся в этих языках операторов ввода-вывода программист пишет команды на языке манипулирования данными (ЯМД), который предназначен для взаимодействия пользователя с СУБД. Эти команды часто создаются так, чтобы ЯМД казался простым расширением языка программирования. Как показано на рис.7.6а, для перевода команд ЯМД в операторы языка программирования, которые вызывают программы СУБД, может быть использован препроцессор. После этого модифицированная исходная программа компилируется стандартным способом. Другой подход заключается в том, чтобы изменить компилятор так, чтобы он сам обрабатывал операции ЯМД. Некоторые языки управления данными определяются как набор операторов CALL, используемых в языке программирования, в результате чего отпадает надобность в препроцессировании или модификации компилятора.

Бек. Введение в системное программирование. 1988 - Страница 2 8841010
Рис.7.6. Взаимодействие с СУБД посредством языка манипулирования данными (а) и языка запросов (б).

Другой подход к взаимодействию с СУБД, показанный на рис.7.6б, не требует от пользователя написания программы для осуществления доступа к базам данных. Вместо этого пользователи вводят команды на специальном, задаваемом СУБД ЯЗЫКЕ ЗАПРОСОВ. Эти команды обрабатываются интерпретатором языка запросов, который для выполнения требуемых операций вызывает программы СУБД.

Оба подхода имеют свои преимущества. Использование языка запросов позволяет получить результаты гораздо быстрее, поскольку нет необходимости писать и отлаживать программы. Языки запросов могут с успехом применяться непрофессиональными программистами и теми, кто программирует лишь от случая к случаю. Однако большинство языков запросов содержит встроенные ограничения. Например, бывает сложно или вообще невозможно выполнить функцию, на которую язык не рассчитан. С другой стороны, ЯМД позволяет программисту использовать гибкость и все возможности языков программирования общего назначения; однако этот подход потребует от пользователей гораздо больших усилий. В большинстве современных систем управления базами данных имеется как язык запросов, так и ЯМД; при необходимости пользователь выбирает удобную для него форму взаимодействия. Дальнейшую информацию о ЯМД, языках запросови их примеры можно найти в Дейт [1981].

При обработке запроса СУБД выполняет одну и ту же последовательность операций вне зависимости от того, используется язык запросов или ЯМД. Эти действия показаны на рис.7.7. Начало последовательности событий приходится на момент, когда посредством запроса от прикладной программы A осуществляется обращение к СУБД (шаг 1 на рисунке). Если используется язык запросов, то программа A является интерпретатором языка запросов. Предполагается, что запросом является требование чтения данных из базы данных. Последовательность событий для других операций базы данных аналогична.

Бек. Введение в системное программирование. 1988 - Страница 2 8841110
Рис.7.7. Типичная последовательность выполняемых СУБД действий. (Заимствовано из Martin J. Computer Data - Base Organization, 2nd ed., c.1977, p.83. Reprinted by permission of Prentice-Hall Inc., Englewood Cliffs, N.J.).

Запрос от программы A формируется в терминах используемой ею подсхемы. Например, программа, использующая подсхему, приведенную на рис.7.5в, может запросить для заданного служащего запись ОПЛАТА. Чтобы обработать этот запрос, СУБД сначала должна исследовать используемое определение подсхемы (шаг 2). Она также должна рассмотреть отношения между подсхемой и схемой (шаг 3), чтобы интерпретировать запрос в терминах всей логической структуры базы данных. Так, например, СУБД обнаружит, что для того, чтобы снабдить программу А ожидаемой ею записью ОПЛАТА, ей потребуется прочитать запись ПЛАТЕЖНАЯ-ВЕДОМОСТЬ схемы для данного служащего (см. рис.7.4). Вдобавок СУБД потребуется для записи ПЛАТЕЖНАЯ-ВЕДОМОСТЬ, о которой идет речь, изучить отношения "студент-по-найму" и "преподаватель-по-найму" и прочитать соответствующие записи СТУДЕНТ и ПРОФ.-ПРЕП.-СОСТАВ.

После определения логических записей базы данных, которые должны быть прочитаны (в терминах схемы), СУБД изучает описание отображения данных (шаг 4). Эта операция дает информацию, необходимую для нахождения нужных записей в файлах базы данных. В этот момент СУБД уже перевела логический запрос записи подсхемы в физический на чтение данных из одного или нескольких файлов. Эти запросы для файла ввода-вывода передаются операционной системе (шаг 5) с использованием запросов на обслуживание, рассмотренных в гл.6. Затем операционная система выдает команды каналу и устройству для выполнения необходимых операций ввода-вывода (шаг 6). В результате осуществляется перенесение требуемых записей из базы данных в буферную область СУБД.

После того как завершено выполнение физических операций, все данные, запрошенные прикладной программой, находятся в центральной памяти. Однако эта информация еще должна быть преобразована в нужную программе форму. И снова СУБД делает это посредством сравнения схемы с подсхемой. В рассматриваемом нами примере СУБД извлечет данные из записи ПЛАТЕЖНАЯ-ВЕДОМОСТЬ и соответствующих записей СТУДЕНТ и ПРОФ.-ПРЕП.-СОСТАВ и создаст запись ОПЛАТА, запрашиваемую программой A. Запись ОПЛАТА затем будет помещена в рабочую область, заданную прикладной программой; на этом обработка программного запроса данных завершается. Наконец, СУБД возвратит управление прикладной программе, предоставив ей все многообразие информации о состоянии, включая указания о возможных ошибках.

Подробнее о затронутых в разделе вопросах можно прочитать в Уидерхолд [1983] и Дейт [1981].
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Ср Фев 17, 2021 1:15 pm

7.2. ТЕКСТОВЫЕ РЕДАКТОРЫ

[Использованы работы Norman Meyrowitz and Andreis van Dam, "Interactive Editing Systems: Part I and Part II", ACM Computer Surveys, September, 1982. Copyright 1982, ACM, Inc. В этих работах также содержится значительно более подробный материал о процессе редактирования, описано большое число существующих редакторов и приведена обширная библиография,- Прим. ред.]

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

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

7.2.1. ОПИСАНИЕ ПРОЦЕССА РЕДАКТИРОВАНИЯ

ИНТЕРАКТИВНЫЙ РЕДАКТОР - это программа, с помощью которой пользователь может создавать и изменять целевой документ. В понятие документа входят такие объекты, как программы, тексты, формулы, таблицы, диаграммы, чертежи и фотографии - все то, что может находиться на печатной странице. Здесь мы ограничимся рассмотрением ТЕКСТОВЫХ РЕДАКТОРОВ, в которых первичными редактируемыми элементами являются строки символов в целевом тексте.

Процесс редактирования документа - это диалог между пользователем и ЭВМ, во время которого требуется решить четыре задачи.
1. Выбрать часть целевого документа для отображения и обработки.
2. Определить, каким образом отформатировать и визуализировать полученный образ.
3. Задать и выполнить операции, изменяющие целевой документ.
4. Изменить в соответствии с этим образ.

Выбор части документа для отображения и редактирования включает первый просмотр всего документа для нахождения нужной области. Поиск производится с помощью таких операций, как выдать СЛЕДУЮЩУЮ (ПРЕДЫДУЩУЮ) СТРАНИЦУ и НАЙТИ ПО ОБРАЗЦУ. Во время просмотра определяется, где находится нужная область; выбор объекта для отображения и работы осуществляется посредством операции ФИЛЬТРАЦИИ. С ее помощью выделяется некоторое подмножество целевого документа, например страница или оператор. Затем посредством форматирования создается визуальное представление (образ), которое отобразит результат фильтрации на экране дисплея или на бумаге.

В реальном процессе собственно редактирования целевой документ создается и изменяется при помощи ряда операций, таких как ВСТАВИТЬ, УНИЧТОЖИТЬ, ЗАМЕНИТЬ, ПЕРЕДВИНУТЬ и КОПИРОВАТЬ. Функции редактирования часто ориентированы на работу с ЭЛЕМЕНТАМИ, зависящими от типа редактора. Например, редактор, ориентированный на работу с рукописями, может оперировать такими элементами, как отдельные символы, слова, строки, предложения и абзацы; программно-ориентированный редактор может работать с идентификаторами, ключевыми словами и операциями.

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

7.2.2. ИНТЕРФЕЙС ПОЛЬЗОВАТЕЛЯ

Пользователю интерактивного редактора предоставляется КОНЦЕПТУАЛЬНАЯ МОДЕЛЬ системы редактирования, т.е. некоторая абстрактная основа, на которой базируется и сам редактор, и все, с чем он работает. В сущности, простота понимания абстрактного представления целевого документа и его элементов достигается в концептуальной модели при помощи набора принципов, которые описывают конечный результат операций редактирования. В некоторых ранних строчных редакторах моделировалась работа на клавишных перфораторах. В них существовали операции над пронумерованными последовательностями 80-символьных строк-образов перфокарт. Операции выполнялись либо в пределах одной строки, либо над несколькими строками. В некоторых более современных ЭКРАННЫХ РЕДАКТОРАХ среда, в которой представлен документ, имеет вид квадрата, не ограниченного ни справа, ни снизу. Операции выполняются над его частями без учета границ строк. Через "окно" на дисплее пользователь видит только прямоугольное подмножество квадрата. Окно может передвигаться вправо и влево, вверх и вниз, при этом визуализируются соответствующие части документа.

Помимо концептуальной модели к интерфейсу пользователя относятся УСТРОЙСТВА ВВОДА и ВЫВОДА, а также ДИАЛОГОВЫЙ ЯЗЫК системы. Освещению этих аспектов посвящена оставшаяся часть раздела.

Устройства ввода служат для ввода элементов редактируемого текста, для введения команд и для задания редактируемых элементов. Эти устройства при использовании их редактором можно разделить на три категории: устройства для ввода текста, клавишные устройства и координатные манипуляторы. К устройствам для ВВОДА ТЕКСТА или СТРОК СИМВОЛОВ относится клавиатура (как у пишущей машинки), на которой пользователи нажатием и отпусканием клавиш вызывают генерирование соответствующих кодов. Фактически все имеющиеся в настоящее время клавиатуры ЭВМ - системы QWERTY (первые шесть букв второго ряда клавиатуры). Предлагались и другие конструкции клавиатур, некоторые из которых имели значительные преимущества по сравнению со стандартными. Однако возможно, что ни одна из них не найдет широкого применения в ближайшем будущем, поскольку для этого потребуются значительные усилия по переучиванию.

КЛАВИШНЫЕ УСТРОЙСТВА или УСТРОЙСТВА ВЫБОРА вызывают прерывание или установку системного флага, что приводит к выполнению прикладной программой определенных действий. К таким устройствам обычно относятся функциональные клавиши, имеющиеся на алфавитно-цифровой клавиатуре или на самом дисплее. Клавиши могут моделироваться и программно - посредством визуализации строк текста или символов. Вместо нажатия на клавишу пользователь выбирает строку или символ.

Координатные манипуляторы - это двумерные аналого-цифровые преобразователи, отслеживающие действия, выполняемые пользователем, и устанавливающие в зависимости от этого курсор на экране. К таким устройствам относятся КООРДИНАТНАЯ РУЧКА УПРАВЛЕНИЯ КУРСОРОМ, ЭКРАННЫЕ СЕНСОРНЫЕ ПАНЕЛИ, ПЛАНШЕТКИ и "МЫШКА". Последние два устройства чаще всего используются для редактирования. ПЛАНШЕТКА - это плоская прямоугольная панелька, обладающая электромагнитной чувствительностью. По ее поверхности перемещается либо игла, похожая на шариковую авторучку, либо шайба - маленькое устройство, умещающееся в ладони. Планшетка передает системной программе текущие координаты иглы или шайбы. Программа может затем перевести эти координаты в экранные и поместить курсор в соответствующее место. "МЫШКА" также умещается в руке и похожа на шайбу. Ее движение по гладкой поверхности вызывает относительное изменение местоположения, которое сравнивается системной программой с некоторым образцом. Координаты "мышки" опять-таки преобразуются в экранные, что приводит к перемещению курсора.

Координатный манипулятор, совмещенный с клавишным устройством, позволяет задавать либо определенную точку на экране, начиная с которой должен быть введен или уничтожен текст, либо начальную и конечную точки строки символов, которая будет обрабатываться. Фактически "мышка" или шайба Имеет встроенные клавиши, позволяющие пользователю сигнализировать о выборе. Когда курсор указывает на нужный элемент, пользователь сообщает об этом нажатием клавиши. Система сопоставляет местоположение курсора с элементом и выполняет требуемые действия.

Для моделирования координатного манипулятора часто используют устройства для ввода текста со "стрелочными" (курсорными) клавишами. На каждой из них изображена стрелка, направленная вверх, вниз, влево, вправо. Нажатие на эти клавиши вызывает генерирование соответствующей последовательности кодов; программа опознает ее и передвигает курсор в направлении, указанном стрелкой.

Все еще находящиеся в стадии исследования УСТРОЙСТВА ВВОДА ГОЛОСОМ, преобразующие произносимые слова в текстовые эквиваленты, могут стать в будущем устройствами для ввода текста. Пока еще ограниченные малым словарем (содержащим менее 1000 слов) анализаторы голоса, вероятно, скоро приобретут коммерческую жизнеспособность.

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

Существует несколько типов диалоговых языков текстового редактирования. Самыми старыми способами общения редактора с пользователем являются НАБИВКА или ВВОД ТЕКСТОВЫХ КОМАНД. Пользователь набивает в виде текстовых строк как сами команды, так и операнды. Все это пересылается редактору, а затем обычным образом отображается на устройстве вывода.

При набивке спецификаций пользователь должен знать точный вид всех команд или по крайней мере их аббревиатуру. Если командный язык сложен, пользователь должен постоянно обращаться за описанием наиболее редко встречающихся команд к руководству по системе или запрашивать помощь у машины. Вдобавок набивка отнимает много времени, особенно у неопытного пользователя. На устранение этих недостатков направлено использование ФУНКЦИОНАЛЬНЫХ (function-key) клавиш. При этом каждой команде соответствует клавиша на клавиатуре. Например, команде "вставить символ" (insert character) может соответствовать клавиша с надписью 1С. Задание команды, определяемой функциональной клавишей, обычно совмещено с перемещениями курсора, что значительно ограничивает набивку.

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

Для работы с системами, ориентированными на набивку (typing-oriented systems), требуется хорошее знание системы и языка, умение печатать на машинке. В системах, ориентированных на функциональную клавиатуру, часто используется либо совсем мало клавиш (при этом для формирования команд нужно многократное их нажатие), либо слишком много уникальных клавиш, в результате чего загромождается клавиатура. В любом случае при работе с такими системами от пользователя требуется еще большая ловкость, чем при использовании стандартной клавиатуры. На решение этой проблемы направлено использование интерфейса, ориентированного на меню. Меню - это набор текстовых строк или образов, выраженных графическими символами, изображающими объекты или операции. Пользователь может задавать нужные действия, выбирая их из меню. В меню, выдаваемом пользователю редактором, перечислены только те действия, которые могут быть предприняты в текущем состоянии системы.

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

7.2.3. СТРУКТУРА РЕДАКТОРА

Большинство текстовых редакторов вне зависимости от их особенностей и машин, на которых они реализованы, имеют структуру, аналогичную той, что показана на рис.7.8. БЛОК ОБРАБОТКИ КОМАНД воспринимает все, что вводится с устройства пользователя, и анализирует признаки и синтаксическую структуру команд. В этом смысле функции данного блока очень похожи на те, что выполняются в лексической и синтаксической фазах компиляции. Как и при компиляции, семантические программы могут вызываться непосредственно блоком обработки команд. В текстовом редакторе на семантические программы возложены такие функции, как редактирование и отображение. Блок обработки команд может сформировать необходимые операции редактирования и в промежуточном представлении. Затем оно будет расшифровано интерпретатором, который и вызовет нужные семантические программы. Использование промежуточного представления позволяет редактору применять для разных языков диалога с пользователем один и тот же набор семантических программ.

Бек. Введение в системное программирование. 1988 - Страница 2 8841810
Рис.7.8. Типичная структура редактора. (Заимствовано из Meyrowits N. van Dam A., "Interactive Editing Systems: Part I and Part II", ACM Computing Surveys, 1982. Copyright 1982, Association for Computing Machinery, Inc.).

Семантические программы реализуют функции просмотра, редактирования, отображения и визуализации. Операции редактирования всегда явно задаются пользователем; операции визуализации задаются неявно прочими тремя категориями операций. Однако операции просмотра и отображения могут вызываться либо пользователем в явном виде, либо неявно операциями редактирования. Отношения между этими классами операций могут быть гораздо более сложными, чем описано в разд.7.2.1. В частности, между тем, что содержится на экране, и тем, что может быть отредактировано, не обязательно должно быть только одно отношение. Чтобы продемонстрировать это, рассмотрим подробнее на рис.7.8 те блоки, которые имеют принципиальное значение.

При редактировании документа начало редактируемой области задается ТЕКУЩИМ УКАЗАТЕЛЕМ РЕДАКТИРОВАНИЯ, которым и управляет БЛОК РЕДАКТИРОВАНИЯ. Последний является объединением модулей, участвующих в редактировании. Указатель может быть сброшен или установлен в явном виде пользователем (например, посредством таких команд просмотра, как ВЫДАТЬ СЛЕДУЮЩИЙ АБЗАЦ или ВЫДАТЬ СЛЕДУЮЩУЮ СТРАНИЦУ), а также неявно системой как побочный эффект от предыдущих операций редактирования (например, УНИЧТОЖИТЬ АБЗАЦ). БЛОК ПРОСМОТРА в редакторе выполняет реальную установку текущих значений указателей редактирования и отображения. Тем самым определяется точка, с которой при отображении или редактировании начинается фильтрация.

При вводе пользователем команды редактирования соответствующий блок запускает фильтр, посредством которого на основе текущего значения указателя редактирования и параметров РЕДАКТИРУЮЩЕГО ФИЛЬТРА создается НОВЫЙ БУФЕР РЕДАКТИРОВАНИЯ. В параметрах, задаваемых как пользователем, так и системой, содержится информация о границах текста, участвующего в операции. Фильтрация может заключаться просто в последовательном выборе смежных символов, начиная с текущей точки. Или же она может зависеть от более сложных условий, задаваемых пользователем и имеющих отношение к содержимому и структуре документа. В этом случае собираются различные части документа, в том числе и несмежные. После этого семантические программы оперируют уже с буфером редактирования, являющимся, по существу, отфильтрованным подмножеством структуры данных документа. Заметим, что наше описание носит умозрительный характер. В действительности фильтрация и редактирование могут быть разделены, а буфер в явном виде не создан.

Аналогично при отображении документа начало отображаемой области задается ТЕКУЩИМ ЗНАЧЕНИЕМ УКАЗАТЕЛЯ отображения. Он поддерживается БЛОКОМ ОТОБРАЖЕНИЯ РЕДАКТОРА, состоящим из модулей, предназначенных для формирования следующего образа. Указатель может быть установлен и сброшен либо самим пользователем посредством команды просмотра, либо неявно системой как результат предыдущей операции редактирования. Когда содержимое экрана требуется обновить, блок отображения запускает соответствующий фильтр, с помощью которого на основе текущего значения указателя отображения и параметров фильтра создается буфер отображения. Параметры могут быть заданы как пользователем, так и системой, них содержится информация о количестве символов, необходимых для заполнения экрана, и о том, как их выбирать из документа. В строчных редакторах буфер отображения может содержать текущую строку, а в экранных редакторах - прямоугольный кусок текста. Буфер отображения поступает в распоряжение блока визуализации, при помощи которого содержимое буфера переносится на прямоугольный участок экрана, называемый ОКНОМ или ПОЛЕМ ИНДИКАЦИИ.

Бек. Введение в системное программирование. 1988 - Страница 2 8842010
Рис.7.9. Простая взаимосвязь между буферами редактирования и отображения.

Буфера редактирования и отображения, оставаясь независимыми, могут быть связаны по-разному. В простейшем случае они совпадают: пользователь редактирует текст прямо на экране (рис.7.9). С другой стороны, оба буфера могут быть полностью разделены. Например, при редактировании пользователь может дойти до строки 75 и, после того как она будет отображена, захотеть изменить в строках с 1 по 50 все вхождения словосочетания "гадкий утенок" на слово "лебедь". Для этого он использует такую команду, как change {изменить):

[1, 50] c/гадкий утенок/лебедь/

Одним из последовательно осуществляемых по этой команде действий будет неявный переход к первой строке файла. Затем выбранные из документа строки с 1 по 50 будут перенесены в буфер редактирования. Здесь и осуществятся последующие замены без соответствующей модификации образа. В случае обнаружения образца текущие значения указателей редактирования и отображения будут соответствовать последней из строк, в которой он был найден, а сама строка по умолчанию будет занесена в буфера редактирования и отображения. Если образец не найден, то в обоих буферах останется строка 75.

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

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

Перенос содержимого буфера отображения в окно осуществляется двумя блоками системы. Первый из них - БЛОК ОТОБРАЖЕНИЯ - формирует идеальный образ, обычно имеющий независимое от устройства вывода промежуточное представление. Образ может быть очень простым и состоять из текста, умещающегося в окне и организованного так, чтобы слова в строках не обрывались посередине. Другая крайность - точная копия отформатированной и распечатанной страницы текста со всеми формулами, таблицами и рисунками. Второй блок - БЛОК ВИЗУАЛИЗАЦИИ - переносит идеальный образ, полученный от блока отображения, на физическое устройство вывода наиболее эффективным образом.

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

Независимые от устройства операции ввода и вывода позволяют упростить диалоговый язык. Отделение операций редактирования и отображения от функций визуализации делает ненужным создание разных версий редактора для каждого из устройств. Во многих редакторах используются БАЗЫ ДАННЫХ УПРАВЛЕНИЯ ТЕРМИНАЛАМИ. Вместо того чтобы держать последовательности управления в программах визуализации в явном виде, эти редакторы просто обращаются к терминально-независимым библиотечным программам, таким как СЧИТАТЬ ПОЛОЖЕНИЕ КУРСОРА (read cursor position) и СМЕСТИТЬ НА СТРОКУ ВНИЗ (scroll down). Такие библиотечные программы при выборе подходящей управляющей последовательности для конкретного дисплея используют базу данных управления терминалами. Следовательно, добавление нового терминала сводится к занесению в базу данных соответствующего описания.

Блоки редактора работают с документом пользователя, находящимся как в основной памяти, так и в файловой системе на диске. Загрузка всего документа в основную память может оказаться невозможной; если же загружена только его часть, а выполнение большого числа задаваемых пользователем операций влечет за собой чтение с диска, то редактирование может осуществляться недопустимо медленно. В некоторых системах эта проблема разрешается посредством переноса всего файла в виртуальную память с предоставлением операционной системе возможности эффективного осуществления размещения страниц по запросу. Или же реализуются входящие в редактор ПРОГРАММЫ СТРАНИЧНОГО ОБМЕНА, которые по необходимости читают в память одну или более логических частей документа. Эти части нередко называют СТРАНИЦАМИ, хотя между ними и страницами виртуальной памяти, а также твердыми копиями документа обычно ничего общего нет. Страницы остается в основной памяти до тех пор, пока по требованию пользователя не будет загружена другая часть документа.

Во внутреннем представлении системы документ организован не как последовательность строк и символов, а как структура данных редактора, позволяющая осуществлять добавление, уничтожение и модификацию при помощи минимума операций ввода-вывода и перемещений символов. На диске документ хранится либо в виде такой же структуры данных, либо в независимом от редактора общецелевом формате. В последнем случае текст состоит из символьных строк, перемежающихся управляющими символами, такими как КОНЕЦ СТРОКИ {linefeed) и ТАБУЛЯЦИЯ (tab).

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

Некоторыми системами редактирования с разделением времени используются аппаратные средства самих терминалов. Такого рода ИНТЕЛЛЕКТУАЛЬНЫЕ ТЕРМИНАЛЫ содержат микропроцессоры и локальную буферную память, в которой может осуществляться редактирование. Небольшие редакции выполняются не под управлением ЦП ведущего процессора, а самими терминалами. Например, редактор может передать текст объемом с экран от ведущего процессора терминалу. Далее пользователь волен добавлять или уничтожать символы и строки, используя для этого буфер и управляющие команды терминала. После того как буфер отредактирован, его обновленное содержимое передается назад, ведущему процессору.

Преимущество такого подхода в том, что ЭВМ не касаются никакие незначительные изменения или нажатия клавиш; но это и основной недостаток. Если используется неинтеллектуальный терминал, ЦП видит каждый набиваемый символ и может немедленно отреагировать на него, исправив ошибку, выдав подсказку, изменив структуру данных и т.п. Что касается интеллектуального терминала, то недостаток постоянного контроля со стороны ЦП часто означает ограниченность в функциональных возможностях, предоставляемых пользователю. В процессе автономной работы на таком терминале пользователь может пропустить момент выхода системы из строя. С другой стороны, в системах, где ввод каждого символа вызывает прерывание ЦП, весь набор аппаратных средств редактирования терминала может не использоваться в связи с тем, что ЦП необходимо отслеживать каждое нажатие клавиши и обеспечить эхо-контроль.
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Ср Фев 17, 2021 1:17 pm

7.3. СИСТЕМЫ ИНТЕРАКТИВНОЙ ОТЛАДКИ

[Использованы работы Rich Seidner and Nick Tindall, "Interactive Debug Requirements". SOFTWARE ENGINEERING Notes and SIGPLAN NOTICES, August 1983. Copyright 1983, Association for Computing Machinery, Inc.]

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

Ниже излагаются результаты, полученные во время совместной работы инициативной группы GUIDE/SHARE Language Futures Task Force (LFTF) и IBM. Группа LFTF была образована в результате деловых встреч двух основных групп пользователей SHAPE и GUIDE в 1979г. В ее задачи входило предоставить фирме IBM информацию о взглядах пользователей на будущее прикладных средств. Одно из направлений исследований было связано с интерактивной отладкой. В работе Сейднер [1983] говорится о том, как фирма IBM расценивает требования, предъявляемые к отладке, которые были сформированы группой LFTF. Все требования умышленно имеют общий характер и не ограничиваются архитектурой System/370, каким-то одним программным продуктом или операционной системой.

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

7.3.1. ФУНКЦИИ И ВОЗМОЖНОСТИ ОТЛАДЧИКОВ

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

Наиболее очевидными являются требования, предъявляемые к задаваемым самим пользователем функциям ТЕСТИРОВАНИЯ УСТРОЙСТВ (unit test functions). Среди них значительная группа связана со СЛЕЖЕНИЕМ ЗА ВЫПОЛНЕНИЕМ, т.е. за наблюдением и контролем за ходом выполнения программы. Например, программист может установить точки останова, по достижении которых происходит останов программы. В результате появится возможность проанализировать ее состояние и провести диагностику обнаруженных ошибок. Затем выполнение программы может быть продолжено. Или же программист может задать условные выражения, которые затем непрерывно вычисляются в процессе выполнения. Как только какое-нибудь из них приобретает значение "истина", выполнение программы останавливается. Аналогично может быть реализован останов программы в результате выполнения заданной последовательности команд. Если же функционирование программы может быть представлено графически, имеет смысл попробовать прогнать ее с различными скоростями, называемыми АЛЛЮРОМ (gaits).

В системах отладки должны быть реализованы и такие функции, как трассировка и обратная трассировка. ТРАССИРОВКА (tracing) позволяет следить за ходом выполнения логических операций и за изменением данных. Она может осуществляться на разных уровнях детализации: модульном, подпрограмм, команд ветвлений и т.п. Трассировка может также основываться на вычислении условных выражений, о чем говорилось выше. ОБРАТНАЯ ТРАССИРОВКА (traceback) может показать маршрут, который привел к текущему оператору. С ее помощью можно увидеть, какой оператор изменил значение данной переменной или параметра. Такая информация должна быть представлена в символической форме. Например, скорее следует выдавать номера операторов, а не их шестнадцатеричное смещение [Имеются в виду языки типа ассемблера и Бейсика, в которых операторы нумеруются.- Прим. ред.].

В отладочной системе важно иметь также хорошие средства ОТОБРАЖЕНИЯ ПРОГРАММ. Должна существовать и возможность вывода отлаживаемой программы вместе с номерами операторов. Пользователю следует разрешить задавать уровень отображения. Например, программа может быть выведена в том виде, как она написана, после макрорасширения и т.п. Полезно также иметь средства модификации и пошаговой перекомпиляции в процессе отладки. При этом система должна сохранять все заданные отладочные спецификации (точки останова, режимы отображения и т.д.), с тем чтобы программисту не пришлось заново выполнять отладочные команды. Нужно, чтобы существовала и возможность вывода любых переменных и констант в символьном виде и их модификации с последующим продолжением выполнения программы. Тем самым вне зависимости от средств, используемых в действительности, достигается эффект интерпретации.

В системах интерактивной отладки должны быть реализованы и многие другие функции и возможности. Их описание можно найти в Сейднер [1983].

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

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

Само использование настройки оказывает немалое и разнообразное влияние на диалог с отладчиком. Например, операторы присваивания, изменяющие значения переменных в процессе отладки, должны быть написаны в соответствии с синтаксисом и семантикой исходного языка программирования. Если в Коболе пользователь должен ввести команду MOVE 3.5 ТО A, то в Фортране она будет иметь вид A = 3.5. Точно так же в условных выражениях должна использоваться система обозначений исходного языка. Условие "A не равно B" в программе на Коболе будет иметь вид IF A NOT EQUAL TO B, а в программе на Фортране: IF(A.NE.B). Аналогичные различия существуют и для форм представления операторов, ключевых слов и т.п.

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

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

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

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

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

Различного рода проблемы возникают и с памятью, отводимой для переменных. Когда программа транслируется, каждой переменной в основной памяти отводится своя ЯЧЕЙКА (home location). Однако, и об этом говорилось в разд.5.2.2, значения переменных в целях увеличения скорости доступа к ним могут время от времени храниться в регистрах. Операторы, обращающиеся к этим переменным, используют значения, находящиеся в регистрах, а не в ячейках памяти. При такой оптимизации не возникает проблем с отображением значений этих переменных. Однако если в процессе отладки пользователь изменит значение переменной в памяти, то оно при возобновлении выполнения программы, вероятно, не будет использовано. Аналогично в случае глобальной оптимизации значение переменной может быть все время присвоено регистру. Тогда соответствующая ей ячейка памяти может вообще не существовать.

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

7.3.2. ВЗАИМОДЕЙСТВИЕ С ДРУГИМИ ЭЛЕМЕНТАМИ СИСТЕМЫ

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

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

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

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

7.3.3. ТРЕБОВАНИЯ, ПРЕДЪЯВЛЯЕМЫЕ К ИНТЕРФЕЙСУ ПОЛЬЗОВАТЕЛЯ

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

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

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

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

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

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

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

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Бек. Введение в системное программирование. 1988 - Страница 2 Empty Re: Бек. Введение в системное программирование. 1988

Сообщение автор Gudleifr Чт Фев 18, 2021 11:27 am

ПРИЛОЖЕНИЯ

ПРИЛОЖЕНИЕ А
СИСТЕМА КОМАНД И СПОСОБЫ АДРЕСАЦИИ УУМ/ДС

СИСТЕМА КОМАНД

При описании системы команд для обозначения соответствующих регистров используются прописные буквы. Буквой m обозначены адреса в оперативной, памяти, а буквой n - целые числа в диапазоне от 1 до 16. Идентификаторы регистров имеют обозначения r1 и r2. Скобки используются для обозначения содержимого области оперативной памяти или регистра. Так, выражение вида A<-(m..m+2) означает запись содержимого оперативной памяти с адреса m по m+2 в регистр A, а выражение вида m..m+2<-(А) - запись содержимого регистра A в слово, начинающееся с адреса m.

Буквы в столбце "примечания" имеют следующие значения:
P - привилегированная команда;
X - команда, исполняемая только на модели ДС;
F - команда арифметики с плавающей точкой;
C - по значению результата операции (<, =, >) устанавливается код услааия.

В столбце "формат" указывается, какой командный формат УУМ/ДС должен использоваться при ассемблировании команды; 3/4 означает, что может использоваться либо формат 3, либо формат 4. Для стандартной модели УУМ все команды ассемблируются в формате, описанном в разд.1.3.1 (он совместим с форматом 3). Неиспользуемые поля команды (например, такие как адресное поле в команде RSUB) содержат нулевые значения.

Бек. Введение в системное программирование. 1988 - Страница 2 8843210

Бек. Введение в системное программирование. 1988 - Страница 2 8843310

ФОРМАТЫ КОМАНД

Бек. Введение в системное программирование. 1988 - Страница 2 8843110

Бек. Введение в системное программирование. 1988 - Страница 2 88433a10

Бек. Введение в системное программирование. 1988 - Страница 2 8843410

СПОСОБЫ АДРЕСАЦИИ

Ниже даются описания способов адресации, которые используются для форматов 3 и 4. Комбинации адресных разрядов, не включенные в таблицу, рассматриваются машиной как ошибочные. В третьем столбце таблицы буква c используется для обозначения константы в диапазоне от 0 до 4095 (или адреса памяти, о котором известно, что он лежит в этом диапазоне). Буквой m обозначен адрес или целое число больше чем 4095. Дополнительную информацию можно найти в разд.1.3.2.

Символы в столбце "примечания" имеют следующий смысл:
4 - командный формат 4;
D - команда с прямой адресацией;
A - ассемблер выбирает адресацию либо относительно счетчика команд, либо относительно базы;
S - совместима с командным форматом стандартной модели УУМ. Значения операндов могут находиться в диапазоне от 0 до 32767 (подробнее см. в разд.1.3.2).

Бек. Введение в системное программирование. 1988 - Страница 2 88434a10

Приложение Б

Таблица символов кода ASCII

Бек. Введение в системное программирование. 1988 - Страница 2 8843510

Примечание. Коды символов приведены в шестнадцатеричном виде,

ПРИЛОЖЕНИЕ В
СПРАВОЧНЫЙ МАТЕРИАЛ ПО УУМ/ДС

СТРУКТУРА СЛОВА СОСТОЯНИЯ

Разряды. Имя поля. Использование
0. MODE. 0 = режим пользователя; 1 = режим супервизора
1. IDLE. 0 = активен; 1 = пассивен
2-5. ID. Идентификатор процесса
6-7. CC. Код условия
8-11. MASK. Маска прерываний
12-15. Не используются
16-23. ICODE. Код прерывания

ПРЕРЫВАНИЯ

Класс. Тип прерывания. Адрес рабочей области (шестнадцатеричный). Код прерывания
I. SVC. 100. Код команды SVC
II. Программное. 130. Причина (см, ниже)
III. По таймеру. 160. Нет
IV. По вводу-выводу. 190. Номер канала

КОДЫ КОМАНДЫ SVC

Код. Мнемоническое имя. Параметры, передаваемые через регистры
0. WAIT. (A) = адрес ESB для события
1. SIGNAL. (A) = адрес ESB для события
2. I/O. (A) = адрес канальной программы; (S) = номер канала; (T) = адрес ESB для операции обмена
3. REQUEST. (T) = адрес имени ресурса
4. RELEASE. (T) = адрес имени ресурса

КОДЫ ПРОГРАММНЫХ ПРЕРЫВАНИЙ

Код. (шестнадцатеричный) Причина прерывания
00. Запрещенная команда
01. Привилегированная команда в режиме пользователя
02. Адрес вне диапазона
03. Нарушение защиты памяти
04. Арифметическое переполнение
10. Отсутствует страница
11. Отсутствует сегмент
12. Нарушение защиты сегмента
13. Выход за границу сегмента

ФОРМАТ КОМАНД КАНАЛА ВВОДА-ВЫВОДА

Разряды. Содержимое
0-3. Код команды (см. ниже)
4-7. Код устройства
8-23. Количество передаваемых байтов
24-27. Не используются
28-47. Адрес начала массива передаваемых данных

КОДЫ КОМАНД КАНАЛА ВВОДА-ВЫВОДА

Код (шестнадцатеричный). Исполняемая команда
0. Прекратить обмен
1. Чтение данных
2. Запись данных
3-F. Зависит от устройства; для каждого устройства назначается индивидуально

РАБОЧИЕ ОБЛАСТИ КАНАЛА ВВОДА-ВЫВОДА

Разряды. Содержимое
0-2. Адрес текущей канальной программы
3-5. Адрес ESB для текущей операции обмена
6-8. Адрес очереди запросов на обмен с каналом
9-B. Признаки состояния
С-F. Резерв

Рабочая область для канала n начинается с шестнадцатеричного адреса памяти 2n0.

ЛИТЕРАТУРА

Амманн (Ammann U.)
[1981] "The Zurich Implementation", in Pascal - The Language and Its Implementation, edited by D.W.Barron, John Wiley and Sons, New York.

Ахо, Ульман (Aho A., Ullman J.)
[1977] Principles of Compiler Desing, Addison-Wesley Publishing Co., Reading, Mass.

Баас (Baase S.)
[1983] VAX-11 Assembly Language Programming, Prentice-Hall, Inc., Englewood Cliffs, N.J.

Баурн (Bourne S.)
[1983] The UNIX System, Addison-Wesley Publishing Co., Reading, Mass. [Имеется перевод: Баурн С. Операционная система UNIX.- М.: Мир, 1986].

Браун (Brown P.)
[1974] Macro Processors and Techniques for Portable Software, John Wiley and Sons, New York. [Имеется перевод: Браун П. Макропроцессоры и мобильность программного обеспечения.- М.: Мир, 1977]

Гир (Gear С.)
[1981] Computer Organization and Programming, 3rd ed., McGraw-Hill Book Co., New York.

Грис (Gries D.)
[1971] Compiler Construction for Digital Computers, John Wiley and Sons, New York. [Имеется перевод: Грис Д. Конструирование компиляторов для цифровых вычислительных машин.- М.: Мир, 1975]

Гришман (Grishman R.)
[1974] Assembly Language Programming for the Control Data 6000 Series and the CYBER 70 Series, 2nd ed., Algorithmics Press, New York.

Дейт (Date C.)
[1981] An Introduction to Database Systems, 3rd ed., Addison-Wesley Publishing Co., Reading, Mass. [Имеется перевод первого издания: Дейт К. Введение в системы баз данных.- М.: Мир, 1980]

Дейтел (Deitel И.)
[1984] An Introduction to Operating Systems, rev. ed., Addison-Wesley Publishing Co., Reading, Mass. [Имеется перевод: Дейтел Г. Введение в операционные системы.- М.: Мир, 1987]

Деннинг (Denning D.)
[1983] Cryptography and Data Security, Addison-Wesley Publishing Co., Reading, Mass.

Джонсон (Johnson S.)
[1975] "YACC - Yet Another Compiler-Compiler", Computing Science Technical Report 32, Bell Laboratories, Murray Hill, N.J.
[1980] "Language Development Tools on the UNIX System", Computer 13: 16-21, August.

Донован (Donovan J.)
[1972] Systems Programming, McGraw-Hill Book Co., New York. [Имеется перевод: Донован Дж. Системное программирование.- Мл Мир, 1975]

Йенсен, Вирт (Jensen К., Wirth N.)
[1974] PASCAL User Manual and Report, Springer-Verlag, New York. [Имеется перевод: Йенсен К., Вирт Н. Паскаль. Руководство для пользователя и описание языка.- М.: Финансы и статистика, 1982]

Керниган, Плаугер (Kernigan В., Plauger P.)
[1976] Software Tools, Addison-Wesley Publishing Co., Reading, Mass.

Кнут (Knuth D.)
[1973а] The Art of Computer Programming, Vol.1: Fundamental Algorithms, 2nd ed., Addison-Wesley Publishing Co., Reading, Mass. [Имеется перевод: Кнут Д. Искусство программирования для ЭВМ. Т.1: Основные алгоритмы.- М.: Мир, 1976]
[1973б] The Art of Computer Programming, Vol.3: Sorting and Searching, Addison-Wesley Publishing Co., Reading, Mass. [Имеется перевод: Кнут Д. Искусство программирования для ЭВМ. Т.3: Алгоритмы сортировки и поиска.- М.: Мир, 1978]

Коул (Cole A.)
[1981] Macro Processors, Cambridge University Press, New York.

Кэмпбел-Келли (Campbell-Kelley M.)
[1973] An Introduction to Macros, MacDonald. [Имеется перевод: Кэмпбел-Келли M. Введение в макросы.- М.: Советское радио, 1978]

Леск (Lesk M.)
[1975] "Lex - A Lexical Analyzer Generator", Computing Science Technical Report 39, Bell Laboratories, Murray Hill, N.J.

Лорин, Дейтел (Lorin H., Deitel M.)
[1981] Operating Systems, Addison-Wesley Publishing Co., Reading, Mass.

Лумис (Loomis M.)
[1983] Data Management and File Processing, Prentice-Hall, Inc., Englewood Cliffs, N.J.

Льюис, Розенкранц, Стирнз (Lewis P., Rosenkrantz D., Stearns R.)
[1976] Compiler Desing Theory, Addison-Wesley Publishing Co., Reading, Mass. [Имеется перевод: Льюис Ф., Розенкранц Д., Стирнз Р. Теоретические основы проектирования компиляторов.- М.: Мир, 1980]

Мартин (Martin J.)
[1977] Computer Data-Base Organization, 2nd ed., Prentice-Hall, Inc., Englewood Cliffs. [Имеется перевод: Мартин Дж. Организация баз данных в вычислительных системах.- М.: Мир, 1980]

Мейровитц, ван Дам (Meyrowitz N., van Dam A.)
[1982а] "Interactive Editing Systems: Part I", ACM Computing Surveys
14:321-352, September.
[1982б] "Interactive Editing Systems: Part II", ACM Computing Surveys 14: 353-416, September.

Мэдник, Донован (Madnick S., Donovan J.)
[1974] Operating Systems, McGraw-Hill Book Co., New York. [Имеется перевод: Мэдник С., Донован Дж. Операционные системы.- М.: Мир, 1978]

Нори, Амман, Иенсен, Нагели, Якоби (Nori К., Amman U., Jensen К., Nageli H., Jacobi С.)
[1981] "Pascal-P Implementation Notes", in Pascal - The Language and Its Implementation, edited by D.W.Barron, John Wiley and Sons, New York.

Овергаард (Overgaard M.)
[1980] "UCSD Pascal: A Portable Software Environment for Small Computers", Proceedings, 1980 National Computer Conference 49: 747.

Питерсон, Силбершатц (Peterson J., Silberschatz A.)
[1983] Operating Systems Concepts, Addison-Wesley Publishing Co., Reading, Mass.

Пфлигер (Pfleeger C.)
[1982] Machine Organization: An Introduction to the Structure and Programming of Computer Systems, John Wiley & Sons, New York.

Ритчи, Томпсон (Ritchie D., Thompson K.)
[1978] "The UNIX Time-Sharing Systems", Bell System Technical Journal 57, no.6 pt.2: 1905-1930, July-August.

Caсca (Sassa M.)
[1979] "A Pattern Matching Macro Processor", Software: Practice and Experience, pp.439-456, June.

Сейднер, Тиндолл (Seidner R., Tindall N.)
[1983] "Interactive Debug Requirements", Proceedings of the ACM SIGSOFT/SIGPLAN Software Engineering Symposium on High-Level Debugging, March 1983, напечатана в SOFTWARE ENGINEERING Notes and SIGPLAN Notes, August 1983, pp.9-22.

Сирайт, Маккиннон (Seawright L., MacKinnon R.)
[1979] "VM/370 - A Study of Multiplicity and Usefulness", IBM Systems Journal 18, no.1: 4-17.

Стендиш (Standish T.)
[1980] Data Structure Techniquest, Addison-Wesley Publishing Co., Reading, Mass.

Страбл (Struble G.)
[1983] Assembler Language Programming: The IBM System 370, 3rd ed., Addison-Wesley Publishing Co., Reading, Mass.

Танненбаум (Tannenbaum A.)
[1984] Structured Computer Organization, Prentice-Hall. Inc., Englewood
Cliffs, N.J.

Трембли, Соренсон (Tremblay J., Sorenson P.)
[1984] An Introduction to Data Structures with Applications, 2nd ed., McGraw-Hill Book Co., New York.

Уидерхолд (Wiederhold G.)
[1983] Database Design, 2nd ed., McGraw-Hill Book Co., New York.

Ульман (Oilman J.)
[1980] Principles of Database Systems, Computer Science Press, Princeton.

Фернандес, Саммерс, Вуд (Fernandez E., Summers R., Wood C.)
[1981] Database Security and Integrity, Addison-Wesley Publishing Co.,
Reading, Mass.

Хантер (Hunter R.)
[1981] The Design and Construction of Compilers, John Wiley and Sons,
New York.

Холт, Грэхем, Лазовска, Скотт (Holt R., Graham G., Lazowska E., Scott M.)
[1978] Structured Concurent Programming with Operating Systems Applications, Addison-Wesley Publishing Co., Reading, Mass.

Хопгуд (Hopgood F.)
[1969] Compiling Techniques, MacDonald/American Elsevier, London. [Имеется перевод: Хопгуд Ф. Методы компиляции.- М.: Мир, 1972]

CDC (Control Data Corporation)
[1981а] CYBER 170 Computer Systems Hardware Reference Manual.
[1982a] COMPASS Version 3 Reference Manual.
[1982б] CYBER Loader Version 1 Reference Manual.
[1979] NOS International Maintenance Specification.
[1981б] NOS Reference Manual.

DEC (Digital Equipment Corporation)
[1981] VAX Architecture Handbook.
[1978] VAX-11 Linker Reference Manual.
[1979] VAX-11 MACRO Language Reference Manual
[1982] VAX Software Handbook.

IBM (International Business Machines Corporation)
[1983] IBM System!370 Principles of Operation.
[1979] OS/VS DOS/VSE VMl370 Assembler Language.
[1974] OS/VS VMl370 Assembler PLM.
[1982] OS/VS VM/370 Assembler Programmer's Guide.
[1978] OS/VS Linkage Editor and Loader.
[1972a] OS/VS Linkage Editor Logic.
[1972б] FORTRAN IV (H) Compiler Program Logic Manual.

SofTech (SofTech Microsystems)
[1980] UCSD Pascal Users Manual.
[1983] P-Systems Internal Architecture Reference Manual.
Gudleifr
Gudleifr
Admin

Сообщения : 3241
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Страница 2 из 2 Предыдущий  1, 2

Вернуться к началу

- Похожие темы

 
Права доступа к этому форуму:
Вы не можете отвечать на сообщения