Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

СПО

.pdf
Скачиваний:
40
Добавлен:
31.05.2015
Размер:
2.9 Mб
Скачать

δ(q, xa) = δ(δ(q, x), a) для любого xΣ* и aΣ.

Таким образом, запись δ(q, x) = p означает, что fa M, начиная в состоянии q Q чтение цепочки xΣ*, записанной на входной ленте, оказывается в состоянии p Q, когда его входная головка продвинется правее цепочки x.

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

Определенная таким образом модель конечного автомата называется детерминированной. Для его обозначения часто используют аббревиатуру dfa.

Определение. Цепочка xΣ* принимается конечным автоматом M, если

δ(q0, x) = p для некоторого p F.

Множество всех цепочек xΣ*, принимаемых конечным автоматом M, называется языком, распознаваемым конечным автоматом M, и обозначается как T(M), т. е.

T(M) = { xΣ* δ(q0, x) = p при некотором p F }.

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

Пример 1. Рассмотрим диаграмму состояний конечного автомата (рис. 35). Пусть задан конечный автомат

M = (Q, Σ, δ, q0, F), где

Q = {q0, q1, q2, q3},

Σ = {0, 1},

F = {q0}, δ(q0, 0) = q2, δ(q0, 1) = q1, δ(q1, 0) = q3, δ(q1, 1) = q0, δ(q2, 0) = q0, δ(q2, 1) = q3, δ(q3, 0) = q1, δ(q3, 1) = q2.

Рис. 35. Диаграмма состояний конечного автомата

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

Так, если δ(q, a) = p, то из узла, представляющего состояние q, в узел, представляющий состояние p, проводится дуга, помеченная входным символом a.

На рис. 35 дана диаграмма состояний конечного автомата M. Двойным кружком выделено единственное в данном примере конечное состояние, которое является одновременно и начальным.

Предположим, что на входе автомата находится цепочка 110101. Поскольку

δ(q0, 1) = q1, а δ(q1, 1) = q0 и q0 F,

то цепочка 11 находится в языке, распознаваемом данным конечным автоматом, т.е. в T(M), но мы интересуемся всей входной цепочкой. Сканируя остаток 0101 входной цепочки, автомат переходит последовательно в состояния q2, q3, q1, q0. Поэтому δ(q0, 110101) = q0 и потому цепочка 110101 тоже находится в T(M).

Легко показать, что T(M) есть множество всех цепочек из

{0, 1}*, содержащих четное число нулей и четное число единиц. В частности, T(M).

8.6.Макроязыки и макрогенерация

8.6.1.Определения макрокоманд и макрогенерации

Разработка программ на языке ассемблера - достаточно трудоемкий процесс, для облегчения труда разработчика были созданы так называемые макрокоманды.

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

ем.

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

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

Макроопределение может содержать параметры. Тогда

macro
хог ах, ах push ax endm

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

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

8.6.2. Примеры макрокоманд

Синтаксис макрокоманд и макроопределений зависит от реализации макропроцессора. Но сам принцип выполнения макроподстановок в тексте программы неизменен и не зависит от их синтаксиса.

Пример 1. Следующий текст макроопределения определяет макрокоманду push_0 в языке ассемблера процессора ти-

па Intel 8086: push_0

Семантика этой макрокоманды заключается в записи числа «0» в стек через регистр процессора ах. Тогда везде в тексте программы, где встретится макрокоманда

push_0

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

хог ах, ах push ax

Это самый простой вариант макроопределения.

Пример 2. Существует возможность создавать более сложные макроопределения с параметрами. Одно из таких макроопределений описано ниже:

add_abx macro

xl, x2

add

ax, xl

add

bx, xl

add

cx,x2

push

ax

endm

 

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

add_abx 4, 8

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

add

ax, 4

add

bx, 4

add

ex, 8

push

ax

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

loop_ax macro xl, x2, yl

local

loopax

mov

ax, xl

xor

bx, bx

loopax: add

bx, yl

sub

ax, x2

jge

loopax

endm

 

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

8.6.3. Макроязыки и препроцессоры

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

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

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

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

против, могут встречаться в произвольном месте исходно-

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

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

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

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

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

ввиде конечного автомата. Более сложные препроцессоры

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

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

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

Рассмотрим пример на языке С. Если описана функция int f1 (int a) { return a + а; }

и аналогичная ей макрокоманда

#define f2(a) ((a) + (а))

то результаты их вызова не всегда будут одинаковы. Действительно, вызовы j = f1(i) и j = f2(i) (где i и j - неко-

торые целочисленные переменные) приведут к одному и тому же результату. Но вызовы j = f1(++i) и j = f2(++i) дадут разные значения переменной j. Дело в том, что поскольку f2 - это макроопределение, то во втором случае будет выполнена текстовая подстановка, которая приведет к последовательности операторов j = ( (++i) + (++i)). Видно, что в этой последовательно-

сти операция ++i будет выполнена дважды, в отличие от вызова функции f1(++i), где она выполняется только один раз.

ЗАКЛЮЧЕНИЕ

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

Освоение базовых принципов построения операционных систем и управления их ресурсами позволит разрабатывать сравнительно сложные системные программные приложения.

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

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

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

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