Как процессор находит код ядра после прерывания?


13

Когда происходит прерывание, процессор прерывает текущий процесс и вызывает код ядра для обработки прерывания. Как процессор знает, куда войти в ядро?

Я понимаю, что есть обработчики прерываний, которые могут быть установлены для каждой линии прерывания. Но поскольку процессор выполняет только «аппаратную логику», должно существовать некоторое предопределенное место, которое указывает либо на сам обработчик прерываний, либо на некоторый код, который выполняется перед обработчиком (поскольку для одной строки прерывания может быть несколько обработчиков, я предполагаю, что последний).

Ответы:


13

При запуске ядро ​​инициализирует таблицу векторов прерываний (называемую таблицей дескрипторов прерываний или IDT на x86), которая указывает на обработчик прерываний для каждой строки.

До 80286 IDT всегда хранилась по фиксированному адресу; начиная с 80286, IDT загружается с использованием LIDTинструкции.

Таблицы векторов прерываний указывают на один обработчик на строку прерывания; При этом ядро ​​может выбрать, например, предоставить обработчик прерываний, который запускает несколько других подпрограмм обработки прерываний, или один обработчик, который покрывает некоторые или все прерывания. Linux делает это, предоставляя общий обработчик прерываний, который определяет, какая линия прерываний была вызвана, и находит соответствующий нисходящий обработчик для вызова.


1
Таким образом, процессор использует строку прерывания в качестве индекса для IDT, помещает запись в ПК и начинает выполнять? но разве нет универсальной функции, которая выполняется перед всеми обработчиками прерываний? для Linux это будет do_IRQ (). это функция, на которую указывает каждая запись IDT, независимо от линии прерывания?
Филипп Мюрри

@PhilippMurry да. Затем ядро ​​использует свой собственный набор обработчиков прерываний (которых может быть более одного на строку) для фактической обработки прерывания перед возвратом к ранее выполненному коду.
Адам Марас

Итак, на самом деле есть два типа обработчиков прерываний: те, которые вызывает процессор (всегда do_IRQ ()), и те, которые вызывает ядро ​​(те, которые я зарегистрировал с помощью request_irq ()). не могли бы вы добавить это в свой ответ? я думаю, тогда я приму это :) большое спасибо
Филипп Мюрри

1
@PhilippMurry Я не эксперт в том, как Linux обрабатывает прерывания (и как разработчики ядра используют эту систему), но я добавил некоторую дополнительную информацию в более широком смысле о том, как ядра могут иметь собственное управление ISR.
Адам Марас

12

Да, есть предопределенное место, которое содержит адрес кода для перехода: вектор прерывания . В зависимости от процессора это может быть конкретное место в физической памяти (8088), конкретное место в виртуальной памяти, регистр процессора, место в памяти, указанное регистром (ARM, 386),…

Детали различаются для разных процессоров, но основными общими элементами обработки прерываний в процессоре являются:

  • Маска прерываний (так что любое последующее прерывание придется ждать).
  • Установите режим процессора на ядро ​​или режим прерывания (если процессор имеет такие режимы).
  • Сохраните значение счетчика программы в известном месте (регистр или память).
  • Возможно сохранить значение других регистров или переключаться между банками регистров).
  • Выполните следующую инструкцию (при новом значении ПК).

1

Два других ответа (на момент написания) говорят о прерываниях и IDT. Это верно, однако, на современном процессоре Intel-esque существует не менее трех способов вызова ядра.

Метод № 1: Прерывания.

Это объяснено выше. Вы устанавливаете запись в таблице дескрипторов прерываний / векторе прерываний, а затем выполняете программное прерывание для входа в ядро.

Основным преимуществом этого метода является то, что типичное ядро ​​должно быть способным обрабатывать прерывания в любом случае, и оно работает на устаревшем оборудовании.

Способ № 2: вызов ворот.

Шлюз вызова - это особый тип селектора сегмента. Цель вызова должна быть загружена в глобальную или локальную таблицу дескрипторов сегментов (GDT и LDT соответственно). Если затем выполнить инструкцию дальнего вызова с использованием шлюза вызова в качестве сегмента (смещение вызова игнорируется), это позволяет вам вызывать более привилегированный код. Ворота вызова чрезвычайно гибки; архитектура IA-32 имеет четыре уровня привилегий, а шлюзы вызовов позволяют вызывать любой уровень.

Я не верю, что в Linux когда-либо использовались шлюзы вызовов, а в Windows 95 - нет. Службы ядра Win95 ( krnl386.exeи kernel.dll) фактически работали в пользовательском режиме (кольцо 3). Самый высокий уровень привилегий (кольцо 0) использовался только для драйверов и микроядра, которое выполняло только переключение процессов. Звонок в водителей был сделан с помощью ворот вызова. Это позволило старому 16-битному коду (которых было много!) Использовать драйверы Win95, просто используя стандартный дальний вызов, как они всегда это делали.

Неадекватная защита таблицы глобальных дескрипторов была причиной нескольких эксплойтов Windows 95, которым удалось установить собственные шлюзы вызовов путем перезаписи памяти.

Метод № 3: SYSCALL / SYSRET и SYSENTER / SYSEXIT

Это два набора инструкций, независимо придуманных AMD и Intel, но по сути они делают то же самое. SYSCALL / SYSRET был первым и был только для AMD, SYSENTER / SYSEXIT был Intel, но AMD реализует его сейчас. Итак, я собираюсь описать SYSENTER / SYSEXIT.

В отличие от шлюзов вызовов, SYSENTER может использоваться только для передачи на кольцо 0 и может быть переведен только в одно место. Однако его преимущество заключается в чрезвычайно низкой задержке, поскольку в отличие от вызова или прерывания он не затрагивает стек.

Местоположение передачи устанавливается с использованием трех регистров, специфичных для модели: один для информации о сегменте, а другой - для указателя инструкций и указателя стека кода ядра. Поскольку ничто не «помещается» в стек, код пользовательского режима отвечает за указание ядру, куда возвращаться, путем передачи указателя инструкции возврата и указателя стека в регистрах. Ядро отвечает за восстановление указателя стека, а инструкция SYSEXIT восстанавливает указатель инструкции.

Дополнительная информация об инструкциях SYSENTER и SYSEXIT.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.