7.3. Дескрипторная таблица прерываний
Дескрипторы точек входа в обработчики прерываний или исключений хранятся в дескрипторной таблице прерываний (IDT). Сгенерировав прерывание, процессор умножает его номер на 8, чтобы получить смещение соответствующего дескриптора от начала таблицы. Так как процессор может генерировать лишь 256 прерываний, то в этой таблице имеют смысл лишь первые 256 элементов. Однако таблица может содержать и меньшее число элементов. Неиспользуемые элементы должны быть представлены дескрипторами с битом P=0 (бит присутствия). Обращение к таким элементам или элементам за пределом таблицы вызывает нарушение общей защиты. Линейный базовый адрес таблицы и ее размер определяются регистром IDTR. Значение этого регистра можно изменить с помощью привилегированной команды LIDT. Получить значение регистра IDTR можно с помощью команды SIDT, которая может выполняться на любом уровне привилегий.
Элементами IDT могут быть лишь три типа дескрипторов: шлюз ловушки, шлюз прерывания или шлюз задачи. Шлюзы ловушки и прерываний ссылаются на обработчики, выполняемые в контексте прерываемой задачи. Шлюз задачи ссылается на обработчик, выполняемый в отдельной задаче.
![Шлюз задачи](task-gate.gif)
Шлюз прерывания
![Шлюз прерывания](intr-gate.gif)
Шлюз ловушки
![Шлюз ловушки](trap-gate.gif)
При вызове обработчика прерывания или исключения через шлюз ловушки или прерывания процессор помещает в стек значение регистра флагов EFLAGS и адрес возврата CS:EIP, а для некоторых исключений еще код ошибки (Error Code). Если обработчик находится на уровне привилегий, отличающемся от уровня привилегий прерванной задачи, то происходит переключение стека (при этом в стеке обработчика сохраняется указатель вершины стека прерванной программы).
При вызове обработчика программного прерывания процессор очищает бит TF. При вызове обработчика аппаратного прерывания или исключения процессор, кроме того, сбрасывает биты VM, RF и NT. При вызове обработчика через шлюз прерывания процессор дополнительно очищает бит IF, блокируя прерывания по входу INTR#. При вызове обработчика через шлюз ловушки состояние флага IF не меняется.
Без переключения стека | |
---|---|
Общий стек прерванной программы и обработчика | |
![]() | |
Переключение стека при смене уровня привилегий Y→X | |
Стек прерванной программы | Стек обработчика |
![]() |
![]() |
Для выхода из обработчика и возврата к прерванной программе следует использовать инструкцию IRET, которая кроме всего прочего загружает из стека сохраненное значение регистра флагов. При этом поле IOPL может быть изменено, только если CPL=0, а бит IF меняется, если CPL<=IOPL. При необходимости по IRET происходит обратное переключение стека. Следует отметить, что обработчик исключения должен содержать команды, удаляющие из стека код ошибки, в противном случае инструкция IRET будет выполнена некорректно.
При вызове обработчиков прерываний / исключений действуют механизмы защиты, аналогичные контролю привилегий при передаче управления: процессор не позволяет передавать управление обработчику, находящемуся в менее привилегированном сегменте, чем прерываемая программа. Нарушение этого правила вызывает исключение #13. В отличие от обычного механизма защиты при непосредственном вызове обработчика не проверяется поле RPL, т.к. вектор прерывания его не предусматривает. Кроме того, поле DPL шлюза в таблице IDT проверяется процессором только при вызове обработчика при помощи команд INT n, INT3 и INTO (должно быть CPL<=DPLшлюза), т.е. для исключений и аппаратных прерываний процессор игнорирует поле DPL соответствующего элемента IDT.
Т.к. прерывания и исключения могут происходить в произвольный момент времени, то, чтобы не нарушать правил защиты при передаче управления, следует использовать один из следующих механизмов:
- поместить обработчик в подчиняемый сегмент кода (такой случай подходит, если обработчику не требуется никаких дополнительных данных, кроме данных в стеке, либо данные могут быть размещены в общедоступном сегменте с уровнем привилегий 3);
- поместить обработчик в обычный сегмент кода с уровнем привилегий 0.
Обработчик прерывания или исключения, вызываемый через шлюз задачи, обладает определенными преимуществами:
- автоматически сохраняется полный контекст прерванной задачи;
- обработчик может использовать независимый стек для нулевого уровня привилегий;
- обработчик может быть еще больше изолирован от остальных программ.
При вызове обработчика через шлюз задачи выполняется обычное переключение задач с вложением, т.е. выставляется бит NT и TSS прерванной задачи заносится в поле "Связь TSS" сегмента состояния задачи обработчика. Если таким образом обрабатывается исключение, то в стек обработчика включается код ошибки.
Однако обработка прерывания или исключения в отдельной задаче имеет также и некоторые недостатки:
- из-за бóльшего количества информации, сохраняемой при вызове обработчика, увеличивается время задержки обработки прерываний (латентность);
- механизм задач не допускает реентерабельности (поэтому на время обработки прерывания в отдельной задаче другие прерывания должны быть заблокированы, иначе повторное прерывание вызовет попытку переключения на занятую задачу и, как следствие, нарушение общей защиты).