§ Исключения

Исключениями называются прерывания, которые генерирует процессор в ответ на нарушения условий защиты. Повлиять на исключения прикладные программы (работающие на уровне привилегий, выше 0) не могут, замаскировать - тоже. Аппаратный контроль защиты - самый надёжный и 32-разрядные процессоры предоставляют этот сервис в полном объёме.
Исключения делятся на три типа, в зависимости от условий их возникновения:
  • Ошибка (fault)
  • Ловушка (trap)
  • Авария (abort)
Ошибка – это исключение, возникающая в ситуации ошибочных действий программы и подразумевается, что такую ошибку можно исправить. Такой тип исключения позволяет рестарт "виноватой" команды после исправления ситуации, для чего в стеке обработчика адрес возврата из прерывания указывает на команду, вызвавшую исключение. Примером такого исключения может быть исключение неприсутствующего сегмента (прерывание 0Bh), возникающее при попытке обратиться к сегменту, в дескрипторе которого бит P=0. Благодаря этому реализуется механизм виртуальной памяти, в частности, подкачка данных с диска.
Ловушка – это исключение, возникающее сразу после выполнения "отлавливаемой" команды. Это исключение позволяет продолжить выполнение программы со следующей команды (без рестарта "виноватой"). На ловушках строится механизм отладки программ.
Авария – это исключение, которое не позволяет продолжить выполнение прерванной программы и сигнализирует о серьёзных нарушениях целостности системы. Примером аварии служит исключение двойного нарушения (прерывание 8), когда сама попытка обработки одного исключения вызывает другое исключение.
Для некоторых исключений процессор помещает с стек обработчика двухбайтовый код ошибки, позволяющий конкретно определить ошибку.
Далее приводится полный список исключений и прерываний. В этой таблице применяются следующие обозначения:
  • Номер вектора – номер вектора прерывания, на которое отображено исключение.
  • Название – используется в документации Intel и состоит из заглавных букв английского названия исключения, например, #DE - Divide Error.
  • Error code – наличие dw-кода ошибки, который процессор добавляет в стек обработчика перед передачей ему управления.
Таблица 3-1. Исключения и прерывания защищённого режима.
Номер вектора Название Описание Тип Error Code Источник исключения
0 #DE Ошибка деления Fault Нет Команды DIV и IDIV
1 #DB Отладка Fault/Trap Нет Любая команда или команда INT 1
2 - Прерывание NMI Прерывание Нет Немаскируемое внешнее прерывание
3 #BP Breakpoint Trap Нет Команда INT 3
4 #OF Переполнение Trap Нет Команда INTO
5 #BR Превышение предела Fault Нет Команда BOUND
6 #UD Недопустимая команда (Invalid Opcode) Fault Нет Недопустимая команда или команда UD21
7 #NM Устройство не доступно (No Math Coprocessor) Fault Нет Команды плавающей точки или команда WAIT/FWAIT
8 #DF Двойная ошибка Abort Да (Нуль) Любая команда
9 - Превышение сегмента сопроцессора (зарезервировано) Fault Нет Команды плавающей точки2
0Ah #TS Недопустимый TSS Fault Да Переключение задач или доступ к TSS
0Bh #NP Сегмент не присутствует Fault Fault Загрузка сегментных регистров или доступ к сегментам
0Ch #SS Ошибка сегмента стека Fault Да Операции над стеком и загрузка в SS
0Dh #GP Общая защита Fault Да Любой доступ к памяти и прочие проверки защиты
0Eh #PF Страничное нарушение Fault Да Доступ к памяти
0Fh - Зарезервировано Intel-ом. Не использовать.   Нет  
10h #MF Ошибка плавающей точки в x87 FPU (Ошибка математики) Fault Нет Команда x87 FPU или команда WAIT/FWAIT
11h #AC Проверка выравнивания Fault Да (Нуль) Обращение к пямяти3
12h #MC Проверка оборудования Abort Нет Наличие кодов и их содержимое зависит от модели4
13h #XF Исключение плавающей точки в SIMD Fault Нет Команды SSE и SSE25
14h-1Fh - Заререзвировано Intel-ом. Не использовать      
20h-FFh - Прерывания определяются пользователем Прерывание   Внешнее прерывание или команда INT n

§ Примечания:

  • Команда UD2 появилась в процессоре Pentium Pro.
  • Процессоры IA-32 после Intel386 не генерируют это исключение.
  • Это исключение появилось в процессоре Intel486.
  • Это исключение появилось в процессоре Pentium и развивается в процессорах семейства P6.
  • Это исключение появилось в процессоре Pentium III.
Некоторое небольшое число исключений, являющиеся ошибками, не позволяют продолжить выполнение программы, т.к. при их генерации теряется часть данных программы, в которой произошла ошибка. Например, выполнение команды POPAD при недостаточном размере стека, вызывает такое исключение. В таком случае, обработчик исключения будет подразумевать, что команда POPAD не выполнена, хотя часть регистров общего назначения уже может измениться. При обнаружении таких ошибок рекомендуется прекращать выполнение программы, вызвавшей сбой.
Операционная система должна определить дескрипторы для всех исключений. Если процессор при генерации исключения не обнаружит в IDT соответствующего дескриптора, это приведёт к генерации ещё одного исключения. Процессор может обработать два исключения последовательно, но в некоторых случаях, когда это ему не удаётся, он генерирует исключение двойной ошибки.
Если в IDT не будет дескриптора и для исключения двойной ошибки, либо не сможет корректно его обработать, то процессор переходит в режим отключения, похожий на режим, в который его переводит команда HLT (т.е. попросту, зависает) и будет реагировать только на сигналы типа аппаратного сброса.
Сами обработчики исключений не обязательно должны выполнять свои функции. На этапе создания операционной системы достаточно сделать "заглушки", которые будут выводить на экран номер исключения и параметры, переданные в стеке. Т.к. действия обработчиков сильно отличаются в зависимости от предназначения ОС, то здесь, в этом разделе, будут приведены лишь примеры "заглушек", а примеры обработчиков исключений и аппаратных прерываний вы сможете найти в соответствующих разделах.