Что такое прерывания в процессоре

Национальная библиотека им. Н. Э. Баумана
Bauman National Library

Персональные инструменты

Прерывание (Операционные Системы)

Каждое прерывание имеет свой собственный обработчик прерываний. Количество аппаратных прерываний ограничено числом строк запроса прерывания (IRQ) для процессора, но могут быть сотни различных программных прерываний. Прерывания — это широко используемая техника многозадачности компьютеров, в первую очередь в реальном времени. Такая система называется управляемой прерываниями.

Содержание

История

Первое фактическое использование прерываний приписывают компьютеру UNIVAC 1103 в 1953 году. [2] На IBM 650 (1954) было применено впервые прерывание путём маскировки. Национальное бюро стандартов DYSEAC (1954) первым использовало прерывания для ввода / вывода. IBM 704 был первым компьютером, использующим прерывания для отладки с «передаточной ловушкой», которая может ссылаться на специальную процедуру, когда команда ветвления была имеет столкновение. Система TX-2 (1957) была первой, обеспечивающей несколько уровней приоритетных прерываний. [3]

Типы прерываний

Прерывания могут быть разделены на следующие типы:

Процессоры обычно имеют внутреннюю маску прерываний, которая позволяет программному обеспечению игнорировать все внешние аппаратные прерывания, пока она установлена. Установка или очистка этой маски может быть быстрее, чем доступ к регистру маски прерываний (IMR) в PIC или отключение прерываний в самом устройстве. В некоторых случаях, например в случае архитектуры x86, отключение и включение прерываний на самом процессоре действует как барьер памяти.

Прерывание, которое оставляет машину в четко определенном состоянии, называется точным прерыванием. Такое прерывание имеет четыре свойства:

Прерывание, которое не соответствует указанным выше требованиям, называется неточным прерыванием.

Аппаратные прерывания

Аппаратные прерывания используются устройствами для передачи информации о том, что они требуют внимания со стороны операционной системы. [4] Внутренние аппаратные прерывания реализуются с использованием электронных сигналов оповещения, которые отправляются процессору от внешнего устройства, которое является частью самого компьютера, например контроллер диска, или внешнее периферийное устройство. К слову, нажатие клавиши на клавиатуре или перемещение мыши вызывают аппаратные прерывания, которые заставляют процессор считывать нажатие клавиши или положение мыши. В отличие от типа программного обеспечения, аппаратные прерывания являются асинхронными и могут произойти в середине выполнения инструкции, что требует дополнительного внимания при программировании. Акт инициирования аппаратного прерывания называется запросом прерывания (IRQ). [1]

Программные прерывания

Программное прерывание вызвано либо исключительным состоянием в самом процессоре, либо специальной инструкцией в наборе команд, которая вызывает прерывание, когда инструкция выполняется (см. рисунок 1). Первую часто называют ловушкой или исключением и используют для ошибок или событий, происходящих во время выполнения программы, которые настолько исключительны, что не могут быть обработаны в самой программе. Например, исключение деления на ноль будет выдано, если арифметическо-логическому блоку процессора приказано будет делить число на ноль, поскольку эта инструкция является ошибкой и невозможной. Операционная система поймает это исключение и сможет решить, что с этим делать: как правило, прерывать процесс и отображать соответствующее сообщение об ошибке. Инструкции программного прерывания могут функционировать аналогично вызовам подпрограмм и используются для различных целей, таких как запрос служб от драйверов устройств, например прерывания, отправляемые на контроллер диска и с контролера диска для запроса чтения или записи данных на диск и с диска. [1]

Что такое прерывания в процессоре. Смотреть фото Что такое прерывания в процессоре. Смотреть картинку Что такое прерывания в процессоре. Картинка про Что такое прерывания в процессоре. Фото Что такое прерывания в процессоре

Сложность с разделением линий прерывания

