Для чего используются команды программных прерываний – Прерывания для самых маленьких / Habr

Прерывания для самых маленьких / Habr

Сегодня мы поговорим о прерываниях процессоров семейства x86 (-64). Подробнее под катом.
Прерывания — это как бы сигнал процессору, что надо прервать выполнение (их поэтому и назвали прерываниями) текущего кода и срочно сделать то, что указано в обработчике.
Все адреса обработчиков прерываний хранятся в IDT. Это таблица, в которой хранятся 256 (можно больше или меньше, но большие значения просто игнорируются) ячеек (векторы прерываний) с типом и атрибутами прерывания, одним просто нулевым значением, собственно адресом обработчика прерываний и селектором кода в GDT или LDT, который будет использовать данный вектор прерываний. Теперь немного о типе и атрибутах.
Тип прерывания и атрибуты занимают 8 бит. Первые 4 бита занимают тип:
  • 0b0101: 32-битный гейт задачи, при появлении такого прерывания происходит хардверное переключение задачи (да-да, есть и такое, но его уже давно не используют)
  • 0b0110: 16-битный гейт прерывания
  • 0b0111: 16-битный гейт trap’a (я не знаю, как это перевести на русский язык, извините)
  • 0b1110: 32-битный гейт прерывания
  • 0b1111: 32-битный гейт trap’a

Далее идут атрибуты. Первым атрибутом является 1 бит, который задан в 0 для гейтов прерывания и в 1 для остальных. Далее идет уровень привилегий дескриптора — 2 бита, задающие минимальный уровень привилегий для вызова прерываний, и 1 бит, заданный в 0 для неиспользуемых прерываний.
Теперь о том, как процессор вызывает обработчики.
Допустим, что вы вызвали инструкцию int 0 в ассемблере. Это даст сигнал процессору, что надо вызвать прерывание 0, если это возможно. Вот последовательность действий, которые происходят при этом.
  1. Поиск вектора №0 в IDT.
  2. Сравнение уровня привилегий дескриптора и текущего уровня привилегий процессора.
  3. Если текущий уровень привилегий процессора меньше уровня привилегий дескриптора, то просто вызвать генеральную ошибку защиты и не вызывать прерывание.
  4. Происходит сохранение адреса возвращения, регистра (E)FLAGS и другой информации.
  5. Происходит переход на адрес, указанный в векторе №0 IDT.
  6. После выполнения обработчика инструкция iret возвращает управление прерванному коду.

Еще есть прерывания, которые генерируются самим процессором при определенных обстоятельствах — исключения. Вот их список с краткими описаниями:
  • Деление на ноль. Генерируется при, собственно, делении на ноль.
  • Отладочное исключение. Генерироваться само не может, используется для, собственно, отладки.
  • Немаскируемое прерывание. Генерируется при ошибках ОЗУ и невосстановимых ошибках «железа». Их невозможно замаскировать с помощью PIC (Programmable Interrupt Controller — программируемый контроллер прерываний), так как оно идет сразу в процессор, минуя PIC, но можно просто отключить.
  • Точка останова. Тоже используется для отладки, потому что его опкод занимает всего 1 байт, в отличии от остальных INT N. Переназначалось DOS-отладчиками для своих целей.
  • Переполнение. Генерируется инструкцией INTO, если в (E)FLAGS включен бит переполнения.
  • Выход за пределы. Генерируется при ошибке инструкции BOUND.
  • Недопустимый опкод. Генерируется при попытке выполнения недопустимого кода операции.
  • Устройство недоступно. Сейчас не используется, генерировался при попытке использования операций с плавающей точкой на процессорах без FPU.
  • Double fault. Сложно перевести название. Ошибка невосстановима, происходит при невозможности вызвать обработчик исключения.
  • Переполнение сегмента сопроцессора. Больше не используется.
  • Недопустимый TSS. Сегмент состояния задачи задан неправильно.
  • Сегмент отсутствует. Возникает при попытке загрузки сегмента с битом Present == 0.
  • Ошибка сегмента стека. Возникает при попытке загрузки сегмента с битом Present == 0 или переполнении стека.
  • Общая ошибка защиты. Генерируется в очень большом числе случаев, среди них есть ошибка сегмента, попытка выполнения инструкции без необходимых прав, запись туда, куда не надо, попытка доступа к нулевому дескриптору GDT и многое другое.
  • Ошибка страницы. Происходит при чтении или записи в несуществующую страницу памяти, попытке доступа к данным без необходимых прав или другом.
  • Ошибка с плавающей точкой. Происходит при выполнении инструкции FWAIT или WAIT с битом №5 в CR0 == 0.
  • Ошибка при проверке на выравнивание. Происходит только в третьем кольце привилегий процессора, если эта ошибка, конечно, включена.
  • Ошибка при проверке машины. Генерируется процессором при обнаружении «железных» ошибок.
  • Исключение с плавающей точкой SIMD. Генерируется при ошибках с 128-битными числами с плавающей точкой.
  • Ошибка виртуализации.
  • Ошибка безопасности.
  • Тройная ошибка. По сути исключением не является, это даже не прерывание. Происходит при невозможности вызвать Double Fault. Вызывает немедленную перезагрузку компьютера.

Существует особый тип прерываний — IRQ (Interrupt ReQuest), или же аппаратные прерывания, но я буду их для краткости называть просто IRQ. Технически они почти не отличаются от любых других прерываний, но генерируются не процессором или самим кодом, а устройствами, подключенными к компьютеру. К примеру, IRQ №0 генерируется PIT (таймер с программируемым интервалом), IRQ 1 генерируется при нажатии клавиши на клавиатуре, а IRQ 12 — при действии с PS/2-мышью.
Еще есть так называемые программные прерывания. Их, как понятно из названия, программа должна вызывать сама — никто их за нее не вызывает. Таковыми являются, например, системные вызовы в некоторых системах. В Linux, например, они висят на векторе 0x80. Во многих хобби-ОС они тоже висят на векторе 0x80. Теперь немного отсебятины — я думаю, что сисвызовы сделаны в виде прерываний из-за того, что 1) их так очень легко вызывать, 2) их можно вызвать из любого кода, работающего в ОС — IDT-то одна на всю систему.
Информация взята с OSDev Wiki.

habr.com

Программные прерывания — Мегаобучалка

Хотя большинство прерываний генерируется аппаратно, ядро Windows тоже может генерировать прерывания — только они являются программными. Этот вид прерываний служит для решения многих задач, в том числе:

· инициации диспетчеризации потоков;

