-
Notifications
You must be signed in to change notification settings - Fork 2
Лекция 4
Стековый кадр (фрейм) — механизм передачи аргументов и выделения временной памяти с использованием аппаратного стека. Содержит информацию о состоянии подпрограммы.
Включает в себя:
- параметры
- адрес возврата (обязательно)
- локальные переменные
Содержит в себе информацию о состоянии подпрограммы, параметры (если их передала вызывающая сторона).
Команда CALL
обязательно кладёт туда адрес возврата из подпрограммы.
Сама подпрограмма может объявлять там локальные переменные.
Прерывание - особая ситуация, когда выполнение текущей программы приостанавливается и управление передаётся программе-обработчику возникшего прерывания.
- Аппаратные (асинхронные) - события от внешних устройств (основной вид прерываний, так как механизм прерываний в основном нужен для работы с внешними устройствами);
- Внутренние (синхронные) - события в самом процессоре, например, деление на ноль (в современных режимах работы процессоров это также различные исключения: попытка программы выйти за пределы своей памяти, обращение к несуществующему устройству и т.д.);
-
Программные - прерывания, вызванные командой
int
.
OFFTOP:
Механизма прерываний нет в простейших микроконтроллерах (где совсем мало ножек).
Тогда, если мы хотим получать что-то от внешних устройств, то нужно в бесконечном цикле
постоянно анализировать состояние входов и смотреть, поменялся там сигнал или нет.
Если поменялся, то начинаем обрабатывать, иначе ждём дальше.
Для простых устройств с 1-2 внешними устройствами этого достаточно.
А если мы имеем дело с компьютером, то в бесконечном цикле ОС анализировать сигналы
от внешних устройств (которых у компьютера десятки) достаточно накладно (будет уходить
много ресурсов).
Поэтому в процессорах реализовали механизм прерываний, которые работают через специальный
контроллер прерываний. Соответственно, если устройство хочет что-то сообщить процессору,
то оно инициирует сигнал прерывания на какой-то, "грубо говоря", ножке (входе) процессора.
В таком случае процессор приостанавливает выполнение текущей программы, находит
программу-обработчик прерывания по специальной таблице прерываний (см. далее) и передаёт ей управление.
От слова "маска", а не от слова "маскировка".
Внешние прерывания, в зависимости от возможности запрета, делятся на:
-
МаскИруемые (в основном аппаратные) — прерывания, которые можно запрещать установкой соответствующего флага (
IF
- Interruption Flag). Если он сброшен, то обработка прерываний запрещена. Если он установлен, то прерывания будут обрабатываться. Здесь речь идёт об аппаратных прерываниях, потому что программные запрещать нет смысла. - НемаскИруемые (в основном внутренние) (англ. Non-maskable interrupt, NMI) — обрабатываются всегда, независимо от запретов на другие прерывания
Откуда процессор берёт адреса обработчиков прерываний?
Векторы прерываний объединяются в таблицу векторов прерываний, содержащую адреса обработчиков прерываний.
- Располагается в самом начале оперативной памяти, начиная с нулевого физического адреса.
- Доступно 256 прерываний (всего аппаратных, программных и внутренних).
- Каждый вектор занимает 4 байта - полный адрес (2 байта для сегментной части адреса + 2 байта для смещения).
- Размер всей таблицы - 1 Кб.
- Приостановка выполнения текущей программы
-
Сохранение в текущий стек значение регистра флагов и полного адреса возврата (адреса следующей команды) - 6 байт. Это действие аналогично команде
CALL
- Нахождение в таблице векторов прерываний программы-обработчика прерывания
-
Передача управления по адресу обработчика из таблицы векторов, т.е. значения регистров
CS
иIP
обновляются на 4 байта, которые хранятся в таблице векторов прерываний - Возвращение управления текущей программе после окончания обработки прерывания
В зависимости от сложности обработчика он может делать одно или несколько действий. Если обработчик сложный (может вызывать в себе другие подпрограммы), процессору возможно придётся перед передачей управления дополнительно проделать следующие шаги:
- Настройка стека: надо ли ему использовать свой стек? Если да, то обработчику потребуется сохранить по своим локальным адресам или в стек программы старые значения регистров
SS:SP
и других, которые он собирается менять - Повторная входимость (реентерабельность), необходимость запрета прерываний
Пример: обработка прерывания таймера затянулась дольше, чем на 1/20 секунды (по поводу 1/20 неточная информация), и прерывание таймера срабатывает повторно.
Если это не предусмотреть, то всё будет плохо: код начнёт работать сначала, несохранённые данные потеряются. В конечном итоге это может привести к сбою.
Прерывание может обеспечивать реентерабельность, а может и не обеспечивать. Если оно не хочет её обеспечивать, то на время обработки прерывания другие аппаратные прерывания имеет смысл запретить, чтобы обработчик не вызывался повторно и не возникало сбоев.
- Используется для выхода из обработчика прерывания (возвращает из стека 6 байт - 4 для адреса метки дальнего перехода, ещё 2 - регистр флагов)
- Восстанавливает значение регистров
FLAGS
,CS:IP
(т.е. возвращается управление той команде, следующей за той командой, на которой произошло прерывание) - При необходимости выставить значение флага обработчик меняет его значение непосредственно в стеке
INT ~ CALL
IRET ~ RET
В DOS не реализована многозадачность, но она имитируется с помощью установки резидентных программ (реализуются с помощью перехвата прерываний)
Перехват прерывания === установка нового обработчика прерывания, который по срабатыванию будет реализовывать свой функционал.
- Сохранение адреса старого обработчика в таблице векторов прерываний
- Изменение вектора на "свой" адрес
- Вызов старого обработчика до/после отработки своего кода
- При деактивации (ситуация, когда наш обработчик прерывания нужно удалить: например, когда надо выгрузить резидентную программу) - восстановление адреса старого обработчика в таблице векторов прерываний. Тогда наша программа перестанет вызываться, а будет снова вызываться только исходная программа.
Установка обработчика прерывания в DOS
-
int 21h
-
AH=35h
,AL
=номер прерывания - возвращает вES:BX
адрес обработчика, который сейчас установлен (вBX
0000:[AL*4]
- сегментная часть адреса, а вES
-0000:[AL*4+2]
- смещение). Технически, мы сами можем залезть в первый килобайт памяти и посмотреть, что там,но так делать не рекомендуется.int 35h
как раз предусмотрена для запроса адреса обработчика. -
AH=25h
,AL
=номер прерывания,DS:DX
- адрес нового обработчика - замена вектора в таблице векторов прерываний на свой.
-
-
0
- деление на 0 -
1
- прерывание отладчика, вызывается после каждой команды при флагеTF
-
3
- "отладочное",int 3
занимает 1 байт (а во всех остальных случаяхint
занимает 2 байта: машинный код и номер прерывания соответственно). Отладчик может его легко на его место подставлять свой код (формулировка неточная, мб неправильная). -
4
- переполнение при командеINTO
(команда проверки переполнения). Может подставляться в код для проверки переполнения. Команда вызовет это прерывание, еслиOF
== 1. В современных системах в защищённом режиме так уже не делают, а в старых программах реального режима это использовалось в арифметических вычислениях. -
5
- при невыполнении условия в командеBOUND
(команда контроля индексов массива). У этой команды есть параметр: возможное смещение, допустимое при работе с каким-то массивом. Если этот параметр выйдет за границы массива, вызовется это прерывание. Помогает контролировать выход за пределы массива (буфера). -
6
- недопустимая (несуществующая) инструкция -
7
- отсутствует FPU (англ. Floating Point Unit - математический сопроцессор). Это прерывание - рудимент, т.к. во всех современных процессорах есть FPU. -
8
- таймер (срабатывает 18.2 раза в секунду) -
9
- клавиатура (срабатывает при нажатии каждой клавиши)
OFFTOP:
С клавиатуры поступают не ascii-коды символов, а скан-коды клавиш
-
10h
- прерывание BIOS. Доступно сразу при включении компьютера (ещё до загрузки ОС). Функционал прерывания10h
похож на функционал21h
, то есть тоже может выводить что-то на экран, работать с какими-то устройствами, но в более низкоуровневом режиме. Позволяет:- управлять положением курсора на экране,
- переключать цвета символов, не залезая напрямую в видеопамять (а как?!),
- переключать видеорежимы (из текстового в графический и обратно)
- Завершение не через
4Ch
, а через функцию31h
прерывания21h
/ прерывание27h
- DOS не является многозадачной операционной системой
- Резиденты - частичная реализация многозадачности
- Резидентная программа должна быть составлена так, чтобы минимизировать используемую память. Т.е. при завершении программы и сохранении её резидентной DOS'у передаётся информация о том, какую часть программы оставить в памяти, а какую память пометить свободной.
- int
27h
(подходит для более мелких программ и .COM-файлов, т.к. с помощью него мы можем оставить в памяти не более одного сегмента)- DX = адрес первого байта за резидентным участком программы (смещение от
PSP
), т.е. байта, начиная с которого память будет освобождена.
- DX = адрес первого байта за резидентным участком программы (смещение от
-
int 21h
,ah
=31h
(применимо для завершения резидентными любых программ, т.к. с помощью него мы можем сохранять памяти столько, сколько нам нужно)-
AL
- код завершения -
DX
- объём памяти, оставляемой резидентной, в параграфах
-
PSP (Program Segment Prefix) - блок памяти, в котором DOS при загрузке программы в память размещает информацию о том, какие сегменты использует эта программа и сколько места она занимает.
Описание таких блоков находится где-то в недрах DOS'а.
Если мы хотим сохранить в памяти какой-то многозадачный функционал, то мы перехватываем одно или несколько прерываний, чтобы при необходимости получать уведомления (не расслышал слово), и возвращаем управление операционной системе так, чтобы никакие последующие программы при запуске нашу программу не перетирали, т.е. операционная система оставила часть памяти зарезервированной под нашу программу (мы ей указываем, сколько памяти оставить, а сколько освободить). Следовательно, раз мы отбираем у операционной системы часть памяти, мы должны стараться минимизировать используемую память, чтобы отбирать как можно меньше.
Часть программы, которая останется в памяти, должна находиться в самом начале памяти нашей программы, а какая-то инициализирующая часть, которая срабатывает при запуске программы и потом будет освобождаться, должна находиться в конце памяти нашей программы. То есть, резидентную часть программы необходимо размещать в начале нашего кода (например, оформить в качестве подпрограммы), а оставшуюся часть - в конце сегмента кода, чтобы потом её обрезать.
Память, занимаемую резидентной программой, невозможно освободить, т.е. она остаётся в памяти навсегда (до перезагрузки компьютера).
Порты ввода-вывода - механизм взаимодействия программы, выполняемой процессором, с устройствами компьютера.
Устройства могут сами о себе что-то сообщать с помощью прерываний, а если наша программа хочет что-то им сообщить, как-то настроить или какие-то данные из них получить, то для этого нужны команды IN
и OUT
.
Порт - отдельное адресное пространство (которое никак не пересекается ни с оперативной памятью, ни с постоянной), на которое отображаются внешние устройства.
Например, какое-то количество байт будет соответствовать клавиатуре, какое-то количество байт - таймеру, какое-то количество байт - жёсткому диску и т.д.
Номер порта - участок этого адресного пространства (в котором все внешние устройства объединены по отношению к процессору)
- Пример:
IN al, 61h ; читаем один байт состояния из порта ввода-вывода 61h
OR al, 3 ; выставляем младшие 2 бита в единицы
OUT 61h, al ; отправляем полученное значение обратно в этот порт (мб мы как-то повлияли на работу устройства)
Где-то это 1 байт, где-то это 2 байта, где-то это десятки байтов, через которые происходит обмен данными с внешним устройством.