Несколько устройств, совместно использующих линию прерывания (любого стиля запуска), действуют как источники паразитных прерываний по отношению друг к другу. При наличии множества устройств в одной линии рабочая нагрузка при обслуживании прерываний увеличивается пропорционально квадрату количества устройств. Поэтому предпочтительно распределять устройства равномерно по доступным линиям прерывания. Нехватка линий прерывания является проблемой в старых конструкциях системы, где линии прерывания являются различными физическими проводниками. Прерывания с сигналом сообщения, где линия прерывания является виртуальной, предпочтительны в новых системных архитектурах (таких как PCI Express) и в значительной степени решают эту проблему.

Проблемы с производительностью

С многоядерными процессорами, дополнительные улучшения производительности в обработке прерываний могут быть достигнуты с помощью масштабирования на стороне приема (RSS), когда используются сетевые адаптеры с несколькими очередями. Такие NIC предоставляют несколько очередей приема, связанных с отдельными прерываниями; путем маршрутизации каждого из этих прерываний на разные ядра обработка запросов на прерывание, запускаемых сетевым трафиком, полученным одним NIC, может быть распределена между несколькими ядрами. Распределение прерываний между ядрами может выполняться операционной системой автоматически, либо маршрутизация прерываний (обычно называемая привязкой IRQ) может быть настроена вручную.

Чисто программная реализация распределения принимаемого трафика, известная как управление приемными пакетами (RPS), распределяет принятый трафик между ядрами позже в тракте данных как часть функциональности обработчика прерываний. Преимущества RPS по RSS не включают требований к конкретному оборудованию, более продвинутым фильтрам распределения трафика и уменьшенной частоте прерываний, создаваемых NIC. Как недостаток, RPS увеличивает частоту межпроцессорных прерываний (IPI). Управление потоком приема (RFS) расширяет программный подход, учитывая локальность приложений; Дальнейшее улучшение производительности достигается за счет обработки запросов на прерывание теми же ядрами, на которых конкретные сетевые пакеты будут использоваться целевым приложением. [1]

Типичное использование

Типичное использование прерываний включает в себя следующее: системные таймеры, дисковый ввод / вывод, сигналы-выключение и ловушки. Существуют прерывания для передачи байтов данных с использованием UART или Ethernet; для чувствительных нажатий клавиш, управления двигателями и т.д.

Другое типичное использование состоит в том, чтобы генерировать периодические прерывания путем деления выходного сигнала кварцевого генератора и с помощью обработчика прерываний подсчитывать прерывания, чтобы процессор мог сохранять время. Эти периодические прерывания часто используются планировщиком задач ОС для перепланирования приоритетов запущенных процессов. Некоторые старые компьютеры генерировали периодические прерывания от частоты линии электропередачи, потому что она контролировалась коммунальными службами, чтобы исключить длительный дрейф электрических часов.

Прерывания используются для эмуляции инструкций, которые не реализованы на определенных моделях в компьютерной линии. Например, инструкции с плавающей запятой могут быть реализованы в аппаратных средствах в некоторых системах и эмулироваться в более дешевых системах. Выполнение невыполненной инструкции вызовет прерывание. Обработчик прерываний операционной системы распознает возникновение невыполненной инструкции, интерпретирует инструкцию в программной подпрограмме и затем вернется к программе прерывания, как если бы инструкция была выполнена. Это обеспечивает переносимость прикладного программного обеспечения по всей линии. [1]

Источник

Прерывания в конвейеризированных процессорах

Наверняка вы знаете, что такое прерывания. Возможно, даже интересовались устройством процессора. Почти наверняка вы нигде не видели внятный рассказ про то, как именно процессор обнаруживает прерывание, переходит к обработчику и, самое главное, возвращается из него именно туда, куда положено.

Я писал эту статью год. Изначально она была рассчитана на хардварщиков. Понимание того, что я ее никогда не закончу, а также жажда славы и желание, чтобы ее прочло больше десяти человек, заставило меня адаптировать ее для относительно широкой аудитории, повыкидывав схемы, куски кода на Верилоге и километры временных диаграмм.

Если когда-нибудь вы задумывались над тем, что значат слова «the processor supports precise aborts» в даташите, прошу под кат.