· обработки прерываний, не критичных по времени;

· обработки событий таймеров;

· асинхронного выполнения какой-либо процедуры в контексте конкретного потока;

· поддержки асинхронного ввода-вывода. Эти задачи подробно рассматриваются ниже.

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

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

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

В зависимости от источника прерывания делятся на три больших класса: внешние, внутренние и программные.

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



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

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

Прерываниям приписывается приоритет, с помощью которого они ранжируются по степени важности и срочности.

Прерывания обычно обрабатываются модулями операционной системы, так как действия, выполняемые по прерыванию, относятся к управлению разделяемыми ресурсами ВС. Процедуры, вызываемые по прерываниям, обычно называют обработчиками прерываний или процедурами обслуживания прерываний (Interrupt Service Routine, ISK). Аппаратные прерывания обрабатываются драйверами соответствующих внешних устройств, исключения – специальными модулями ядра, а программные прерывания – процедурами ОС, обслуживающими системные вызовы. Кроме этих модулей в операционной системе может находиться так называемый

диспетчер прерываний, который координирует работу отдельных обработчиков прерываний.

В начале прерывания использовались в основном для управления процессором устройствами ввода-вывода. Затем прерывания стали использовать для организации внутренней работы ЭВМ. В соответствии с этим существуют следующие типы прерываний:

1. Аппаратные прерывания – прерывания от устройств компьютера.

2. Программные прерывания – прерывания, которые вырабатывают процессы, находящиеся на стадии выполнения.

3. Логические прерывания – Эти прерывания вырабатывает сам процессор, когда встречается с каким-либо необходимым условием:

1. деление на 0

2. переполнение регистров микропроцессора

3. пошаговое выполнение программ

4. режим контрольных точек.

 

Каждое прерывание имеет два параметра:

1. Номер прерывания

2. Вектор прерывания.

 

Вектор прерывания– это адрес ячейки памяти, где хранится программа – обработчик прерывания.

Прерывания обозначаются — IRQ.

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

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

Диспетчеризация сводится к следующему:

· сохранение контекста текущего потока, который требуется сменить;

· загрузка контекста нового потока, выбранного в результате планирования;

· запуск нового потока на выполнение.

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

Уровни запросов программных прерываний

Хотя контроллеры прерываний различают уровни приоритетов прерываний, Windows использует свою схему приоритетов прерываний, известную под названием уровни запросов прерываний (interrupt request levels, IRQL). Внутри ядра IRQL представляются в виде номеров 0-31 в системах x86 и 0-15 в системах x64 и IA64, причем больший номер соответствует прерыванию с более высоким приоритетом. Ядро определяет стандартный набор IRQL для программных прерываний, a HAL увязывает IRQL с номерами аппаратных прерываний. IRQL, определенные для архитектуры x86, показаны на рис. 3–3, а аналогичные сведения для архитектур x64 и IA64 — на рис. 3–4.

 

ПРИМЕЧАНИЕ Уровень SYNCH_LEVEL, используемый многопроцессорными версиями ядра для защиты доступа к индивидуальным для каждого процессора блокам PRCB (processor control blocks), не показан на этих схемах, так как его значение варьируется в разных версиях Windows. Описание SYNCH_LEVEL и его возможных значений.

 

Рис. 3–4. Уровни запросов прерываний (IRQL) в системах x64 и IA64

 

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

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

Значение IRQL определяет, какие прерывания может получать данный процессор. IRQL также используется для синхронизации доступа к структурам данных режима ядра (о синхронизации мы поговорим позже). При выполнении поток режима ядра повышает или понижает IRQL процессора либо напрямую (вызовом соответственно KeRaiseIrql или KeLowerIrqL), либо — что бывает гораздо чаще — опосредованно (через функции, которые обращаются к синхронизирующим объектам ядра). Как показано на рис.3–5, прерывания от источника с IRQL, превышающим текущий уровень, прерывают работу процессора, а прерывания от источников, IRQL которых меньше или равен текущему уровню, маскируются до тех пор, пока выполняемый поток не понизит IRQL.

 

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

Поток режима ядра повышает и понижает IRQL процессора, на котором он выполняется, в зависимости от того, что именно делает этот поток. Например, обработчик ловушки (или сам процессор) при прерывании повышает IRQL процессора до IRQL источника прерывания. B результате все прерывания с более низким или равным IRQL маскируются (только на этом процессоре), что не дает прерыванию с таким же или более низким IRQL помешать процессору обработать текущее прерывание. Замаскированные прерывания либо обрабатываются другим процессором, либо откладываются до понижения IRQL. Поэтому все системные компоненты, в том числе ядро и драйверы устройств, пытаются удерживать IRQL на уровне passive («пассивный»), иногда называемом низким уровнем. Если бы IRQL долго оставался неоправданно высоким, драйверы устройств не смогли бы оперативно реагировать на аппаратные прерывания.

megaobuchalka.ru

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

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

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

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

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

Чтобы не пытаться объять необъятное, я не буду рассматривать:
  • Процессоры с экзотическими архитектурами (стековыми, потоковыми, асинхронными и так далее), потому что их доля на рынке весьма мала, а в качестве примера логичнее использовать распространенную архитектуру. RISC я выбрал исключительно по религиозным соображениям
  • Многоядерные процессоры, потому что каждое процессорное ядро обрабатывает свои прерывания независимо от других ядер
  • Суперскалярные, многопоточные и VLIW процессоры, потому что с точки зрения организации прерываний они похожи на скалярные процессоры (хотя, разумеется, гораздо сложнее).

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

Итак, процессор — это устройство, выполняющее последовательность команд (программу) для решения некоторой задачи. Для каждой команды, в свою очередь, процессор должен выполнить последовательность операций, называемую циклом команды (instruction cycle) и состоящую из следующих этапов:

  1. Выборка команды из памяти
  2. Декодирование команды
  3. Исполнение команды
  4. Запись результатов в регистры и/или память

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

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

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

  • счетчика команд процессора (program counter, он же instruction pointer)
  • регистров процессора (общего назначения, статусных, флагов и так далее)
  • оперативной памяти

В системах реального времени необходимо также учитывать влияние кэш-памяти, буферов ассоциативной трансляции MMU (translation lookaside buffer, TLB) и таблиц динамического предсказания переходов.

Каждая выполненная команда каким-то образом обновляет состояние процесса:

  • арифметические и логические команды обновляют содержимое регистров и счетчика команд
  • команды перехода обновляют содержимое счетчика команд и таблицы динамического предсказания переходов
  • команды загрузки обновляют содержимое регистров, счетчика команд и кэш-памяти (при промахе кэша; если потребуется замещение линии кэша — то еще и оперативной памяти)
  • команды сохранения обновляют содержимое оперативной памяти (или кэш-памяти) и счетчика команд

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

