ЭВУ 2 семестр / Презентации ЭВУ в пдф / метода моховикова
.pdfэтого используется другая стратегия. Для переменных резервируется особая область памяти, которая называется стеком, но отдельные переменные не получают в нем абсолютных адресов.
Под стек можно отвести область в любом месте памяти. Размер ее может быть любым, но не должен превосходить 64 Кб, а ее начальный адрес должен быть кратным 16. Другими словами, эта область должна быть сегментом па-
мяти; он называется сегментом стека. Начало этого сегмента (первые 16 бит начального адреса) должно обязательно храниться в сегментном регистре SS.
В стековой памяти ячейки образуют одномерный массив, в котором со-
седние ячейки связаны друг с другом разрядными цепями передачи слов. За-
пись нового слова производится в верхнюю ячейку, при этом все ранее запи-
санные слова сдвигаются вниз, в соседние ячейки. Считывание возможно только из верхней ячейки памяти. Если производится считывание с удалени-
ем, все остальные слова в памяти сдвигаются вверх.
В этой памяти порядок считывания слов соответствует правилу FIFO:
последним поступил, первым обслуживается. В ряде устройств рассматри-
ваемого типа предусматривается также операция простого считывания слова из нулевой ячейки без его удаления и сдвига слов в памяти.
Иногда стековая память снабжается счетчиком стека, показывающим количество занесенных в память слов.
В вычислительных машинах часто стековую память организуют, ис-
пользуя адресную память и специальный регистр – указатель стека.
Стек (англ. stack – стопка) – структура данных с методом доступа к эле-
ментам LIFO (англ. Last In – First Out, «последним пришел – первым вы-
шел»). Стек представляет собой непрерывную область памяти, адресуемую регистрами ESP (указатель стека) и SS (селектор сегмента стека). Данные помещаются в стек с помощью инструкции PUSH (заталкивание), а извлека-
71
ются по инструкции POP (вытаскивание). За одну операцию можно помес-
тить или извлечь только слово (2 байт) или двойное слово (4 байт). Указатель стека ESP (или SP) показывает на верхушку стека – данные, которые будут извлечены по инструкции POP. При помещении данных первым делом указа-
тель стека декрементируется на 2 или 4, в зависимости от разрядности дан-
ных; после этого данные помещаются в сегмент, определенный регистром
SS, со смещением, определяемым значением ESP. При извлечении данные считываются из памяти по адресу SS:ESP, после чего указатель стека инкре-
ментируется на 2 или 4. Таким образом, при помещении данных в стек об-
ласть, занимаемая стеком в стековом сегменте, как бы растет вниз. При из-
влечении данных стек сжимается вверх.
Кроме явного доступа к стеку посредством инструкций PUSH и POP
стек автоматически используется процессором при выполнении инструкций вызова (CALL), возвратов (RET и IRET), входа и выхода из процедур (ENTER и LEAVE), а также при обработке прерываний. По инструкции CALL в
стек помещаются адрес возврата – значение регистров CS и EIP, указы-
вающие на инструкцию, следующую после инструкции вызова (при ближних вызовах сегментный регистр в стеке не сохраняется). По прерыванию в стек помещается значение регистра EFLAGS, а затем адрес инструкции, следую-
щей за той, на которой произошло прерывание. По инструкциям возврата эти значения извлекаются из стека в соответствующие регистры, и процессор продолжает выполнять прерванную последовательность инструкций. Исклю-
чение работает аналогично прерыванию, но следом за содержимым EIP в ря-
де случаев в стек помещается также слово кода ошибки, которое должно быть извлечено обработчиком исключения.
Стек используют для различных целей:
•организации прерываний, вызовов и возвратов;
•временного хранения данных, когда нет смысла выделять для них фиксированное место в памяти;
•передачи возвратов параметров при вызовах процедур.
72
Для передачи параметров процедур служит регистр EBP (BP), который тоже использует сегментный регистр SS. Чтобы передать параметры проце-
дуре, вызывающая программа помещает их в стек в определенном порядке и выполняет вызов. Вызванная процедура может использовать стек и для своих локальных целей (в том числе и вызова вложенных процедур). Чтобы иметь доступ к переданным ей данным в стеке, вызываемая процедура первым де-
лом может скопировать содержимое ESP в EBP, после чего к переданным ей параметрам (и возвращаемым в вызывающую программу) может адресовать-
ся относительно регистра EBP. Перед возвратом процедура может обратно скопировать EBP в ESP, в результате в верхушке стека гарантированно ока-
жется адрес точки возврата вызывающей программы. Для выделения и осво-
бождения в стеке областей под переменные служат инструкции ENTER и LEAVE, используемые языками высокого уровня при вызовах процедур с пе-
редачей параметров.
Разрядность элементов, которыми по умолчанию работают инструкции
PUSH и POP, определяется битом D в дескрипторе кодового сегмента теку-
щей задачи: 0–16 бит (инкремент/декремент ESP на 2), 1–32 бита (инкре-
мент/декремент ESP на 4). Разрядность может быть изменена на противопо-
ложную для конкретной инструкции префиксом SIZ. Когда в 32-битном стеке сохраняют или извлекают содержимое сегментных регистров, процессор ав-
томатически их расширяет до 32 бит нулями. Для остальных 16-битных опе-
рандов такая автоматическая коррекция не выполняется.
Разрядность указателя стека определяется битом B в текущем дескрип-
торе сегмента стека: 0 – используется SP, максимальный размер стека 64
Кбайт; 1 – ESP, максимальный размер стека 4 Гбайт. Для реального режима работы процессора, а также для режима V86 используется 16-битный указа-
тель SP и разрядность стековых операций – 16 бит.
До использования стека он должен быть инициализирован – значения селектора SS и указателя ESP устанавливаются так, чтобы они указывали на область реально существующей оперативной памяти (стек в ПЗУ, естествен-
73
но, работать не может). При работе с 16-битными элементами указатель стека должен быть выровнен по границе слова, с 32-битными – по границе двойно-
го слова. В противном случае гарантировано заметное снижение производи-
тельности процессора, а возможна и неработоспособность программ. При-
кладные программы, как правило, от операционной системы получают гото-
вый к употреблению стек. При выполнении операций, использующих стек,
возможно появление исключения #SS (нарушение границы или отсутствие сегмента стека). В защищенном режиме сегмент состояния задачи содержит четыре селектора сегментов стека (для разных уровней привилегий), но в ка-
ждый момент используется, естественно, только один стек (регистры SS и ESP в процессоре предоставлены только в одном экземпляре, они определя-
ют стек на текущем уровне привилегий). Если при вызове процедуры (пре-
рывании, исключении) происходит переключение стекового сегмента, значе-
ния регистров EFLAGS, CS, EIP и код ошибки (если используется) сначала временно сохраняются внутри процессора, а после загрузки нового сегмента стека сохраняются уже в нем.
7.1. Форма ПОЛИЗ (обратная польская запись)
Обратная польская нотация (обратная польская запись, обратная бесско-
бочная запись (ОБЗ), постфиксная нотация, бесскобочная символика Лука-
шевича, польская инверсная запись, ПОЛИЗ) – форма записи математических выражений, в которой операнды расположены перед знаками операций.
В общем виде запись выглядит следующим образом:
Запись набора операций состоит из последовательности опе-
рандов и знаков операций. Операнды в выражении при письменной за-
писи разделяются пробелами.
Выражение читается слева направо. Когда в выражении встречается знак операции, выполняется соответствующая операция над двумя последними встретившимися перед ним операндами в по-
рядке их записи. Результат операции заменяет в выражении последова-
74
тельность ее операндов и ее знак, после чего выражение вычисляется
дальше по тому же правилу.
Результатом вычисления выражения становится результат по-
следней вычисленной операции.
Например, рассмотрим вычисление выражения 7 2 3 * - (эквивалентное выражение в инфиксной нотации: 7-2*3).
1. Первый по порядку знак операции – «*», поэтому первой выполня-
ется операция умножения над операндами 2 и 3 (они стоят последними перед знаком). Выражение при этом преобразуется к виду 7 6 - (результат умноже-
ния – 6 – заменяет тройку «2 3 *»).
2.Второй знак операции – «-». Выполняется операция вычитания над операндами 7 и 6.
3.Вычисление закончено. Результат последней операции равен 1, это и есть результат вычисления выражения. Наблюдается очевидное расширение обратной польской записи на унарные, тернарные и операции с любым дру-
гим количеством операндов: при использовании знаков таких операций в вы-
числении выражения операция применяется к соответствующему числу по-
следних встретившихся операндов.
Построение польской записи
Базовый алгоритм построения польской (постфиксной) нотации: про-
сматриваем инфиксную запись слева направо. Если встречаем операнд, то копируем в постфиксную запись. Если встречаем знак операции, то если стек пуст, помещаем знак операции в стек. Иначе, если приоритет последней опе-
рации в стеке меньше приоритета текущей операции, то помещаем знак опе-
рации в стек. В противном случае, записываем знаки операций из стека в постфиксную запись до тех пор, пока приоритет последней операции в стеке не будет больше, либо равен приоритету текущей операции. Помещаем знак текущей операции в стек.
Записываем знаки операций из стека в постфиксную запись до тех пор,
пока стек не опустеет.
75
Особенности обратной польской записи:
Порядок выполнения операций однозначно задается поряд-
ком следования знаков операций в выражении, поэтому отпадает необ-
ходимость использования скобок и введения приоритетов и ассоциа-
тивности операций.
В отличие от инфиксной записи, невозможно использовать одни и те же знаки для записи унарных и бинарных операций. Так, в
инфиксной записи выражение 5 * (-3 + 8) использует знак «минус» как символ унарной операции (изменение знака числа), а выражение (10 -
15) * 3 применяет этот же знак для обозначения бинарной операции
(вычитание). Конкретная операция определяется тем, в какой позиции находится знак. Обратная польская запись не позволяет этого: запись 5
3 - 8 + * (условный аналог первого выражения) будет интерпретирова-
на как ошибочная, поскольку невозможно определить, что «минус» по-
сле 5 и 3 обозначает не вычитание; в результате будет сделана попытка вычислить сначала 5 - 3, затем 2 + 8, после чего выяснится, что для операции умножения не хватает операндов. Чтобы все же записать это выражение, придется либо переформулировать его, либо ввести для операции изменения знака отдельное обозначение, например, «±»: 5 3 ±
8 + *.
Так же, как и в инфиксной нотации, в ОПН одно и то же вы-
числение может быть записано в нескольких разных вариантах. На-
пример, выражение (10 – 15) * 3 в ОПН можно записать как 10 15 – 3 *,
а можно – как 3 10 15 – *.
Из-за отсутствия скобок обратная польская запись короче ин-
фиксной. За этот счет при вычислениях на калькуляторах повышается скорость работы оператора (уменьшается количество нажимаемых кла-
виш), а в программируемых устройствах сокращается объем тех частей программы, которые описывают вычисления. Последнее может быть
76
немаловажно для портативных и встроенных вычислительных уст-
ройств, имеющих жесткие ограничения на объем памяти.
Некоторые примеры инфиксных выражений и их эквиваленты в обрат-
ной польской записи приведены в табл. 2.
Таблица 2
Примеры инфиксной и постфиксной записей для различных выражений
|
Инфиксная запись |
Обратная польская запись |
|
|
|
|
А+В*С |
АВС*+ |
|
|
|
|
А*В+С |
АВ*С+ |
|
|
|
|
A*B+C*D |
АВ*СDx+ |
|
|
|
|
(A+B)/(C-D) |
АВ+СD-/ |
|
|
|
|
А*В/С |
АВ*С/ |
|
|
|
|
((А+В) *C+D)/(E+F+G) |
A B+C*D+EF+G+/ |
|
|
|
7.2. Вычисления на стеке. Стековая машина
Стековой машиной называется алгоритм, проводящий вычисления по обратной польской записи.
Алгоритм вычисления для стековой машины:
1. Обработка входного символа. Если на вход подан операнд, он поме-
щается на вершину стека. Если на вход подан знак операции, то соответст-
вующая операция выполняется над требуемым количеством значений, извле-
ченных из стека, взятых в порядке добавления.
2.Результат выполненной операции кладется на вершину стека. Если входной набор символов обработан не полностью, перейти к шагу 1.
3.После полной обработки входного набора символов результат вычис-
ления выражения лежит на вершине стека.
Например, вычисление выражения ((1 + 2) * 4) + 3 в ОПН может быть записано так: 1 2 + 4 * 3 +. Вычисление на стеке производится следующим образом (указано состояние стека после выполнения операции) (табл. 3).
77
Таблица 3
Вычисление на стеке
|
Ввод |
Операция |
Стек |
|
|
|
|
|
|
|
1 |
поместить в стек |
1 |
|
|
|
|
|
|
|
2 |
поместить в стек |
1, 2 |
|
|
|
|
|
|
|
+ |
сложение |
3 |
|
|
|
|
|
|
|
4 |
поместить в стек |
3, 4 |
|
|
|
|
|
|
|
* |
умножение |
12 |
|
|
|
|
|
|
|
3 |
поместить в стек |
12, 3 |
|
|
|
|
|
|
|
+ |
сложение |
15 |
|
|
|
|
|
|
Результат (15) в конце вычислений находится на вершине стека.
7.3.Использование стека процессором при обработке прерываний
При обработке прерываний процессор использует стек следующим об-
разом: генерирует исключение, операционная система находит нужный обра-
ботчик и вызывает его. При этом, выполняя поток команд, процессор прове-
ряет возможность выполнения каждой инструкции, корректность ее аргумен-
тов, остальные факторы, влияющие на корректность выполнения кода. В
случаях, если команда не может быть выполнена (деление на ноль, обраще-
ние к несуществующей странице, несоответствие уровня привилегий или что-нибудь др.), процессор генерирует исключение – вызывает один из обра-
ботчиков, зарегистрированных операционной системой в IDT (Interrupt
Dispatch Table).
При вызове обработчика процессор делает сразу несколько вещей: пере-
ключается в режим ядра (Ring 0), переключает указатель стека на ядерный стек и сохраняет предыдущие указатели команд и стека в ядерном стеке. По-
лучив контроль, обработчик исключения сохраняет остальные регистры про-
78
цессора в стеке и выполняет действия, специфичные для конкретного исклю-
чения.
Например, обработчик Page Fault Exception запрашивает подкачку страни-
цы у Memory Manager. Если обработчику удалось разрешить проблему, вы-
звавшую генерацию исключения, он восстанавливает сохраненное состояние процессора и выполняет возврат в пользовательский код (Ring 3). В против-
ном случае в дело вступает диспетчер исключений.
Диспетчер исключений размещает структуру CONTEXT в пользователь-
ском стеке и копирует туда сохраненное состояние регистров из ядерного стека. Туда же сохраняется текущее состояние Floating Point регистров. Ин-
формация об исключении записывается в структуру EXCEPTION_RECORD.
Далее диспетчер подменяет адрес возврата в пользовательский код адре-
сом диспетчера исключений пользовательского режима и выполняет возврат в Ring 3 (рис. 29).
Рис. 29. Использование стека при обработке прерываний
Windows поддерживает специальную структуру, TEB (Thread Environment Block), где хранятся локальные данные потока. Эта структура доступна из Ring 3 через сегмент FS (или GS для x64). В самом начале этой структуры хранится указатель на список вложенных блоков ―__try‖ и соот-
79
ветствующих им блоков ―__except‖ и ―__finally‖. Компилятор генерирует код, добавляющий элемент в этот список при входе в блок ―__try‖ и удаляю-
щий при выходе из блока2. Получив управление, диспетчер исключений пользовательского режима по очереди опрашивает обработчиков из списка,
позволяя каждому из них обработать данное исключение. Найдя обработчик,
согласившийся обработать исключение, диспетчер исключений выполняет
«раскрутку» стека (Stack Unwinding) и передает управление выбранному об-
работчику. Этот механизм довольно детально описывался во всевозможных статьях о том, как работает SEH (Structured Exception Handling) , поэтому мы не будем останавливаться на этом детально. «Раскрутчик» стека отслеживает текущее состояние (указатель стека и команд, состояние регистров) в ло-
кальной копии структуры CONTEXT. По окончании обработки исключения состояние из структуры CONTEXT загружается в процессор, завершая обра-
ботку исключения.
7.4. Использование стека процессором при вызове
Перед вызовом процедуры параметры необходимо поместить в стек с помощью команды PUSH. Здесь существует два варианта: параметры могут помещаться в стек в прямом или в обратном порядке. Обычно используется обратный порядок, и его мы будем использовать в примерах. Параметры по-
мещаются в стек, начиная с последнего, так что перед вызовом процедуры на вершине стека оказывается первый параметр. Перед выполнением команды
CALL стек будет иметь вид, как показано на рис. 30.
2 Справедливо только для x86. 64-битный код «раскручивает» стек, пользуясь сгенерированным компилятором описанием кода.
80