Немного терминологии: процессор, процессы и прерывания

Процессор с параллельным выполнением команд может выполнять несколько команд одновременно. Например, процессор с четырехстадийным конвейером команд может одновременно записывать результаты первой команды, испонять вторую, декодировать третью и выбирать из памяти четвертую.

Точные и неточные прерывания

Программные прерывания и исключения могут быть точными или неточными. В некоторых случаях без точных исключений просто не обойтись — например, если в процессоре есть MMU (тогда, если случается промах TLB, управление передается соответствующему обработчику исключения, который программно добавляет нужную страницу в TLB, после чего должна быть возможность заново выполнить команду, вызвавшую промах).

В микроконтроллерах исключения могут быть неточными. Например, если команда сохранения вызвала исключение из-за ошибки памяти, то вместо того, чтобы пытаться как-то исправить ошибку и повторно выполнить эту команду, можно просто перезагрузить микроконтроллер и начать выполнять программу заново (то есть сделать то же самое, что делает сторожевой таймер, когда программа зависла).

В большинстве учебников по архитектуре компьютеров (включая классику типа Patterson&Hennessy и Hennessy&Patterson) точные прерывания обходятся стороной. Кроме того, неточные прерывания не представляют никакого интереса. По-моему, это отличные причины продолжить рассказ именно про точные прерывания.

Точные прерывания в процессорах с последовательным выполнением команд

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

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

Место, где процессор должен определить, позволить ли команде обновить состояние процесса или нет, называется точкой фиксации результатов (commit point). Если процессор сохраняет результаты команды, то есть команда не вызвала исключение, то говорят, что эта команда зафиксирована (на сленге — закоммичена).

Как можно догадаться, эту проблему довольно сложно решить, поэтому во многих процессорах для простоты реализованы «почти точные» прерывания, то есть точными сделаны все прерывания, кроме исключений, вызванных ошибками памяти при записи результатов. В этом случае точка фиксации результатов находится между третьим и четвертым этапами цикла команды.
Важно! Нужно помнить, что счетчик команд тоже должен обновляться строго после точки фиксации результатов. При этом он изменяется вне зависимости от того, зафиксирована команда или нет — в него записывается либо адрес следующей команды, либо вектор прерывания, либо РАВ.

Точные прерывания в процессорах с параллельным выполнением команд

На сегодняшний день процессоров с последовательным выполнением команд почти не осталось (могу вспомнить разве что аналоги интеловского 8051) — их вытеснили процессоры с параллельным выполнением команд, обеспечивающие при прочих равных более высокую производительность. Простейший процессор с параллельным выполнением команд — процессор с конвейером команд (instruction pipeline).
Несмотря на многочисленные преимущества, конвейер команд значительно усложняет реализацию точных прерываний, чем много десятков лет печалит разработчиков.

В процессоре с последовательным выполнением команд этапы цикла команды зависят друг от друга. Простейший пример — счетчик команд. Вначале он используется на этапе выборки (как адрес в памяти, откуда должна быть прочитана команда), затем на этапе исполнения (для вычисления его следующего значения), и потом, если команда зафиксирована, он обновляется на этапе записи результатов. Это приводит к тому, что нельзя выбрать следующую команду до тех пор, пока предыдущая не завершит последний этап и не обновит счетчик команд. То же самое относится и ко всем прочим сигналам внутри процессора.

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

Обратите внимание на столбец СК («счетчик команд»). Его значение меняется каждый такт и определяет адрес в памяти, откуда выбирается команда.
Внимательный читатель уже заметил небольшую неувязочку — для обеспечения точности прерываний первая команда не имеет права изменить счетчик команд раньше четвертого такта. Чтобы это исправить, мы должны перенести счетчик команд за точку фиксации результата (предположим, что она находится между третьим и четвертым этапами):

Производительность процессора немного упала, не так ли? На самом деле, решение лежит на поверхности – нам нужно два счетчика команд! Один должен находиться в начале конвейера и указывать, откуда читать команды, второй – в конце, и указывать на ту команду, которая должна быть зафиксирована следующей.
Первый называется «спекулятивным», второй – «архитектурным». Чаще всего спекулятивный счетчик команд не существует сам по себе, а встроен в предсказатель переходов. Выглядит это вот так:

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