В зависимости от источника прерывания оно может быть:

  1. Внутренним, если вызвано выполнением команды в процессоре:
    • Программным (software interrupt), если вызвано специальной командой
    • Исключением (exception, fault, abort – это все оно), если вызвано ошибкой при выполнении команды

  2. Внешним, если вызвано произошедшим снаружи процессора событием

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

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

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

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

Формально прерывание называется точным, если выполнены все перечисленные ниже условия:
  1. все команды, предшествующие прерываемой, были полностью выполнены и корректно сохранили состояние процесса
  2. все команды, следующие за прерываемой, не были выполнены и ни коим образом не изменили состояние процесса
  3. прерываемая команда, в зависимости от типа прерывания, либо была полностью выполнена, либо не была выполнена вовсе

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

Очевидно, что внешние прерывания должны быть точными всегда. Кому нужен процессор, который не может корректно восстановить процесс после обработки прерывания от таймера?

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

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

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

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

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

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

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

Чтобы понять, где же должна быть расположена точка фиксации результатов, полезно вспомнить этапы цикла команды:

  1. Выборка команды из памяти
  2. Декодирование команды
  3. Исполнение команды
  4. Запись результатов в регистры и/или память

По определению, она должна находиться до записи результатов, но к этому моменту уже должно быть известно, вызвала команда исключение или нет. Исключение может произойти на любом из четырех этапов, например:
  1. ошибка памяти при выборке команды
  2. неизвестный код операции при декодировании
  3. деление на ноль при исполнении
  4. ошибка памяти при записи результатов

Очевидно, что реализация точных прерываний невозможна до тех пор, пока не решена проблема записи результатов в память:
  • нельзя фиксировать команду и разрешать ей записывать результаты в память до тех пор, пока не станет ясно, что команда не вызвала исключение
  • нельзя узнать, что исключение не вызвано, не записав результаты в память (для этого нужно получить подтверждение от контроллера памяти, что запись произведена успешно)

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

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

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

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

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

Для этого результаты каждого этапа, кроме последнего, сохраняются во вспомогательных элементах памяти (регистрах), расположенных между этапами:

  1. Результат выборки — закодированная команда — сохраняется в регистре, расположенном между этапами выборки и декодирования
  2. Результат декодирования — тип операции, значения операндов, адрес результата — сохраняются в регистрах между этапами декодирования и исполнения
  3. Результаты исполнения — новое значение счетчика команд для условного перехода, вычисленный в АЛУ результат арифметической операции и так далее — сохраняются в регистрах между этапами исполнения и записи результатов
  4. На последнем этапе результаты и так записываются в регистры и/или память, поэтому никакие вспомогательные регистры не нужны.

Вот так работает получившийся конвейер:
Такт  СК    Выборка   Декодирование   Исполнение   Запись_результатов
1     0x00  Команда1  -               -            -
2     0x04  Команда2  Команда1        -            -
3     0x08  Команда3  Команда2        Команда1     -
4     0x0C  Команда4  Команда3        Команда2     Команда1            
5     0x10  Команда5  Команда4        Команда3     Команда2            

Обратите внимание на столбец СК («счетчик команд»). Его значение меняется каждый такт и определяет адрес в памяти, откуда выбирается команда.
Внимательный читатель уже заметил небольшую неувязочку — для обеспечения точности прерываний первая команда не имеет права изменить счетчик команд раньше четвертого такта. Чтобы это исправить, мы должны перенести счетчик команд за точку фиксации результата (предположим, что она находится между третьим и четвертым этапами):
Такт  Выборка   Декодирование  Исполнение  Запись_результатов  СК
1     Команда1  -              -           -                   0х00
2     -         Команда1       -           -                   0х00
3     -         -              Команда1    -                   0х00
4     Команда2  -              -           Команда1            0х04
5     -         Команда2       -           -                   0х04

Производительность процессора немного упала, не так ли? На самом деле, решение лежит на поверхности – нам нужно два счетчика команд! Один должен находиться в начале конвейера и указывать, откуда читать команды, второй – в конце, и указывать на ту команду, которая должна быть зафиксирована следующей.
Первый называется «спекулятивным», второй – «архитектурным». Чаще всего спекулятивный счетчик команд не существует сам по себе, а встроен в предсказатель переходов. Выглядит это вот так:
Такт  ССК   Выборка   Декодирование  Исполнение  Запись_результатов  АСК
1     0x00  Команда1  -              -           -                   0х00
2     0x04  Команда2  Команда1       -           -                   0х00
3     0x08  Команда3  Команда2       Команда1    -                   0х00
4     0x0C  Команда4  Команда3       Команда2    Команда1            0х04
5     0x10  Команда5  Команда4       Команда3    Команда2            0х08

Дальше происходит вот что. Команда, перемещаясь между этапами, тащит за собой адрес, из которого она была выбрана (то есть ее ССК). Перед точкой фиксации результата процессор смотрит, не пришло ли внешнее прерывание, не вызвала ли команда исключение, а также сравнивает ее адрес с АСК:
  • Если пришло внешнее прерывание, команда коммитится, но адрес следующей команды записывается не в АСК, а в РАВ. В АСК записывается адрес вектора прерывания.
  • Если возникло исключение, команда не коммитится, вместо этого в АСК записывается адрес вектора соответсвующего исключения, а адрес команды записывается в РАВ.
  • Если адрес команды не равен АСК, она тоже не коммитится (об этом позже). Если адрес равен АСК и исключения не произошло – процессор фиксирует команду и обновляет АСК (записывает адрес перехода в случае команды ветвления или просто инкрементирует в случае другой команды)

Почему адрес команды может быть не равен АСК? Возьмем мой любимый пример: процессор только что включили, и он выбирает первую команду из таблицы прерываний, которая является ни чем иным как командой перехода в далекую даль (по адресу 0х1234):
Такт  ССК     Выборка      Декодирование  Исполнение   Запись_результатов  АСК
1     0x00    jump 0x1234  -              -            -                   0х00
2     0x04    Команда2     jump 0x1234    -            -                   0х00
3     0x08    Команда3     Команда2       jump 0x1234  -                   0х00
4     0x0C    Команда4     Команда3       Команда2     jump 0x1234         0х1234
*** Для Команды2 на четвертом такте ее адрес (0х04) не равен АСК, потому что переход был предсказан неверно***
5     0x1234  Команда666   -              -            -                   0х1234
6     0x1238  Команда667   Команда666     -            -                   0х1234
7     0x1240  Команда668   Команда667     Команда666   -                   0х1234
8     0x1244  Команда669   Команда668     Команда667   Команда666          0х1238

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

