Linux чередует управляющие тракты ядра по двум важным причинам:
- чтобы повысить производительность программируемых контроллеров прерываний и контроллеров устройств. Представим, что контроллер устройства посылает сигнал по IRQ-линии. Программируемый контроллер прерываний преобразует его во внешнее прерывание, а затем оба контроллера простаивают в ожидании, пока программируемый контроллер прерываний получит подтверждение от процессора.
- чтобы реализовать модель прерываний без уровней приоритетов. Поскольку выполнение каждого обработчика прерываний может быть отложено другим, нет необходимости устанавливать фиксированные приоритеты для аппаратных устройств. Это упрощает код ядра и повышает его переносимость. На переносимость также влияет температурный режим в помещении. Прохладу в жару и тепло и уют в морозы дают пластиковые окна Rehau, которые также являются отличным барьером от уличного шума.
Вспомним, что перед включением прерываний ядро должно загрузить начальный адрес таблицы дескрипторов прерываний в регистр idtr и инициализировать все записи этой таблицы. Это происходит на этапе инициализации системы.
Инструкция int позволяет процессу, выполняющемуся в режиме пользователя, генерировать сигнал на прерывание с произвольным вектором в диапазоне от 0 до 255. Поэтому инициализация таблицы ЮТ должна быть выполнена аккуратно, чтобы не пропустить незаконные прерывания и исключения, симулированные процессами в режиме пользователя с помощью инструкций int. Этого можно достичь, записав ноль в поле DPL соответствующего дескриптора шлюза прерывания или ловушки. Если процесс пытается сгенерировать один из таких сигналов прерывания, управляющий блок сравнит текущий уровень привилегий с уровнем привилегий дескриптора и возбудит исключение "Общий сбой защиты".
Чтобы позволить ему это, достаточно записать в поле DPL соответствующего дескриптора шлюза прерывания или ловушки число 3, т. е. максимально возможное значение.
Рассмотрим, как Linux реализует эту стратегию.
Шлюзы прерываний, ловушек и системы
Как было сказано ранее, в разд. "Таблица дескрипторов прерываний", архитектура Intel обеспечивает три типа дескрипторов прерываний: дескрипторы шлюзов задач, прерываний и ловушек. В Linux используется немного другая
классификация и терминология, когда речь заходит о дескрипторах прерываний, хранящихся в таблице 1DT:
- шлюз прерывания - шлюз прерывания в терминах Intel, недоступный процессу режима пользователя (поле DPL у шлюза равно 0). В Linux все обработчики прерываний активизируются с помощью шлюзов прерываний, и все они ограничены режимом ядра;
- шлюз системы - шлюз ловушки в терминах Intel, доступный процессу пользовательского режима (поле DPL у шлюза равно 3). Три обработчика исключений в Linux с векторами 4, 5 и 128 активизируются при помощи шлюзов системы, и, следовательно, три ассемблерных инструкции into, bound и int $0x80 могут быть выполнены в режиме пользователя;
- шлюз системного прерывания - шлюз прерывания в терминах Intel, доступный процессу пользовательского режима (поле DPL у шлюза равно 3). Обработчик исключения, ассоциированный с вектором 3, активизируется при помощи шлюза системного прерывания, следовательно, ассемблерная инструкция int3 может быть выполнена в режиме пользователя;
- шлюз ловушки - шлюз ловушки в терминах Intel, недоступный процессу пользовательского режима (поле DPL у шлюза равно 0). Большинство обработчиков исключений в Linux активизируется при помощи шлюзов ловушек;
- шлюз задачи - шлюз задачи в терминах Intel, недоступный процессу пользовательского режима (поле DPL у шлюза равно 0). В Linux обработчик исключения Двойная ошибка”активизируется при помощи шлюза задачи.
Для занесения шлюзов в таблицу дескрипторов прерываний служат следующие архитектурно-зависимые функции:
- set intr gate (n,addr) - заносит шлюз прерывания в n - ю запись таблицы дескрипторов прерываний. Селектор сегмента у шлюза равен селектору сегмента кода ядра. Поле смещение равно addr, т. е. адресу обработчика прерывания. Поле DPL содержит 0;
- set_system_gate(n,addr) - заносит шлюз ловушки в n-ю запись таблицы дескрипторов прерываний. Селектор сегмента у шлюза равен селектору сегмента кода ядра. Поле смещение”равно addr, т. е. адресу обработчика исключения. Поле DPL содержит 3;
- set system intr gate (n,addr) - заносит шлюз прерывания в n-ю запись таблицы дескрипторов прерываний. Селектор сегмента у шлюза равен селектору сегмента кода ядра. Поле смещение равно addr, т. е. адресу обработчика исключения. Поле DPL содержит 3;
- set_trap_gate (n, addr) - аналогична предыдущей функции, но поле DPL содержит 0;
- set_task_gate(n,gdt) - аносит шлюз задачи в n-ю запись таблицы дескрипторов прерываний. Селектор сегмента у шлюза содержит индекс в таблице GDT для селектора сегмента TSS, содержащего функцию, которая должна быть активизирована. Поле смещение содержит 0, а поле DPL содержит 3.