Источник

Прерывания и особые случаи

Прерывания и особые случаи

Механизм прерывания обеспечивается соответствующими аппаратно-программными средствами компьютера.

Классификация прерываний представлена на рис. 7.1.

Что такое прерывания в процессоре. Смотреть фото Что такое прерывания в процессоре. Смотреть картинку Что такое прерывания в процессоре. Картинка про Что такое прерывания в процессоре. Фото Что такое прерывания в процессоре

Запросы аппаратных прерываний возникают асинхронно по отношению к работе микропроцессора и связаны с работой внешних устройств.

Запрос от немаскируемых прерываний поступает на вход NMI микропроцессора и не может быть программно заблокирован. Обычно этот вход используется для запросов прерываний от схем контроля питания или неустранимых ошибок ввода/вывода.

Для запросов маскируемых прерываний используется вход INT микропроцессора. Обработка запроса прерывания по данному входу может быть заблокирована сбросом бита IF в регистре флагов микропроцессора.

Программные прерывания делятся на следующие типы.

Порядок обработки прерываний

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

Обработка запросов прерываний состоит из:

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

Всего микропроцессор различает 256 типов прерываний. Таким образом, все они могут быть закодированы в 1 байте.

«Рефлекторные» действия микропроцессора по обработке запроса прерывания выполняются аппаратными средствами МП и включают в себя:

Что такое прерывания в процессоре. Смотреть фото Что такое прерывания в процессоре. Смотреть картинку Что такое прерывания в процессоре. Картинка про Что такое прерывания в процессоре. Фото Что такое прерывания в процессоре

Что такое прерывания в процессоре. Смотреть фото Что такое прерывания в процессоре. Смотреть картинку Что такое прерывания в процессоре. Картинка про Что такое прерывания в процессоре. Фото Что такое прерывания в процессоре

В IDT могут храниться только дескрипторы следующих типов:

Шлюзы ловушки и прерывания сходны со шлюзом вызова, только в них отсутствует поле счетчика WC (рис. 7.4). Так как прерывание является неожиданным событием и не связано с текущей программой, говорить о передаче параметров их обработчику не приходится.

Что такое прерывания в процессоре. Смотреть фото Что такое прерывания в процессоре. Смотреть картинку Что такое прерывания в процессоре. Картинка про Что такое прерывания в процессоре. Фото Что такое прерывания в процессоре

Бит присутствия P может быть равен как 0, так и 1.

При входе в обработчик через шлюз ловушки флаг IF не меняется.

Источник

Изучаем архитектуру Intel x86-64 при помощи ассемблера (Часть 5 — Прерывания)

После того как в предыдущей заметке мы написали загрузчик, мы не ограничены в размере кода нашей программы. Вернемся теперь снова к логике повествования книги Руслана Аблязова — Программирование на ассемблере на платформе х86-64 — 2011. В сегодняшней заметке реализуем обработку прерываний в защищенном режиме, которая рассмотрена в разделе книги 2.2. Прерывания в защищенном режиме.

Что такое прерывание

Прерывания — это механизм, при помощи которого программа (вероятнее всего — операционная система), которую выполняет процессор, может реагировать на различные события. События бывают двух видов:

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

Всего может существовать до 256 различных прерываний. Они пронумерованы от 0 до 255 и для каждого, вообще говоря, должен существовать свой обработчик. Полный список прерываний смотрите в разделе 6.3.1 External Interrupts руководства Intel 64 and IA-32 Architectures Software Developer’s Manual — Volume 3. System Programming Guide.

Программные прерывания