Желающим усугубить взрыв мозга рекомендую ознакомиться с Implementation of precise interrupts in pipelined processors. Да-да, ваш новейший Интел Кор Ай Семь работает именно так, как описано в этой статье двадцатипятилетней давности. Добро пожаловать в восьмидесятые!

habr.com

Программные прерывания — это… Что такое Программные прерывания?


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

Программное прерывание — синхронное прерывание, которое может осуществить программа путем выполнения команды INT. BIOS, хранящиеся в ПЗУ, и прикладные программы IBM PC используют другие прерывания, с большими или меньшими номерами. Это распределение номеров прерываний условно и никаким образом не закреплено аппаратно. Синхронные прерывания происходят не в результате внешних событий, а при выполнении команд процессора. Например, синхронное прерывание происходит при попытке выполнить деления на ноль.

См. также

Wikimedia Foundation. 2010.

  • Шведское имя
  • Сутупово

Смотреть что такое «Программные прерывания» в других словарях:

  • Прерывания — Прерывание (англ. interrupt)  сигнал, сообщающий процессору о наступлении какого либо события. При этом выполнение текущей последовательности команд приостанавливается, и управление передаётся обработчику прерывания, который выполняет работу по… …   Википедия

  • Вектор прерывания — Прерывание (англ. interrupt)  сигнал, сообщающий процессору о наступлении какого либо события. При этом выполнение текущей последовательности команд приостанавливается, и управление передаётся обработчику прерывания, который выполняет работу по… …   Википедия

  • Обработчик прерывания — Эту статью следует викифицировать. Пожалуйста, оформите её согласно правилам оформления статей. Обработчик прерываний (или процедура обслуживания прерываний)  специальная процедура, вызываемая по прерыванию для выполнения его обработки.… …   Википедия

  • Прерываний система —         в ЦВМ, аппаратные и программные средства, обеспечивающие временное прекращение выполнения последовательности команд для перехода к выполнению др. последовательности команд или для возвращения к ранее прерванной программе. П. с. позволяет… …   Большая советская энциклопедия

  • Прерывание — (англ. interrupt)  сигнал, сообщающий процессору о наступлении какого либо события. При этом выполнение текущей последовательности команд приостанавливается и управление передаётся обработчику прерывания, который реагирует на событие и… …   Википедия

  • Таблица векторов прерываний — (англ. Interrupt Descriptor Table, IDT) используется в x86 архитектуре и служит для определения корректного ответа на прерывания и исключения. В микропроцессорах 8086/80186 таблица векторов прерываний расположена в первом килобайте памяти… …   Википедия

  • STI — В системе команд x86 совместимых процессоров, инструкция STI  сокращение от «Set Interrupt Enable Flag». Она устанавливает флаг interrupt flag (IF) в регистре EFLAGS, что разрешает процессору обрабатывать асинхронные прерывания от внешних… …   Википедия

  • CLI (x86) — У этого термина существуют и другие значения, см. CLI. В системе команд x86 совместимых процессоров инструкция CLI  сокращение от «Clear Interrupt Enable Flag». Она сбрасывает interrupt flag (IF) в регистре EFLAGS. Когда этот флаг сброшен… …   Википедия

  • Расширители DOS — Расширитель DOS (также дос экстендер  от англ. DOS extender)  технология, позволяющая программам для операционных систем семейства защищённом режиме процессора. Существуют 16 и 32 битные расширители DOS: 16 битные расширители предназначены для… …   Википедия

  • Расширитель DOS — Эта статья или раздел нуждается в переработке. Пожалуйста, улучшите статью в соответствии с правилами написания статей. Расширитель …   Википедия


dic.academic.ru

Прерывания и особые ситуации: Типы прерываний

 

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

