Расширенная классификация прерываний в защищенном режиме
В защищенном режиме термином «прерывание» принято обозначать только аппаратные прерывания, в то время как для программных прерываний принято использовать термин «особые случаи» или «исключения» (exception).
В зависимости от способа возникновения особых случаев и возможности перезапуска (рестарта) CPU после их обработки с вызвавшей их команды принято различать три вида особых случаев. Причем это деление производится в соответствии со следующими признаками:
- какая информация сохраняется о месте возникновения прерывания (исключения)?
- возможно ли возобновление прерванной программы?
Итак, в защищенном режиме различают три вида особых случаев:
1. Нарушение (сбой, fault) – это особые случаи, которые выявляются и обслуживаются либо перед выполнением, либо во время выполнения «виновной» команды. При обнаружении нарушения, сохраняемые в стек значения CS и EIP указывают на команду, вызвавшую это нарушение, для возможности осуществить рестарт программы после устранения нарушения, связанного с «виновной» командой. Вспомните, что в реальном режиме при возникновении прерывания в стеке всегда запоминается адрес команды, следующей за той, которая вызвала это прерывание. Типичными примерами нарушений (отказов) могут служить не присутствие сегмента или страницы.
2. Ловушка (trap) – это особый случай, который возникает непосредственно после команды, вызвавшей этот особый случай. Значения регистров CS и EIP, сохраняемые в стеке при обрабатывании ловушки, указывают на команду, следующую по отношению к команде, вызвавшей это срабатывание. Типичными примерами ловушек могут служить: ловушка пошагового исполнения программы (ее генератором является установленный флаг IF), команды-генераторы прерываний (с мнемоникой INT).
Так же, как и в случае ошибок возможен рестарт программы. Для этого необходимо лишь исправить в обработчике прерывания соответствующие код или данные, послужившие источником ошибки. После этого перед возвратом управления нужно скорректировать значение IP в стеке на длину команды, вызвавшей данное прерывание.
Механизм ловушек похож на механизм прерываний в реальном режиме, хотя не во всем. Здесь есть один тонкий момент. Если прерывание типа ловушки возникло в команде передачи управления JMP, то содержимое пары CS: IP в стеке будет отражать результат этого перехода, то есть соответствовать команде назначения.
3. Авария (abort) (выход из процесса, аварийное завершение процесса) – является особым случаем, который не позволяет точно локализовать вызвавшую его команду и осуществить рестарт программы. Аварии используются для сообщений о крупных ошибках, таких как сбои аппаратуры или ошибки в системных таблицах.
Аварийное завершение — прерывание, при котором информация о месте его возникновения недоступна или неполна и поэтому рестарт практически невозможен, если только данная ситуация не была запланирована заранее.
Соответствующие программы-обработчики ошибок, ловушек и аварий будут отличаться алгоритмами работы. Микропроцессор жестко определяет, какие прерывания являются ошибками, ловушками и авариями.
Замечания. Для некоторых номеров прерываний в реальном режиме возможны 2 источника прерывания (развитие микропроцессоров).
Некоторые прерывания при своем возникновении дополнительно генерируют и записывают в стек так называемый код ошибки. Этот код может впоследствии использоваться для установления источника прерывания. Код ошибки записывается в стек вслед за содержимым регистров EFLAGS, CS и EIP. По коду ошибки можно распознать истинный источник прерывания (если есть неоднозначность).
3.6. Дескрипторная таблица прерываний и ее элементы
В отличие от таблицы векторов прерываний, местоположение которой в памяти является строго регламентированным (она находится в младших адресах), IDT, в принципе, может размещаться в любом месте линейного адресного пространства, однако, как правило, для сохранения преемственности ее также располагают в младших адресах. Локализация IDT в линейном адресном пространстве и ее допустимый размер задаются содержимым системного регистра IDTR (этот регистр, так же как и GDTR является 48-разрядным: 32 бита – Base, 16 бит – Limit). Максимальный размер IDT должен быть рассчитан на 256 типов прерываний и составляет 2 Ki байта. В свою очередь минимальный размер IDT должен быть рассчитан по крайней мере на 32 зарезервированных типов прерываний.
Особенности таблицы IDT:
1) нулевой дескриптор используется для описания шлюза для программы обработки исключительной ситуации 0 (ошибка деления), (в таблице GDT отсутствует);
2) дескрипторы в таблице IDT строго упорядочены в соответствии с номерами прерываний. В таблицах GDT и LDT порядок описания дескрипторов роли не играет, хотя и допускается наличие некоторых соглашений по их упорядоченности;
3) размерность таблицы IDT – не более 256 элементов размером по восемь байт, по числу возможных источников прерываний.
В отдельных случаях есть смысл описывать все 256 дескрипторов этой таблицы, формируя для неиспользуемых номеров прерываний шлюзы-заглушки. Это позволит корректно обрабатывать все прерывания, даже если они и не планируются к использованию в данной задаче. Если этого не сделать, то при незапланированном прерывании с номером, превышающим пределы IDT для данной задачи, будет возникать исключительная ситуация общей защиты (с номером 13 (0Dh)).
Адрес и длина таблицы IDT содержатся в регистре IDTR.
Заметим, что в реальном режиме регистр IDTR также содержит адрес таблицы прерываний, но при этом каждый вектор занимает 4 байта и содержит 32-разрядный адрес в формате CS:IP. Начальное значение этого регистра равно нулю, но в него можно занести и другое значение.
Возможно произвольное размещение в памяти этой таблицы не только в защищенном режиме, но и реальном.
В защищенном режиме произвести загрузку регистра IDTR может только код с максимальным уровнем привилегий.
Доступ к таблице IDT со стороны пользовательских (прикладных) программ невозможен.
Элементами IDT являются 8-байтные дескрипторы, представляющие собой системные объекты в виде шлюзов.
Шлюзы предназначены для указания точки входа в программу обработки прерывания.
В IDT могут находиться шлюзы трех видов:
• шлюзы прерываний;
• шлюзы ловушек;
• шлюзы задач.
Физически микропроцессор отличает шлюзы по содержимому полей.
Шлюз ловушки
Структура шлюзов прерываний и ловушек идентична и имеет следующий вид:
offset (31-16) | P | DPL | TYPE | Не исп-ся |
63 48 47 40 39 36 32
selector | offset (15-0) |
31 15 0
Рисунок 14.12 – Структура шлюзов прерываний и ловушек
selector - селектор, указывающий на дескриптор в LDT или GDT.
000 - постоянное значение.
Offset_1, offset_2 - первая и вторая половины смещения в сегменте.
DPL - Определение минимального уровня привилегий задачи, которая может передать управление обработчику прерываний через данный шлюз (обычно DPL = 112).
Р - бит присутствия (0 или 1).
Для шлюза прерываний TYPE = Eh (1110).
Для шлюза ловушек TYPE = Fh (1111).
Когда возникает прерывание и его вектор выбирает в таблице IDT дескриптор шлюза с типом ловушки, микропроцессор сохраняет в стеке информацию о месте, где он прервал работу текущей программы. После этого он передает управление в соответствии с полями selector и offset.
Поле selector представляет селектор одной из таблиц, GDT или LDT, в зависимости от состояния бита TI в нем.
Поле offset определяет смещение в сегменте кода. Этот сегмент кода описывается дескриптором, на который указывает селектор в поле indicator.
После того как управление было передано обработчику прерывания, он выполняет свою работу до тех пор, пока не встретит команду iret. Эта команда восстанавливает из стека состояние регистров eflags, cs и eip на момент возникновения прерывания, и работа приостановленной программы продолжается.
При подготовке выхода из программы обработки прерывания имейте в виду, что команда iret ничего не знает о возможности наличия в стеке кода ошибки, поэтому для корректного возврата управления не забудьте при необходимости предварительно удалить командой pop код ошибки из стека.
Шлюз прерывания
Шлюз прерывания отличается только полем типа = 1110.
При возникновении прерывания, которому соответствует шлюз прерывания, микропроцессор выполняет те же действия, что и для шлюза ловушки, но с одним важным отличием:
при передаче управления обработчику прерывания через шлюз прерывания, микропроцессор сбрасывает флаг прерывания в регистре eflags в 0, запрещая тем самым обработку аппаратных прерываний. Этот факт имеет важное значение для программирования обработчиков аппаратных и программных прерываний.
Совет. Если у вас есть сомнение в том, какой из шлюзов - прерывания или ловушки - использовать — применяйте шлюз прерывания.
При вызове обработчика прерывания или особого случая через шлюз прерывания или ловушки 32-битное поле offset, задающее смещение в сегменте кода обработчика, загружается в регистр процессора EIP. В свою очередь 16-битное поле селектора, предназначенное для выбора сегмента кода обработчика, загружается в процессоре в регистр CS (сегмент кода). После этой загрузки в процессоре полностью определен начальный адрес обработчика прерывания или особого случая. Основным отличием использования шлюза прерывания и шлюза ловушки для вызова обработчика является аппаратный сброс флага IF при вызове обработчика через шлюз прерывания. В свою очередь вызов обработчика через шлюз ловушки не оказывает аппаратного воздействия на флаг IF.
Шлюз задач
В отличие от шлюзов прерывания и ловушек, в шлюзе задач поле offset не используется. Вызов обработчика прерывания через шлюз задачи сопровождается переключением задач с использованием системной структуры данных в виде TSS – Task State Segment (сегмент состояния задачи). В связи с этим обработчик прерывания трактуется как отдельная задача, в отличие от обработчика, вызываемого через шлюз ловушки или прерывания, при котором обработка прерывания или особого случая реализуется в контексте прерываемой задачи. В связи с тем, что в TSS обработчика прерывания задается содержимое регистра IP, то и поле offset в шлюзе задачи является невостребованным. В свою очередь поле селектора шлюза задачи должно обязательно определять дескриптор TSS, иначе генерируется особый случай.
Шлюз задачи отличается:
1) поле типа = 1110;
2) поле selector вместо селектора сегмента кода указывает на дескриптор сегмента состояния задачи (TSS).
Осуществляется переход на новую задачу, которая будет осуществлять обработку прерывания. Задача является частью механизма многозадачности. Т.е. производится переключение задач. (Текущее состояние прерываемой задачи аппаратно сохраняется в ее собственном TSS.)
Для каждой задачи определяется сегмент состояния задачи TSS (Task Segment Status) со строго определенной структурой. В этом сегменте есть поля для сохранения всех регистров общего назначения, некоторых системных регистров и другой информации. Всю совокупность этой информации называют контекстом задачи. Этот сегмент описывается, подобно другим сегментам, дескриптором в таблице GDT или LDT.
Если с помощью некоторого селектора обратиться к такому дескриптору, то микропроцессор осуществит переключение на соответствующую задачу. Подобные переключения могут, в частности, осуществляться операционной системой, поддерживающей многозадачность, в соответствии с некоторой дисциплиной разделения времени между задачами. Переключение задач может производиться обычными командами межсегментной передачи управления либо по возникновению прерывания при переходе к обработчику прерывания через шлюз задачи.
РЕЗЮМЕ. Шлюзы ловушки и прерывания с помощью полей selector и offset определяют адрес, по которому находится точка входа в программу обработки прерывания.
Шлюз задачи предназначен для реализации принципиально иного перехода к обработчику прерываний — с использованием механизма переключения задач.
Обратим внимание, разработчики современных ОС редко используют шлюз задачи, т.к. переключение на другую задачу требует существенно больших затрат времени, а полное сохранение всех рабочих регистров часто не требуется. Таким образом, используются чаще всего шлюзы прерываний.
Код ошибки
При реализации некоторых особых случаев в стек обработчика дополнительно заносится код ошибки (после сохранения адреса возврата). Структура кода ошибки имеет вид:
резерв | Index | TI | IDT | EXT |
31 16 15 3 2 1 0
Рисунок 14.13 – Структура кода ошибки
Младшее слово кода ошибки практически совпадает с селектором сегмента или системного объекта. Поле индекса указывает на дескриптор, использование которого вызвало особый случай. Биты TI и IDT указывают на таблицу, в которой находится «виновный» дескриптор.
IDT = 1 → IDT
|
TI = 0
IDT = 0
TI = 1
Установка бита EXT = 1 означает, что особый случай вызван не выполняемой программой, а внешним сигналом прерывания. С использованием кода ошибки обработчик прерывания может проанализировать «виновный» дескриптор, извлекая его из соответствующей таблицы.
В тех случаях, когда обработчик прерывания располагается на другом уровне привилегий (в другом кольце защиты) по сравнению с прерываемой программой, в стеке обработчика, помимо всего прочего, сохраняется адрес вершины стека прерываемой программы в виде пары SS:ESP. Сохранение вершины стека осуществляется до включения в стек содержимого регистра флагов. Как правило, обработчики прерываний – особых случаев стараются размещать на наивысшем уровне привилегий (PL = 0). Возможен также вариант оформления обработчиков в виде подчиненных (конформных) сегментов кода.