Заметим, что обработчик любого прерывания может быть вызван программно при помощи команды int n (где n — номер прерывания), что аналогично вызову функции командой call. Однако некоторые прерывания вызывать программно не стоит, так как это непременно приведет к аварии. К таким прерываниям относятся те исключения, при возникновении которых процессор помещает в стек т. н. код ошибки — дополнительная информация о произошедшем исключении. Команда int не помещает в стек код ошибки, но «ничего не подозревающий» обработчик исключения ожидает, что код ошибки будет в стеке — поэтому и произойдет авария.

Команду int n могут выполнять как программы, работающие в режиме ядра (наиболее привилегированном), так и программы, работающие в пользовательском режиме (наименее привилегированном). Для пользовательских программ вызов программного прерывания — это способ воспользоваться услугами операционной системы — т. н. системными вызовами. Подробнее о системных вызовах читайте тут: Wikipedia — System call.

При программном вызове обработчика прерывания процессор проверяет, обладает ли вызывающий код достаточным уровнем привилегий для осуществления такого вызова. Каков этот достаточный уровень привилегий, определяет поле DPL дескриптора шлюза прерывания (об этом см. ниже). Если уровень привилегий у вызывающего кода недостаточен, то процессор генерирует исключение General Protection Fault. К примеру, для того, чтобы пользовательский код мог вызвать обработчик прерывания программно, дескриптор шлюза прерывания должен иметь поле DPL=3.

Среди программных прерываний команда int 3 занимает особое место и имеет короткий опкод 0xСС. Эта команда используется отладчиками для реализации точек останова. Подробнее об отладке читайте в статье Хабрахабр — Про брейкпойнты.

Какие прерывания мы будем обрабатывать

Самый наглядный пример обработки аппаратных прерываний — это обработка прерываний от клавиатуры. Когда пользователь нажимает или отпускает клавишу на клавиатуре, контроллер клавиатуры генерирует запрос на прерывание. Благодаря обработке этого прерывания пользователь сможет хотя бы на минимальном уровне взаимодействовать с нашей программой. Кроме того, в сегодняшней программе мы будем обрабатывать еще одно аппаратное прерывание — от системного таймера. Остальные аппаратные прерывания (IRQ — прерывания от периферийных устройств) тоже будут иметь обработчики, только эти обработчики не будут ничего делать, а будут только возвращать управление прерванной программе. Мы также будем обрабатывать одно исключение — исключение общей защиты (General Protection Fault). Остальные исключения не будут иметь обработчиков, поэтому при возникновении таких исключений будет происходить Triple Fault (авария и перезагрузка процессора).

Откуда берутся обработчики прерываний

Обработчики прерываний — это практически обычные функции. Они находятся в оперативной памяти. Как процессор находит обработчик для каждого прерывания?
В реальном режиме работы процессора в самом начале оперативной памяти находится т. н. таблица векторов прерываний, которая представляет собой массив из 256-ти 32-разрядных чисел. Каждое число — это пара 16-разрядных чисел сегмент:смещение, которая и является адресом обработчика прерывания в памяти. Номер прерывания является номером записи в таблице векторов прерываний.
В защищенном режиме все сложнее, но не намного. В оперативной памяти хранится таблица векторов прерываний — Interrupt Descriptor Table (IDT), которая представляет собой массив 64-разрядных чисел — дескрипторов шлюзов. Дескриптор шлюза содержит селектор сегмента кода, в котором находится обработчик прерывания и адрес (смещение) обработчика в этом сегменте (т. е. ту же информацию, что и вектор прерывания в реальном режиме). Перед тем как разрешить прерывания, необходимо адрес и размер таблицы IDT загрузить в регистр IDTR при помощи команды lidt (аналогично тому, как мы загружали адрес и лимит таблицы GDT, когда переходили в защищенный режим).

Типы шлюзов в таблице векторов прерываний

В таблице IDT могут находиться шлюзы трех разных типов (шлюз прерывания, шлюз ловушки и шлюз задачи), но я в этой заметке буду использовать только один из них — шлюз прерывания. Шлюз ловушки ничем не отличается от шлюза прерывания, кроме одного: если в таблице IDT находится шлюз прерывания, то при вызове обработчика этого прерывания процессор сбрасывает флаг разрешения прерываний IF в регистре EFLAGS (запрещая маскируемые прерывания); если же в таблице находится шлюз ловушки, то процессор не сбрасывает флаг IF. А вот что такое шлюз задачи, я вообще пока не знаю.

