Skip to content

Лекция 4

Andrey Sapozhkov edited this page Jun 25, 2022 · 9 revisions

Использование стека подпрограммами.

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

Включает в себя:

  • параметры
  • адрес возврата (обязательно)
  • локальные переменные

Содержит в себе информацию о состоянии подпрограммы, параметры (если их передала вызывающая сторона).
Команда CALL обязательно кладёт туда адрес возврата из подпрограммы.
Сама подпрограмма может объявлять там локальные переменные.

Прерывания.

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

Виды прерываний:

  • Аппаратные (асинхронные) - события от внешних устройств (основной вид прерываний, так как механизм прерываний в основном нужен для работы с внешними устройствами);
  • Внутренние (синхронные) - события в самом процессоре, например, деление на ноль (в современных режимах работы процессоров это также различные исключения: попытка программы выйти за пределы своей памяти, обращение к несуществующему устройству и т.д.);
  • Программные - прерывания, вызванные командой int.
OFFTOP:
Механизма прерываний нет в простейших микроконтроллерах (где совсем мало ножек).
Тогда, если мы хотим получать что-то от внешних устройств, то нужно в бесконечном цикле
постоянно анализировать состояние входов и смотреть, поменялся там сигнал или нет.
Если поменялся, то начинаем обрабатывать, иначе ждём дальше.

Для простых устройств с 1-2 внешними устройствами этого достаточно.
А если мы имеем дело с компьютером, то в бесконечном цикле ОС анализировать сигналы
от внешних устройств (которых у компьютера десятки) достаточно накладно (будет уходить
много ресурсов). 

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

МаскИрование прерываний.

От слова "маска", а не от слова "маскировка".
Внешние прерывания, в зависимости от возможности запрета, делятся на:

  • МаскИруемые (в основном аппаратные) — прерывания, которые можно запрещать установкой соответствующего флага (IF - Interruption Flag). Если он сброшен, то обработка прерываний запрещена. Если он установлен, то прерывания будут обрабатываться. Здесь речь идёт об аппаратных прерываниях, потому что программные запрещать нет смысла.
  • НемаскИруемые (в основном внутренние) (англ. Non-maskable interrupt, NMI) — обрабатываются всегда, независимо от запретов на другие прерывания

Таблица векторов прерываний в реальном режиме работы процессора.

Откуда процессор берёт адреса обработчиков прерываний?

Вектор прерывания — номер, который идентифицирует соответствующий обработчик прерываний.

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

Свойства:

  • Располагается в самом начале оперативной памяти, начиная с нулевого физического адреса.
  • Доступно 256 прерываний (всего аппаратных, программных и внутренних).
  • Каждый вектор занимает 4 байта - полный адрес (2 байта для сегментной части адреса + 2 байта для смещения).
  • Размер всей таблицы - 1 Кб.

Срабатывание прерывания.

Что делает процессор при срабатывании прерывания (любого):

  1. Приостановка выполнения текущей программы
  2. Сохранение в текущий стек значение регистра флагов и полного адреса возврата (адреса следующей команды) - 6 байт. Это действие аналогично команде CALL
  3. Нахождение в таблице векторов прерываний программы-обработчика прерывания
  4. Передача управления по адресу обработчика из таблицы векторов, т.е. значения регистров CS и IP обновляются на 4 байта, которые хранятся в таблице векторов прерываний
  5. Возвращение управления текущей программе после окончания обработки прерывания

Прерывание по механизму обработки идентично обработке подпрограммы.

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

  1. Настройка стека: надо ли ему использовать свой стек? Если да, то обработчику потребуется сохранить по своим локальным адресам или в стек программы старые значения регистров SS:SP и других, которые он собирается менять
  2. Повторная входимость (реентерабельность), необходимость запрета прерываний

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

Пример: обработка прерывания таймера затянулась дольше, чем на 1/20 секунды (по поводу 1/20 неточная информация), и прерывание таймера срабатывает повторно.
Если это не предусмотреть, то всё будет плохо: код начнёт работать сначала, несохранённые данные потеряются. В конечном итоге это может привести к сбою.

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