В общем случае все эти ситуации разделяются по принципу их обнаружения, источника возникновения и реакции на них на следующие три категории:

  • Прерывания и особые ситуации (Interrupts and Exceptions) — обнаруживаются и обрабатываются процессором в самые различные моменты времени и могут происходить как из внешних так и из внутренних источников, все другие типы экстраординарных ситуаций занимают более низкий уровень иерархии и могут обрабатываться, только если сперва заявят о своем существовании через прерывание или особую ситуацию.
  • Исключительные ситуации FPU (Floating-Point Exceptions) — могут возникать только при выполнении команд сопроцессора, команд MMX или 3DNow!-команд. Возникновение исключительной ситуации FPU, в свою очередь, может вызывать генерацию прерывания через подачу сигнала на специальные внешние выводы процессора (так называемая реакция в стиле MS-DOS) или особой ситуации (внутренний механизм процессора обеспечивает генерацию ошибки сопроцессора #MF), обработчик которой далее сам разбирается с тем, какое исключение произошло и какие действия в связи с этим следует предпринять (подробнее …).
  • Исключительные ситуации SIMD (SSE) (SIMD Floating-Point Exceptions) — могут возникать только при выполнении SIMD-команд и полностью определяются состоянием SIMD-регистров процессора. SIMD-исключения сообщают о своем возникновении через генерацию специальной особой ситуации #XM. Получив управление, обработчик особой ситуации должен сам программным путем определить причину возникновения исключения (подробнее …).

 

Прерывания и особые ситуации

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

Все прерывания и особые ситуации имеют уникальные идентификационные номера. Эти номера называются векторами прерываний и лежат в пределах от 0 до 255. Векторы от 0 до 31 отведены для особых ситуаций и немаскируемого прерывания, причем некоторые из них зарезервированы и не должны использоваться программами. Векторы от 32 до 255 свободны для любого использования пользовательскими программами и внешними устройствами.

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

 

Внешние или аппаратные прерывания (External or Hardware interrupts):

  • Внешние маскируемые прерывания (Maskable Hardware Interrupts) — инициируются сигналами на внешних выводах процессора или с помощью встроенного контроллера прерываний (Advanced Programmable Interrupt Controller – APIC). Для процессоров, начиная с Pentium, встроенный APIC-контроллер является наиболее распространенным способом управления прерываниями. В этом случае выводы LINT[1:0]# программируются через локальную таблицу векторов (LVT) контроллера APIC, которая позволяет назначать соответствующий вывод для приема любого вида прерываний. Если же встроенного контроллера APIC в процессоре нет, или же он отключен, то внешние маскируемые прерывания принимаются на выводе INTR#. При этом номер поступившего прерывания должен передаваться процессору по системной шине специальным внешним контроллером прерываний (например, таким как 8259А). Как правило, встроенный APIC-контроллер также взаимодействует с системным контроллером прерываний (I/O APIC), который обеспечивает прием множества прерываний от разных источников и передает в процессор(ы) информацию о полученном прерывании по системной шине или специальной выделенной шине APIC (APIC serial bus). Если флаг разрешения прерываний не установлен (EFLAGS.IF = 0), то внешние маскируемые прерывания не обрабатываются. Выше указывалось, что для использования маскируемыми прерываниями предназначены векторы от 32 до 255. Технически, однако, возможно определить любому маскируемому прерыванию, которое принимается на выводе INTR# процессора значение вектора в диапазоне от 0 до 255, а если прием прерываний происходит через встроенный APIC-контроллер – в диапазоне от 16 до 255 (при попытке использования векторов от 0 до 15 APIC-контроллер сигнализирует о некорректном векторе прерывания).
  • Внешние немаскируемые прерывания (Nonmaskable External Interrupts) — принимаются на выводе NMI# процессора или внутренней шине APIC-контроллера, механизм запрета немаскируемых прерываний отсутствует (на них не влияет текущее значение флага EFLAGS.IF). Получив запрос на немаскируемое прерывание, процессор передает управление по вектору 2 и блокирует прием новых запросов на немаскируемые прерывания вплоть до выполнения команды IRET/IRETD. Технически вектор прерывания 2 может использоваться и для обрабтки маскируемых прерываний, но только описанный выше способ поступления запросов на немаскируемые прерывания обеспечивает особое поведение процессора при их обработке. 

 

Программные прерывания и особые ситуации (Software interrupts and Exceptions):

  • Генерируемые процессором особые ситуации (Program-Error Exceptions) — возникают в процессе и по результатам выполнения программного кода, разделяются на ошибки, ловушки и сбои. Каждая особая ситуация генерируется по набору определенных условий и ей соответствует строго определенный вектор прерывания в диапазоне от 0 до 31. Зачастую, при генерации таких особых ситуаций процессор сохраняет в стеке не только адрес возврата из прерывания, но и специальный код ошибки, который позволяет обработчику детально понять причину ошибки, внести коррективы и перезапустить команду, если это возможно. Для обозначения особых ситуаций принято использовать специальные мнемонические обозначения (#DE, #DB и т.д.). Полный перечень всех особых ситуаций, поддерживаемых разными моделями процессоров, приведен в Таблице 3.1.
  • Программные прерывания и особые ситуации (Software-Generated Interrupts and Exceptions) — могут быть вызваны командами INTO, INT 3, INT01, INT n, BOUND. При этом только команду INT n правильно относить к командам вызова программных прерываний (Software-Generated Interrupts). Команды INTO, INT 3, BOUND и INT01 по сути являются командами программной генерации особых ситуаций (Software-Generated Exceptions). Например, команда INT 40 генерирует прерывание, передавая управление по вектору номер 40, а команда BOUND edi, [ecx] может сгененрировать особую ситуацию нарушение границ (#BR). В качестве непосредственного операнда команды INT n могут использоваться любые вектора прерываний от 0 до 255. То есть она может использоваться, в том числе, и для программной эмуляции любых особых ситуаций с векторами от 0 до 31. В этом случае, однако, не происходит записи в стек кода ошибки (как это может иметь место в случае генерации особой ситуации аппаратными средствами контроля функционирования процессора). Обработчик особой ситуации, который предполагает, что процессор всегда генерирует и помещает в стек предусмотренный код ошибки, не сможет правильно обработать программный вызов командой INT n, так как выберет из стека некорректное значение адреса возврата для указателя команд EIP. Кроме того, существуют определенные отличия в обработке программных прерываний и программных особых ситуаций в режиме V86.
  • Особые ситуации генерируемые средствами самопроверки процессора (Machine-Check Exceptions) — реализованы в процессорах, начиная с Pentium. Условия генерации и типы таких особых ситуаций зависят от модели процессора. Для их обработки используется вектор прерывания 18.

 

Таблица 3.1. Типы прерываний и особых ситуаций

Название

Номер

Мне-мо-ника

Тип

Источник возникновения

Генери-руется ли код ошибки?

Процессор, в котором впервые появилось

Деление на нуль

0

#DE

Ошибка

Команды DIV, IDIV и команда AAM с нулевым непосредственным операндом

Нет

8086

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

1

#DB

Ошибка/Лов.

Любые

Нет

8086

Немаскируемое прерывание

2

NMI

Прер.

Сигнал на выводе NMI# или внутренней шине APIC-контроллера

Нет

8086

Точка останова

3

#BP

Лов.

Команда INT3

Нет

8086

Переполнение

4

#OF

Лов.

Команда INTO

Нет

8086

Нарушение границ

5

#BR

Ошибка

Команда BOUND

Нет

80186

Неопределенный код операции

6

#UD

Ошибка

Команда UD2, зарезервированные коды операций, некорректные команды

Нет

Intel286

Сопроцессор отсутствует

7

#NM

Ошибка

Команды FPU, команда WAIT/FWAIT; команды MMX, 3DNow!, SIMD, когда CR0.TS = 1 и CR0.EM = 0

Нет

Intel286

Двойная ошибка

8

#DF

Сбой

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

Да (всегда нуль)

Intel286

Превышение сегмента сопроцессором – в Intel486 … зарезервировано

9

 

Сбой

Команды FPU, обращающиеся к памяти

Нет

только Intel286 Intel386

Неправильный TSS1

10

#TS

Ошибка

Переключение задачи или доступ к сегменту TSS.

Да

Intel286

Сегмент не присутствует1

11

#NP

Ошибка

Загрузки сегментных регистров или попытки доступа к системным сегментам (код, данные, стек, LDT, TSS)

Да

Intel286

Ошибка стека

12

#SS

Ошибка

Стековые операции и загрузки сегментного регистра SS

Да

Intel286

Общая защита

13

#GP

Ошибка/Лов.

Любые ссылки на код или данные и иные операции, предусмотренные механизмом защиты

Да

Intel286

Страничная ошибка1

14

#PF

Ошибка

Любые ссылки на код или данные в памяти

Да (спец. формат)

Intel386

Зарезервировано

15

 

 

 

 

 

Ошибка сопроцессора

16

#MF

Ошибка

Команды FPU, команда WAIT/FWAIT,команды MMX, 3DNow!, SIMD при наличии отложенных исключений FPU, когда CR0.TS = 0 и CR0.EM = 0

Нет

Intel286

Контроль выравнивания1

17

#AC

Ошибка

Любые невыровненные ссылки на данные в памяти, если активирован контроль выравнивания (CR0.AM = 1, EFLAGS.AC  = 1,  CPL  = 3)

Да (всегда нуль)

Intel486

Контроль машины

18

#MC

Сбой

Зависит от модели

Зависит от модели

Penium

SIMD-исключение

19

#XM

Ошибка

Команды SIMD (SSE)

Нет

Pentium III

Зарезервировано

20-31

 

 

 

 

 

Прерывания пользователя

32-255

Прер.

Внешние прерывания или команда INT n

Нет

8086

 

1. Указанные особые ситуации генерируются только в защищенном режиме и режиме V86.

             

 

Типы особых ситуаций и особенности их обработки

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

  • Ошибка (Fault) — это особая ситуация, которая может быть исправлена обработчиком особой ситуации. При встрече ошибки состояние процессора сохраняется в том виде, каким оно было до начала выполнения команды, инициировавшей генерацию ошибки, а значения CS:EIP, указывающие на эту команду сохраняются в стеке обработчика. Прерванная программа после исправления ошибки может быть продолжена непосредственно с команды, вызвавшей эту ошибку.
  • Ловушка (Trap) — особая ситуация, которая генерируется после выполнения соответствующей команды. В этом случае сохраняемые в стеке значения CS:EIP, указывают на команду, которая будет выполняться вслед за командой, вызвавшей ловушку; например, если ловушка произошла во время команды JMP, то сохраненные значения CS:EIP указывают на команду, являвшуюся целью команды JMP.
  • Сбой (Abort) — это особая ситуация, которая не допускает точную локализацию вызвавшей ее команды и не допускает перезапуска. Сбои используются для сообщений о некоторых ошибках, таких как: технические неисправности и наличие некорректных значений в системных таблицах.

 

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

 

Таблица 6.55. Способы обработки процессором прерываний и особых ситуаций в различных режимах работы

Режим работы процессора

Тип прер. CR0.PE VM CR4.VME CR4.PVI IOPL IRB IDT IVT

Режим реальной адресации

П 0 0 x x 0 x +
А, О 0 0 x x 0 x +

Защищенный режим

П 1 0 x 0 x x +
А, О 1 0 x 0 x x +

Защищенный режим с поддержкой виртуальных флагов прерываний
(CR4.PVI = 1)

П 1 0 x 1 x x +
А, О 1 0 x 1 x x +
Режим V86 (CPL = 3, CR4.VME = 0) П 1 1 0 x = 3 x +
П
1 1 0 x < 3 x #GP(0)
А, О1 1 1 0 x x x +

Режим EV86 (CPL = 3, CR4.VME = 1)

П 1 1 1 x x 0 +
П 1 1 1 x = 3 1 +
П
1 1 1 x < 3 1 #GP(0)
А, О1 1 1 1 x x x +

x — Флаг или битовое поле может иметь любое значение.

+ — Возможное событие (переход к обработчику прерывания в соответствующем режиме).

1 — Включая особые ситуации, генерируемые командами INTO, INT 3, INT01, BOUND.

 

 

< Предыдущая   Следующая >

www.club155.ru

INT n — Вызов процедуры прерывания

Команда INT n обеспечивает программную генерацию прерывания и передачу управления в соответствующий обработчик. Непосредственный операнд команды может принимать значения от 0 до 255, что задает вектор (индекс) вызываемого прерывания по таблице дескрипторов прерываний (IDT) или по таблице векторов прерываний (IVT). При этом первые 32 вектора прерываний (от 0 до 31) зарезервированы Intel для системного использования. Большинство из них используется для обработки генерируемых процессором особых ситуаций. Команда INT n предназначена для программной генерации прерываний с номерами от 32 до 255, но технически возможно вызвать и прерывания с меньшими (любыми) векторами. В этом случае процессор ведет себя не так, как это описано для соответствующего внешнего прерывания или особой ситуации, а так, как это предусмотрено именно для генерируемых программно прерываний пользователя (то есть не записываются никакие коды ошибок, не соблюдаются приоритеты и т.п.).

Команда INT n в общем ведет себя как дальний вызов командой CALL, за исключением того, что регистр флагов помещается в стек перед адресом возврата. Процедуры обработки прерываний завершаются командой IRET/IRETD, которая выбирает из стека флаги и адрес возврата. В деталях же реакция процессора на поступление программного прерывания определяется его текущим режимом работы (см. Прерывания и особые ситуации: Реальный режим, Защищенный режим, Режим V86).

В режиме реальной адресации таблица векторов прерываний (IVT), которую иногда называют просто таблицей прерываний, является массивом 4-байтных дальних указателей (см. рис. 3.1). Конечным результатом исполнения команды INT n в этом случае и является переход (передача управления) по заданному адресу. Процессор автоматически выбирает из памяти соответствующий указатель, затем сохраняет в стеке текущие (16-битные) значения FLAGS, CS, IP (см. рис. 3.2.), очищает биты IF, TF, AC регистра FLAGS и передает управление по считанному адресу. Линейный базовый адрес таблицы прерываний определяется содержимым регистра IDTR. Первоначальное значение IDTR при сбросе процессора или переходе в режим реальной адресации является нулевым.

 

Рис. 3.1. Таблица векторов прерываний в реальном режиме работы

 

Рис. 3.2. Стек после вызова прерывания или особой ситуации в реальном режиме работы

 

В защищенном режиме таблица дескрипторов прерываний (IDT) состоит из множества 8-байтных дескрипторов (см. рис. 3.3). Дескрипторы в таблице могут иметь разные типы. Допустимы: шлюз прерывания, шлюз ловушки или шлюз задачи. При поступлении команды прерывания процессор сохраняет текущие значения EFLAGS, CS и EIP в стеке. Когда передача управления в обработчик связана с изменением уровня привилегий на более высокий (меньшее числовое значение), это сопровождается переключением стека. Новые значения для SS и ESP берутся из TSS текущей задачи в соответствии с уровнем привилегий обработчика. Переключение стека производится до сохранения указанных выше регистров, причем в новом стеке в первую очередь сохраняются значения SS и ESP, указывающие на старый стек (см. рис. 6.10.).

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

При передаче управления через шлюз прерывания или шлюз ловушки процессор сбрасывает флаги EFLAGS.TF, EFLAGS.RF и EFLAGS.NT, чтобы отключить трассировку прерываний и некоторые особенности обработки вложенных задач. Кроме того, при использовании шлюза прерывания сбрасывается также и флаг разрешения прерываний EFLAGS.IF.

В защищенном режиме (как в и режиме реальной адресации) линейный базовый адрес таблицы IDT определяется содержимым регистра IDTR (см. рис. 3.3).

 

Рис. 3.3. Таблица дескрипторов прерываний (IDT) в защищенном режиме работы

 

Рис. 6.10. Стек после вызова программного прерывания в защищенном режиме

 

При поступлении программного прерывания в режиме V86 процессор автоматически переключается в защищенный режим с уровнем привилегий CPL = 0 (в связи с этим происходит замена указателя стека SS:ESP на стек нулевого уровня привилегий из сегмента состояния задачи TSS) и обращается к соответствующему дескриптору прерывания в таблице IDT. Если в качестве такого дескриптора используется 32-битный шлюз ловушки или шлюз прерывания, то он должен указывать на несогласованный кодовый сегмент с нулевым уровнем привилегий (уровень привилегий DPL соответствующего шлюза должен быть установлен в 3, а CPL процедуры обработки прерывания должен быть равен 0, чтобы обеспечить обработку прерывания защищенного режима на нулевом уровне привилегий), в противном случае генерируется ошибка общей защиты (#GP) с селектором этого сегмента в коде ошибки.

После переключения на нулевой уровень привилегий и загрузки новых значений указателя стека SS:ESP процессор сохраняет в стеке текущие значения сегментных регистров GS, FS, DS, ES, а затем очищает эти регистры, помещая в них нулевые селекторы. Далее действия процессора идентичны процедуре обработки прерываний защищенного режима — он сохраняет в стеке старые значения SS, ESP, EFLAGS, CS и EIP, сбрасывает флаги VM, NT, TF, RF и IF (последний сбрасывается, только если используется шлюз прерывания) регистра флагов EFLAGS и передает управление в обработчик прерывания (см. рис. 6.11.).

Если при вызове программных прерываний командой INT n текущий уровень привилегий ввода/вывода EFLAGS.IOPL меньше текущего уровня привилегий задачи (то есть EFLAGS.IOPL < 3), то генерируется ошибка общей защиты (#GP) и управление передается в обработчик этой ошибки. Такое поведение называется IOPL-чувствительностью и в режиме V86 характерно также для команд CLI, STI, POPF/POPFD, PUSHF/PUSHFD, IRET/IRETD.

При возврате из прерывания по завершении работы программы обработчика защищенного режима процессор получает команду IRETD. Последовательно загружая сохраненные в стеке значения, он считывает и значение флага EFLAGS.VM. При обнаружении единицы в этом флаге производится обратное переключение в режим V86 и восстановление из стека сегментных регистров ES, DS, FS, GS, после чего управление передается в прерванную программу. Если команда IRETD будет получена на уровне привилегий, отличном от нуля, то процессор не сможет установить флаг EFLAGS.VM – и соответственно – не переключится в режим V86 — возврат будет произведен некорректно.

 

Рис. 6.11. Стек после вызова прерывания в режиме V86

 

В процессорах, начиная с Pentium, реализуются специальные расширения системы обработки прерываний в защищенном режиме и режиме V86. Это так называемые виртуальные прерывания защищенного режима (включаются при CR4.PVI = 1) и режим EV86 (включается при CR4.VME = 1). В защищенном режиме активация виртуальных прерываний никак не влияет на порядок обработки программных прерываний и особых ситуаций, генерируемых командой INT n (равно как и командами INTO, INT 3, INT01, BOUND). А вот в режиме EV86 включается совершенно другой механизм обработки программных прерываний, который подразумевает наличие четырех разных режимов, определяемых текущими установками флагов и управляющих параметров процессора и конкретной задачи V86:

 

Указанные режимы действуют только для команд генерации программных прерываний INT n, имеющих двухбайтный код операции (CD ib). Однобайтные команды INTO, INT 3, INT01, а также команда BOUND относятся к командам генерации программных особых ситуаций и на них не распространяется действие расширений режима EV86.

 

Таблица 6.54. показывает, какое действие из нижней части таблицы происходит в случае выполнения условий из верхней части таблицы. Каждый символ «+» в нижней части указывает на то, что выполняется соответствующая процедура, как это определено в подпункте Операция, в противном случае имеет место ошибка общей защиты #GP(0).

 

Таблица 6.54. Возможные варианты обработки прерываний
(согласно подпункта Операция)

www.club155.ru

Процедуры и прерывания. Использование стека при вызове процедур и прерываний

Часть 1. Первоначальные сведения об ассемблере и разработке программ для процессора 8086

Глава 3. Загрузка и выполнение программ в DOS

Использование DOS сегментной организации памяти для загрузки и выполнения программ

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

Для того чтобы адресовать одновременно два сегмента данных, например при выполнении операции пересылки из одной области памяти в другую, можно использовать регистр дополнительного сегмента данных ES. Кодовый сегмент и сегмент стека всегда определяются содержимым своих регистров (CS и SS), и поэтому в каждый момент выполнения программы всегда используется какой-то один кодовый сегмент и сегмент стека. Причем если переключение кодового сегмента – довольно простая операция, то переключать сегмент стека можно только при условии четкого представления логики работы программы со стеком, иначе это может привести к зависанию системы.

Все сегменты могут использовать различные области памяти, а могут частично или полностью совпадать (рис. 3.1).

Кодовый сегмент должен обязательно описываться в программе, все остальные могут отсутствовать. В этом случае DOS при загрузке программы в оперативную память инициализирует регистры DS и ES значением адреса префикса программного сегмента PSP (Program Segment Prefics) – специальной области оперативной памяти размером 256 (100h) байт. PSP может использоваться в программе для определения имен файлов и параметров из командной строки, введенной при запуске программы на выполнение, объема доступной памяти, переменных окружения системы и т.д. Регистр SS при этом инициализируется значением сегмента, находящегося сразу за PSP, т.е. первого сегмента программы. При этом необходимо учитывать, что стек «растет вниз», иными словами при помещении в стек каких-либо значений содержимое регистра SP, указывающего на вершину стека, уменьшается на 2, а при считывании из стека – на столько же увеличивается (цифра 2 связана с тем, что стек работает со словами, а не с байтами). Таким образом, при помещении в стек каких-либо значений они могут затереть PSP и программы, находящиеся в младших адресах памяти (например, DOS), что может привести к непредсказуемым последствиям. Поэтому рекомендуется всегда явно описывать сегмент стека в тексте программы, задавая ему размер, достаточный для нормальной работы.



В примере программы HELLO из главы 1 в тексте программы явно описаны два сегмента – кода с именем CODE и данных с именем DATA. Директива ASSUME связывает имена этих сегментов, которые в общем случае могут быть произвольными, с сегментными регистрами CS и DS соответственно (рис.3.2).

Как видно из рисунка, сегмент стека в данном случае установлен на конец PSP, что при его интенсивном использовании могло бы привести к неожиданным результатам (именно по этой причине компоновщик выдал предупреждение Warning: No stack).

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

DS после загрузки программы установлен на начало PSP, поэтому для его использования в первых двух командах программы выполняется загрузка DS значением сегмента данных программы.

 

 

Рис.3.2. Состояние сегментных регистров после загрузки программы HELLO.

 

Процедуры и прерывания. Использование стека при вызове процедур и прерываний

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

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

Ассемблер поддерживает применение процедур. Процедуры в программах на ассемблере могут быть двух типов – ближнего (near) и дальнего (far).

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

При вызове процедуры в стеке сохраняется адрес возврата в вызывающую процедуру:

• при вызове ближней процедуры – слово, содержащее смещение точки возврата относительно текущего кодового сегмента;

• при вызове дальней процедуры – слово, содержащее адрес сегмента, в котором расположена точка возврата, и слово, содержащее смещение точки возврата в этом сегменте.

На рис. 3.3 приведен текст программы HELLO с изменениями, реализующими модульный принцип разработки программ. Как видно из текста программы, теперь она оформлена в виде двух процедур – главной процедуры с именем Main, имеющей тип far, и процедуры вывода сообщения на экран WriteMsg типа near.

 

 

AStack SEGMENT STACK

DW 12 DUP(?)

AStack ENDS

AData. SEGMENT

Hello DB ‘Здравствуйте !$’

AData ENDS

ACode SEGMENT

ASSUME CS: ACode, DS: AData, SS: AStack

WriteMsg PROC NEAR

mov AH, 9

int 21h

ret

WriteMsg ENDP

Main PROC FAR

push DS

sub AX, AX

ush AX

mov AX, AData

mov DS, AX

mov DX, OFFSET Hello

call WriteMsg

ret

Main ENDP

ACode ENDS

END Main

 

Рис. 3.3. Программа HELLO, построенная по модульному принципу.

 

Появление трех новых операторов в главной процедуре (push DS – помещение в стек содержимого регистра DS; ub АХ,АХ – вычитание содержимого регистра АХ из содержимого регистра АХ, т.е. обнуление AX; push АХ – помещение в стек регистра АХ, т.е. 0) объясняется именно оформлением программы в виде процедур. В конце каждой процедуры находится команда ret, выполнение которой приводит к тому, что из стека восстанавливается адрес точки возврата (одно или два слова, в зависимости от типа процедуры) и по этому адресу осуществляется передача управления.

При вызове процедуры WriteMsg оператором call WriteMsg в стек помещается слово (так как WriteMsg имеет ближний тип), являющееся смещением следующей команды относительно текущего сегмента (в данном случае это команда ret процедуры Main), после чего указатель стека SP автоматически уменьшается на 2, а управление передается первому оператору процедуры WriteMsg (mov АН,9). При выполнении в процедуре WriteMsg команды ret все операции повторяются в обратном порядке: из стека восстанавливается слово, являющееся смещением точки возврат,] относительно кодового сегмента, и по этому адресу выполняется передача управления. При этом содержимое SP увеличивается на 2.

В процедуре же Main выполнение команды ret приводит к восстановлению из стека двух слов, адресующих точку передачи управления. Поскольку в начале процедуры Main в стек были помещены значения регистра DS в момент инициализации программы И 0, а начальное значение регистра DS устанавливается в DOS равным адресу PSP, эти два слова, восстанавливаемые из стека, есть не что иное, как адрес начала PSP, куда и передается управление программой. Первые два байта PSP, в свою очередь, представляют команды, принуждающие программу возвратиться в DOS, поэтому выполнение программы завершается. При помещении в стек в начале процедуры Main DS и 0 содержимое указателя стека уменьшается на 4, при выполнении командь1 ret – восстанавливается до исходного состояния.

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

call ProcNeme

используются команда типа

int Number,

где Number – номер прерывания, например, int 10, int OAh, int lOlOb и т. п.

Прерывания могут быть аппаратными и программными. Аппаратные прерывания возникают от какого-нибудь устройства компьютера, например от клавиатуры, таймера; дисковода или какого-нибудь нестандартного устройства, подключенного к компьютеру через плату расширения. При возникновении аппаратного прерывания процессор прекращает выполнение текущей программы, сохраняет в стеке текущие значения регистров CS и IP, а также флагового регистра и переходит к выполнению программы обработки прерывания путем помещения в CS: IP адреса этой процедуры» Программы обработки аппаратных прерываний заканчиваются командой IRET, которая всегда выталкивает из стека три слова – старое значение CS, IP и регистра флагов, что приводит к продолжению выполнения прерванной программы с того места, в котором возникло аппаратное прерывание.

Механизм вызова программных прерываний аналогичен вызову аппаратных прерываний, за исключением того, что они вызываются в нужных местах программы с помощью команды INT. При вызове прерывания процессор ищет его адрес, используя номер прерывания, в таблице векторов прерываний (вектор – адрес программы обработки прерывания). Эта таблица начинается по адресу 0000:0000, т.е. с самых младших адресов оперативной памяти, и имеет длину 1024 байта. Учитывая, что векторы прерываний хранятся в этой таблице как 16-битовое смещение и 16-битовый сегмент, и каждый вектор, таким образом, имеет длину 4 байта, размер таблицы достаточен для размещения в ней 256 векторов. Чтобы получить адрес необходимого вектора прерывания, достаточно умножить его номер на 4. Например, вектор прерывания 9 (прерывание, аппаратно вызываемое при нажатии клавиши на клавиатуре) находится по адресу 0000:0024 (24h = 36 = 9 х 4).

В программе HELLO, приведенной на рис.3.3, в процедуре WriteMsg осуществляется вызов прерывания int 21h. Это прерывание называется «Вызов функции DOS» и в качестве параметра получает номер необходимой функции DOS в регистре АН (в данном случае 9 – вывод на дисплей строки, адрес первого символа которой должен находиться в DS:DX).

megaobuchalka.ru

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

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