Формат шлюза прерывания

Формат дескрипторов шлюзов прерываний, ловушек и задач смотрите в разделе 6.11 IDT DESCRIPTORS руководства Intel 64 and IA-32 Architectures Software Developer’s Manual — Volume 3. System Programming Guide. Здесь я хочу привести только формат шлюза прерывания.

31161514131287540
Offset[31:16]PDPL0 D 1 1 00 0 04
Segment SelectorOffset[15:0]0

Segment Selector — это селектор сегмента кода, в котором находится обработчик прерывания. Offset — это смещение обработчика относительно базового адреса сегмента кода. О поле DPL я уже говорил — оно играет роль только при программном вызове обработчика прерывания командой int n. Бит D определяет т. н. разрядность шлюза прерывания. О роли этого бита читайте раздел 21.1 DEFINING 16-BIT AND 32-BIT PROGRAM MODULES руководства Intel 64 and IA-32 Architectures Software Developer’s Manual — Volume 3. System Programming Guide. Я буду использовать только 32-разрядные сегменты и шлюзы, поэтому у меня бит D всегда будет равен 1.

Исходный код программы

Поскольку программа становится все больше и больше, мне приходится усложнять ее структуру — разбивать исходный код на отдельные файлы. Ниже схематично показана структура программы boot_sector.asm. Эта часть программы вряд ли подвергнется изменениям в дальнейшем. Всё, что может измениться в дальнейшем, я поместил в файл protected_mode.inc, который включается директивой include в самый конец программы.

Функции BIOS, загрузку секторов с диска, переход в защищенный режим и глобальную таблицу дескрипторов мы уже рассмотрели в предыдущих заметках:

В нынешнем файле boot_sector.asm используется как загрузчик, так и переход в защищенный режим, так что этот файл по сути является комбинацией одноименных файлов из двух упомянутых выше заметок. Плюс я добавил в него несколько мелких новшеств:

Итак, вот полный код файла boot_sector.asm:

start :
jmp far dword 0x0000 : entr ; makes CS=0, IP=0x7c00

; Print message
push bootloader_message
call BIOS_PrintString

; Reset disk subsystem
push [ disk_id ]
call BIOS_ResetDiskSubsystem

; Disable maskable interrupts
cli ; clear flag IF in EFLAGS register

lgdt [ gdtr ] ; load the address and size of Global Descriptor Table (GDT) into GDTR register

; Load protected mode entry point into CS:EIP
jmp far dword 0000000000001000b : pm_entry ; 0000000000001000b is a segment selector which is loaded into CS register

CODE_SELECTOR equ 0000000000001000b ; code segment selector: RPL = 0, TI = 0, Descriptor Index = 1
DATA_SELECTOR equ 0000000000010000b ; data segment selector: RPL = 0, TI = 0, Descriptor Index = 2
VIDEO_SELECTOR equ 0000000000011000b ; video segment selector: RPL = 0, TI = 0, Descriptor Index = 3

Далее нас будет интересовать только файл protected_mode.inc. Его структура такова:

Рассмотрим каждую часть кода в отдельности.

Вывод на экран текстовых сообщений

Пока файл pm_utility_functions.inc содержит только функции для вывода текста на экран. Возможно в дальнейшем я добавлю в него еще какие-то функции. В текстовом режиме экран делится на 25 строк и 80 столбцов и содержит 25*80=2000 текстовых символов. Все 2000 символов представляют собой массив, который находится в видеопамяти, которая проецируется на адресное пространство процессора начиная с адреса 0xB800 (этот адрес, как вы помните, записан у нас в специальном дескрипторе сегмента). Каждый символ занимает два байта. Формат этих двух байт следующий:

АтрибутСимвол
7654321076543210
МерцаниеЦвет фонаЦвет текстаASCII-код символа

