Обработка прерываний 1. Общие принципы 2. Рестарт команды 3. Приоритеты одновременно происходящих прерываний 4. Механизмы передачи управления обработчикам прерываний 5. Код ошибки 1. Общие принципы. Прерывания подразделяются на аппаратные (маскируемые и немаскируемые), вызываемые электрическими сигналами на входах процессора, и программные, выполняемые по команде INT xx. Программные прерывания, строго говоря, прерываниями не являются — это лишь своеобразный способ вызова процедур, но процессором они обрабатываются как разновидность прерываний. Аппаратные прерывания подразделяются на маскируемые и немаскируемые. Процессор может воспринимать прерывания после выполнения каждой команды, длинные строковые команды имеют для восприятия прерываний специальные окна. Аппаратные прерывания вызываются электрическими сигналами на входах INTR и NMI. Маскируемые прерывания вызываются переходом в высокий уровень сигнала на входе INTR (Interrupt Request) при установленном флаге разрешения (IF=1). В этом случае процессор сохраняет в стеке регистр флагов, сбрасывает флаг IF и вырабатывает два следующих друг за другом (back to back) цикла подтверждения прерывания, в которых генерируются управляющие сигналы INTA# (Interrupt Acknowledge). Высокий уровень сигнала INTR должен сохраняться по крайней мере до подтверждения прерывания. Первый цикл подтверждения холостой, по второму импульсу внешний контроллер прерываний передает по шине номер вектора, обслуживающего данный тип аппаратного прерывания. Прерывание с полученным номером вектора выполняется процессором так же, как и программное. Обработка текущего прерывания может быть в свою очередь прервана немаскируемым прерыванием, а если обработчик установит флаг IF, то и другим маскируемым аппаратным прерыванием. Немаскируемые прерывания выполняются независимо от состояния флага IF по сигналу NMI (Non Mascable Interrupt). Высокий уровень на этом входе вызовет прерывание с типом (вектором) 2, которое выполняется так же, как и маскируемое. Его обработка не может прерываться под действием сигнала на входе NMI до выполнения команды IRET. В защищенном режиме, помимо перечисленных видов прерываний существуют так называемые исключения (exceptions). Исключение происходит в результате нештатной ситуации выполнения программы, которую возможно необходимо обработать определенным образом. Исключения подразделяются на отказы, ловушки и аварийные завершения. Отказ (fault) — это исключение, которое обнаруживается и обслуживается до выполнения инструкции, вызывающей ошибку. После обслуживания этого исключения управление возвращается снова на ту же инструкцию (включая все префиксы), которая вызвала отказ. Отказы, использующиеся в системе виртуальной памяти, позволяют, например, подкачать с диска в оперативную память, затребованную страницу или сегмент. Ловушка (trap) — это исключение, которое обнаруживается и обслуживается после выполнения инструкции, его вызывающей. После обслуживания этого исключения управление возвращается на инструкцию, следующую за вызвавшей ловушку. К классу ловушек относятся и программные прерывания. Аварийное завершение (abort) - это исключение, которое не позволяет точно установить инструкцию, его вызвавшую. Оно используется для сообщения о серьезной ошибке, такой как аппаратная ошибка или повреждение системных таблиц. Обработка прерываний и исключений в защищенном режиме базируется на таблице дескрипторов прерываний (шлюзов прерываний) IDT – адрес начала и размер которой хранятся в регистре IDTR. Его формат аналогичен формату регистра GDTR (рис. 1). Рис. 1 Формат регистра IDTR Таблица прерываний может содержать до 256 дескрипторов. При попытке обслуживания прерывания с номером, выходящим за размер таблицы, генерируется исключение #DF. Под исключения отданы первые 32 номера (0 ¸31). Не все из этих векторов используются процессором в настоящее время; не назначенные векторы из этого диапазона резервируются для возможного использования в будущем. Использовать не назначенные векторы не следует. Векторы маскируемых прерываний определяются аппаратно. Контроллеры внешних прерываний (например, Intel 8259, программируемый контроллер прерываний) передают вектор на шину процессора во время цикла квитирования прерывания. Изначальным программирование контроллера для реального режима занимается BIOS. Итого в защищенном режиме остается 208 номеров под программные прерывания. Назначения векторов исключений и прерываний показаны в Таблице 1. 1 В реальном режиме — вектор прерывания не попадает в таблицу. 2 В реальном режиме не возникают, но возможны в V86. 3 В реальном режиме — нарушение границы сегмента стека. 4 В реальном режиме — нарушение границы сегмента данных или кода. Таблица 1. Назначения векторов исключений и прерываний 2. Рестарт команды. Рестарт команды используется для обработки исключений, блокирующих доступ к операндам. Например, прикладная программа могла обратиться к данным в сегменте, не присутствующем в памяти. В случае такого исключения обработчик исключения должен загрузить отсутствующий нужный сегмент (возможно, с жесткого диска) и возобновить выполнение, начиная с команды, вызвавшей данное исключение. Во время исключения команда могла изменить содержимое некоторых регистров процессора. При считывании командой значения из стека необходимо восстановить прежнее значение указателя стека. Все эти восстановительные операции выполняются процессором способом, совершенно прозрачным для прикладной программы. 3. Приоритеты одновременно происходящих прерываний. Анализ условий обслуживания прерываний и исключений выполняется в следующем порядке (по убыванию приоритета): 1. Проверка на исключение-ловушку отладки (#DS) по выполненной инструкции (пошаговый режим через флаг TF или точка останова по данным через регистры отладки). 2. Проверка на исключение-отказ отладки (#DB) по последующей инструкции (точка останова по инструкции через регистр отладки). 3. Немаскируемое прерывание (аппаратное по входу NMI). 4. Маскируемое прерывание (аппаратное по входу INTR при IF=1). 5. Проверка на исключение-отказ сегментации (#NP или #GP) при выборке следующей инструкции. 6. Проверка на исключение-отказ страницы (#PF) при выборке следующей инструкции. 7. Проверка на отказ декодирования следующей инструкции (#UD или #GP). 8. Для операции WAIT проверка TS и МР (исключение #NM, если TS=1 и МР=1). 9. Для операции ESCAPE (инструкция математического сопроцессора) проверка ЕМ и TS (исключение #NМ, если EМ=1 или TS=1). 10. Для операции WAIT или ESCAPE проверка на исключение #MF от сопроцессора. 11. Проверка на отказ сегментации (#NР, #SS, #GP) или отказ страницы (#PF) для операндов, используемых в инструкции. Двойной отказ (Double Fault) — исключение #DF — возникает, когда при обработке исключения, связанного с сегментацией (#TS, #NP, #SS или #GP), процессор обнаруживает исключение, отличное от отказа страницы (#PF). Также двойной отказ возникает, если при обработке исключения отказа страницы #PF обнаруживается исключение другого типа. В этом случае тоже исполняется исключение #DF. Если во время обслуживания исключения отказа страницы произойдет еще один отказ страницы, то происходит аварийная остановка (Shutdown) процессора. Во время аварийной остановки никакие новые инструкции не выполняются. Из этого состояния процессор можно вывести только аппаратным сигналом NMI, оставляя его в защищенном режиме, или сигналом RESET, переводящим процессор в реальный режим. 4. Механизмы передачи управления обработчикам прерываний. Дескрипторная таблица прерываний (IDT) содержит шлюзы (рис 2). Рис. 2 Формат шлюза IDT Поле «TYPE» в байте доступа определяет тип шлюза: · 4 - шлюз вызова 80286 (Call Gate); · 5 - шлюз задачи 80286 (Task Gate); · 6 - шлюз прерывания 80286 (Interrupt Gate); · 7 - шлюз ловушки 80286 (Trap Gate); · C - шлюз вызова 386+ (Call Gate); · D - шлюз задачи 386+ (Task Gate); · Е - шлюз прерывания 386+ (Interrupt Gate); · F - шлюз ловушки 386+ (Trap Gate). Шлюз прерывания содержит адрес входа в обработчик прерывания в виде «селектор сегмента» и «смещение». Шлюзы вызова (Call Gates) используются для вызовов процедур со сменой уровня привилегий, шлюзы задач (Task Gates) используются для переключения задач, шлюзы прерываний (Interrupt Gates) и ловушек (Trap Gates) определяют процедуры обслуживания прерываний. Шлюзы вызова позволяют автоматически копировать заданное число слов из старого стека в новый. Шлюзы прерываний отличаются от шлюзов ловушек только тем, что они запрещают прерывания (сбрасывают IF), а шлюзы ловушек — нет. Аналогично тому, как команда CALL вызывает процедуру или задачу, так и исключение или прерывание может "вызвать" обработчик прерывания, представленный в виде процедуры или задачи. Реагируя на исключение или прерывание, процессор использует вектор особого прерывания или прерывания в качестве индекса дескриптора в IDT. Если процессор индексирует шлюз прерывания или шлюз ловушки, то вызов обработчика происходит аналогично вызову при помощи CALL шлюза вызова. Если процессор индексирует шлюз задачи, это приводит к переключению задачи аналогично вызову при помощи CALL шлюза задачи. Прерывания, использующие либо шлюзы прерывания, либо шлюзы ловушки, вызывают очистку флага TF после того, как его текущее значение сохранено в стеке как часть сохраняемого содержимого регистра EFLAGS. Поступая таким образом, процессор предотвращает воздействие трассировки команды на реакцию прерывания. Последующая команда IRET восстанавливает флаг TF в значение, сохраненное в регистре EFLAGS в стеке. Различие между шлюзом прерывания и шлюзом ловушки состоит в их воздействии на флаг IF. Прерывание, использующее шлюз прерывания, очищает флаг IF, предотвращая тем самым влияние на текущий обработчик прерывания прочих возможных прерываний. Последующая команда IF восстанавливает флаг IF в состояние, которое он имел в сохраненном в стеке регистре EFLAGS. Прерывание, проходящее через шлюз ловушки, не изменяет флага IF. 5. Код ошибки. При отработке исключения в защищенном режиме процессор сохраняет в стеке слово кода ошибки (Error Code). Если оно отлично от нуля, то в нем содержится селектор дескриптора, с которым связана ошибка. Код ошибки имеет формат, показанный на Рисунке 3 . Рис. 3 Формат кода ошибки Код ошибки похож на селектор сегмента, однако вместо поля RPL код ошибки содержит два однобитовых поля: 1. Процессор устанавливает бит EXT, если исключение вызвано событием, внешним по отношению к программе. 2. Процессор устанавливает бит IDT, если индексная часть кода ошибки ссылается к дескриптору шлюза в IDT. Если бит IDT не установлен, то бит TI указывает на то, ссылается ли код ошибки к GDT (бит TI очищен), или же к LDT (бит TI установлен). Остальные 13 битов - это старшие биты селектора сегмента. В некоторых случаях код ошибки является пустым (т.е. все биты его младшего слова очищены.) Код ошибки помещается в стек в виде двойного слова. Это делается для выравнивания стека по адресам, кратным четырем. Старшая половина двойного слова резервируется.