IRET - возврат из прерывания.

  • Используется для выхода из обработчика прерывания (возвращает из стека 6 байт - 4 для адреса метки дальнего перехода, ещё 2 - регистр флагов)
  • Восстанавливает значение регистров FLAGS, CS:IP (т.е. возвращается управление той команде, следующей за той командой, на которой произошло прерывание)
  • При необходимости выставить значение флага обработчик меняет его значение непосредственно в стеке
INT  ~ CALL
IRET ~ RET

Перехват прерывания.

В DOS не реализована многозадачность, но она имитируется с помощью установки резидентных программ (реализуются с помощью перехвата прерываний)

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

Процесс перехвата прерывания:

  1. Сохранение адреса старого обработчика в таблице векторов прерываний
  2. Изменение вектора на "свой" адрес
  3. Вызов старого обработчика до/после отработки своего кода
  4. При деактивации (ситуация, когда наш обработчик прерывания нужно удалить: например, когда надо выгрузить резидентную программу) - восстановление адреса старого обработчика в таблице векторов прерываний. Тогда наша программа перестанет вызываться, а будет снова вызываться только исходная программа.

Установка обработчика прерывания в 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, то есть тоже может выводить что-то на экран, работать с какими-то устройствами, но в более низкоуровневом режиме. Позволяет:
    • управлять положением курсора на экране,
    • переключать цвета символов, не залезая напрямую в видеопамять (а как?!),
    • переключать видеорежимы (из текстового в графический и обратно)

Прерывание 21h может работать с файлами (файловой системой на диске), а 10h - нет.

Резидентные программы.

Резидентная программа - та, которая остаётся в памяти после возврата управления DOS'у.

Свойства:

  • Завершение не через 4Ch, а через функцию 31h прерывания 21h / прерывание 27h
  • DOS не является многозадачной операционной системой
  • Резиденты - частичная реализация многозадачности
  • Резидентная программа должна быть составлена так, чтобы минимизировать используемую память. Т.е. при завершении программы и сохранении её резидентной DOS'у передаётся информация о том, какую часть программы оставить в памяти, а какую память пометить свободной.

Завершение с сохранением в памяти.

  • int 27h (подходит для более мелких программ и .COM-файлов, т.к. с помощью него мы можем оставить в памяти не более одного сегмента)
    • DX = адрес первого байта за резидентным участком программы (смещение от PSP), т.е. байта, начиная с которого память будет освобождена.
  • int 21h, ah=31h (применимо для завершения резидентными любых программ, т.к. с помощью него мы можем сохранять памяти столько, сколько нам нужно)
    • AL - код завершения
    • DX - объём памяти, оставляемой резидентной, в параграфах

PSP (Program Segment Prefix) - блок памяти, в котором DOS при загрузке программы в память размещает информацию о том, какие сегменты использует эта программа и сколько места она занимает.

Описание таких блоков находится где-то в недрах DOS'а.

Зачем это всё нужно:

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

Как минимизировать используемую память:

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

Память, занимаемую резидентной программой, невозможно освободить, т.е. она остаётся в памяти навсегда (до перезагрузки компьютера).

Порты ввода-вывода.

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

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

Порт - отдельное адресное пространство (которое никак не пересекается ни с оперативной памятью, ни с постоянной), на которое отображаются внешние устройства.

Например, какое-то количество байт будет соответствовать клавиатуре, какое-то количество байт - таймеру, какое-то количество байт - жёсткому диску и т.д.

Номер порта - участок этого адресного пространства (в котором все внешние устройства объединены по отношению к процессору)

IN - команда чтения данных из порта ввода.

OUT - команда записи в порт вывода.

  • Пример:
IN al, 61h    ; читаем один байт состояния из порта ввода-вывода 61h
OR al, 3      ; выставляем младшие 2 бита в единицы
OUT 61h, al   ; отправляем полученное значение обратно в этот порт (мб мы как-то повлияли на работу устройства)

С разными устройствами связаны различные диапазоны портов:

Где-то это 1 байт, где-то это 2 байта, где-то это десятки байтов, через которые происходит обмен данными с внешним устройством.

Clone this wiki locally