Таким образом, чтобы вывести текстовую строку на экран, надо скопировать символы этой строки в видеопамять, не забыв добавить к каждому символу его атрибут. Вот это все и делает функция PrintString, код которой приведен ниже. Также у нас есть функция ClearScreen, которая заполняет весь экран нулевыми символами.

;
ClearScreen :
push eax
push ecx
push edi

push eax
push edi
push esi

pop esi
pop edi
pop eax

Шлюзы прерываний

Макросы для определения дескрипторов сегментов и шлюзов я поместил в отдельный файл descriptor_macros.inc. Формат дескриптора шлюза прерывания мы уже рассмотрели. Написать после этого макрос для его определения несложно. Все пояснения — в комментариях.

Контроллер прерываний

Аппаратные прерывания — это механизм, при помощи которого периферийные устройства могут сообщать процессору о неких событиях. Периферийных устройств довольно много, и каждому выделена линия прерывания, на которую устройство может выдавать цифровой сигнал. Все эти линии прерываний идут от периферийных устройств на специальную микросхему на материнской плате компьютера — контроллер прерываний (PIC — Programmable Interrupt Controller). До выхода процессора Intel Pentium этой микросхемой была i8259A (точнее, две соединенные друг с другом микросхемы i8259A). Начиная с процессора Pentium начал использоваться новый механизм обработки прерываний под названием APIC — Advanced Programmable Interrupt Controller, а в качестве контроллера прерываний стала использоваться микросхема i82093AA (i82093AA — это так называемый I/O APIC, а есть еще т. н. Local APIC — блок, встроенный в процессор). В модели APIC предусмотрена обратная совместимость со старой моделью (PIC), когда I/O APIC эмулирует работу двух микросхем i8259A. В этой заметке мы будем пользоваться старой моделью, а почитать подробнее о новой модели можно в следующих источниках:

Итак, рассматриваем старую модель (PIC — две микросхемы i8259A). О возникновении запроса на прерывание от периферийного устройства процессор узнает благодаря входу INTR (interrupt request). Линия INTR соединена с PIC. У PIC есть 16 линий IRQ, на которые периферийные устройства присылают свои запросы на прерывания. PIC состоит из двух микросхем i8259A, одна из них называется ведущей (master), другая — ведомой (slave). У каждой i8259A восемь входов IRQ — всего получается 16. Причем ведомая i8259A подключена ко входу IRQ2 ведущей i8259A. Когда периферийное устройство посылает контроллеру прерываний запрос на прерывание (устанавливая активный логический уровень на соответствующем входе IRQ), контроллер в свою очередь устанавливает активный уровень на входе INTR процессора, после чего процессор прерывает выполнение основной программы, получает по системной шине от контроллера прерываний номер вектора прерывания, находит вектор прерывания в таблице векторов прерываний и переходит к выполнению обработчика прерывания. Каждому входу IRQ контроллера прерываний соответствует номер вектора прерываний, и программист должен запрограммировать это соответствие при инициализации контроллера прерываний. Например, в программе я настраиваю PIC так, чтобы вход IRQ0 соответствовал вектору прерывания номер 32, а IRQ15 — вектору номер 47. Программирование PIC осуществляется при помощи портов ввода-вывода 0x20 (регистр iSr ведущего PIC), 0x21 (регистр iMr ведущего PIC), 0xA0 (регистр iSr ведомого PIC), 0xA1 (регистр iMr ведомого PIC). Подробности читайте в книге [Аблязов] раздел 2.2.6. Аппаратные прерывания.

У процессора есть еще один вход — NMI (non-maskable interrupt), который активируется при возникновении неких аппаратных сбоев (см. OSDev.org — Non Maskable Interrupt и раздел 6.7 NONMASKABLE INTERRUPT (NMI) руководства Intel 64 and IA-32 Architectures Software Developer’s Manual — Volume 3. System Programming Guide). Обработчик NMI имеет номер 2 в таблице векторов прерываний. Несмотря на название, немаскируемые прерывания все-таки можно отключить при помощи порта ввода-вывода 0x70 (CMOS controller).

Обработчики прерываний

Обработчик прерывания — это в общем обычная функция, впрочем есть пара особенностей. Когда возникает прерывание, процессор прерывает выполнение основной программы. При этом он сохраняет в стеке адрес машинной команды основной программы, на которой ему пришлось прерваться (пара CS:EIP), с тем, чтобы после обработки прерывания продолжить выполнение основной программы с этой команды. Еще в стеке сохраняется содержимое регистра EFLAGS. Команда возврата из обработчика прерывания должна восстановить значение регистра EFLAGS из стека и перепрыгнуть обратно на прерванную основную программу. Эта команда возврата называется IRETD.

Кроме того, при возникновении некоторых исключений в стек помещается так называемый код ошибки — его обработчик исключения должен вытолкнуть их стека перед выполнением команды IRETD. Какие исключения помещают в стек код ошибки, а какие нет — смотрите в Table 6-1. Protected-Mode Exceptions and Interrupts руководства [Intel-Volume3].

Обработчики аппаратных прерываний должны при своем завершении производить так называемый сброс обеих микросхем i8259A, сообщая им, что прерывание обработано. Это делается записью в их регистры iSr числа 0x20. Соответствующий код в программе помещен в функцию int_EOI, и все обработчики аппаратных прерываний в конце прыгают на эту функцию.

General Protection Fault

Исключение общей защиты возникает при нарушении кодом ограничений, задаваемых его уровнем привилегий. Это может быть например доступ к памяти ОС или выполнение команд in и out пользовательским кодом. В программе обработчик этого исключения просто выводит на экран сообщение «General Protection Exception». В этом исключении есть код ошибки, поэтому обработчик выталкивает его из стека (pop eax).

Прерывание от системного таймера

Прерывание от системного таймера происходит с частотой 18 раз в секунду. На нем можно построить измерение времени, как это сделано в книге [Аблязов], но я решил упростить дело и просто вывожу в правый верхний угол экрана косую черточку, каждую секунду меняя ее направление (с \ на / и обратно). Для подсчета прерываний от таймера используется глобальная переменная timer_counter.

Прерывание от контроллера клавиатуры

Тема работы с клавиатурой сама по себе достаточно большая, поэтому я написал об этом отдельную заметку.

Исходный код программы в защищенном режиме

Ниже приведен полный код файла protected_mode.inc:

; protected_mode.inc
jmp protected_mode_code

; counter being incremented at each interrupt from the system timer
timer_counter dd 0

; cursor position (0. 1999). initial value of 160 places the cursor to the 4th row on the screen.
cursor dd 240

protected_mode_code :
push word 1 ; row
push word 0 ; column
push pm_message
call PrintString

; Disable keyboard
;push word KEYBOARD_COMMAND_DISABLE
;call Keyboard_WriteInputBuffer
;call Keyboard_ReadOutputBuffer

; Enable keyboard
;push word KEYBOARD_COMMAND_ENABLE
;call Keyboard_WriteInputBuffer
;call Keyboard_ReadOutputBuffer

; Load IDTR register
lidt [ idtr ]

; Initialize Programmable Interrupt Controller (PIC)
iSr_master equ 0x20
iMr_master equ 0x21

iSr_slave equ 0xA0
iMr_slave equ 0xA1

; Enable maskable interrupts
sti

. exit :
pop edi
pop ax
jmp int_EOI

Makefile

Наконец, вот makefile для построения программы:

2 комментария к “Изучаем архитектуру Intel x86-64 при помощи ассемблера (Часть 5 — Прерывания)”

Здравствуйте. С удовольствием прочел цикл Ваших статей, так как потребовалось «с нуля» написать нечто похожее. Я правильно понимаю, что на прерываниях и клавиатуре Вы и остановились?

Здравствуйте, Денис! Спасибо, рад, что вам понравились мои заметки. В общем, да, я на этом и остановился. Но может быть вернусь к теме ОС когда-нибудь. К OpenGL, например, я периодически возвращаюсь 🙂

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *