Процедура обработки прерывания (ISR) 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Процедура обработки прерывания (ISR)



Строки Unicode

 

Ядро Windows хранит строки в формате Unicode. Строки, передаваемые функциям ядра, находятся почти всегда в формате Unicode. Для их представления используется структура UNICODE_STRING

 

Структура UNICODE_STRING:

 

Буфер, на который указывает UNICODE_STRING.Buffer, обычно выделяется из пула подкачиваемой страничной памяти.

 

UNICODE_STRING.Length содержит число байтов (не WCHARS). Завершающий символ UNICODE NULL в это число не включен.

 

 

Команды отладчика для просмотра строк:

  • ‘du’ отображает символы Unicode
  • ‘! du’ отображает физический адрес символов Unicode
  • ‘.enable_unicode’ позволяет/запрещает отладчику напечатать PUSHORTs как строки Unicode
  • ‘dS’ отображает структуру UNICODE_STRING

 


Двусвязные списки

 

Большинство структур данных ядра хранятся как части двусвязных списков. Структура LIST_ENTRY используется для создания и обслуживания таких списков. Список состоит из поля ListHead (тип LIST_ENTRY) и различного числа элементов списка (List Elements, включающих структуру типа LIST_ENTRY).

 

Поля Flink & Blink в структуре LIST_ENTRY указывают на вложенную структуру LIST_ENTRY, а не на List Element.

 

Структуры LIST_ENTRY НИКОГДА не содержат нулевых указателей. Когда список пуст, поля Flink и Blink ListHead указывают непосредственно на ListHead.

 

 


Уровни прерываний (IRQLs)

 

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

 

IRQL - приоритет, связанный с каждым прерыванием. Чем больше IRQL, тем выше приоритет прерывания.

 

Когда процессор обслуживает прерывание, IRQL процессора повышен.

 

Когда процессор находится на определенном IRQL, все последующие прерывания, равные либо ниже данного IRQL, “застревают” на этом же процессоре (в контроллере PIC).

 

На многопроцессорных системах каждый процессор может находиться на своем IRQL. Текущий IRQL процессора сохранен в Области Управления Процессором (KPCR – processor control block).

 

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

 

Код режима ядра может менять IRQL процессора.

 

Код пользовательского режима всегда выполняется на PASSIVE_LEVEL.

 

Таблица приоритетов прерываний для процессоров x86:

 

Уровень IRQL Назначение
Уровни аппаратных прерываний
  HIGH_LEVEL Machine check, NMI. Немаскируемое прерывание. Предназначено для остановки системы и маскирования всех прерываний. Вызывается из KeBugCheckEx.
  POWER_LEVEL Power failure. Предназначено на тот случай, если происходит пропадание электропитания. Реально никогда не использовалось.
  IPI_LEVEL Inter-processor interrupt. Используется для того, чтобы попросить другой процессор выполнить какие-то действия, например, обновить translation look-aside buffer (TLB) cache.
  CLOCK_LEVEL Clock interrupt. Используется для обновления системного времени и учета выделяемого потокам времени.
  PROFILE_LEVEL Profiling timer interrupt. Используется системным таймером для профилирования ядра, если включен такой режим. Собирается информация о выполняемых функциях путем анализа стека.
3 – 26 DEVICE_LEVEL DIRQLx – device interrupts. Уровни устройств. Они назначаются операционной системой. Алгоритм отличается на x86 (one CPU: 27 – IRQ number; multiple CPUs: round-robin в диапазоне IRQL устройств) и x64/IA64 (IRQ number / 16).
Уровни программных прерываний
  DISPATCH_LEVEL/ DPC_LEVEL Dispatch code and DPCs. Уровень программных прерываний. На этом уровне работает планировщик потоков (диспетчер). Повышение IRQL процессора до этого уровня прекращает переключение задач на данном процессоре. При этом планировщик все еще может работать на других процессорах. Обработчики аппаратных прерываний тоже могут работать, потому что их IRQL выше. Блокирование/ожидание на уровне DISPATCH_LEVEL вызывает deadlock. К страничной памяти нельзя получить доступ на уровне IRQL >= DISPATCH_LEVEL. Память, к которой выполняется обращение на уровнях прерываний DISPATCH_LEVEL и выше, должна быть резидентной (non-paged).
  APC_LEVEL APCs and page faults. На уровне APC_LEVEL работает обработчик пробоя страницы. Применяются приоритеты потоков. Каждый поток может независимо устанавливать для себя уровень APC_LEVEL, т.е. этот уровень программных прерываний локален для потока.
  PASSIVE_LEVEL/ LOW_LEVEL Thread switching. Фактически не является уровнем прерывания и существует для полноты. Уровень PASSIVE_LEVEL означает обычную работу процессора при выполнении потока. На этом уровне применяются приоритеты потоков. Если поток выполняет системный вызов, который приводит к переходу из пользовательского режима в режим ядра, IRQL не изменяется.

 

Таблица приоритетов прерываний для процессоров x64:

 

Уровень IRQL
  HIGH_LEVEL/ PROFILE_LEVEL
  POWER_LEVEL/ IPI_LEVEL
  CLOCK_LEVEL
  SYNC_LEVEL
3 – 11 DEVICE_LEVEL
  DISPATCH_LEVEL/ DPC_LEVEL
  APC_LEVEL
  PASSIVE_LEVEL/ LOW_LEVEL

 

Поток, работающий в ядре, может подымать и опускать уровень прерываний с помощью функций KeRaiseIrql() и KeLowerIrql(). Но чаще всего это происходит неявно при работе с объектами синхронизации. При блокировке объекта происходит поднятие уровня до соответствующего объекту значения. При освобождении объекта выполняется снижение уровня. Функции KeRaiseIrql() и KeLowerIrql() реализованы в HAL. В реализации используется оптимизация под названием lazy IRQL.

 

При написании обработчиков прерываний следует учитывать, что на уровнях DPC/Dispatch и выше нельзя выполнять ожидание объектов, если это требует переключения процессора на другой поток. Дело в том, что переключение на другой поток выполняется планировщиком на уровне DPC/Dispatch, который в данном случае замаскирован, и поэтому возникает блокировка. Отсюда следует, что в обработчиках прерываний на уровнях DPC/Dispatch и выше можно работать лишь с физической (non-paged) памятью. Объясняется это тем, что попытка доступа к странице, которой нет в памяти, вызывает прерывание, в ответ на которое менеджер памяти вынужден инициировать подкачку страницы с диска и дождаться завершения операции. Ожидание означает переключение на другой поток через вызов прерывания уровня DPC/Dispatch, но оно замаскировано. Нарушение правила приводит к тому, что система обваливается с кодом IRQL_NOT_LESS_OR_EQUAL. В библиотеке WDK существует программа Driver Verifier, которая позволяет выявить ошибки такого рода. Библиотека WDK определяет допустимый уровень IRQLs для каждой API-функции ядра. Драйверы устройств должны придерживаться этих рекомендаций.

 

IRQL API:

 

KeRaiseIrql()

KeLowerIrql()

KeGetCurrentIrql()

KeRaiseIrqlToDpcLevel()

 


 

Любой код в режиме ядра работает в одном из следующих контекстов:

 

  • Процедура обработки прерывания (ISR)

o Выполняется в DIRQLx в произвольном контексте потока

o Связана с аппаратными прерываниями

 

  • Отложенный вызов процедуры (DPC)

o Выполняется на DISPATCH_LEVEL в произвольном контексте потока

o Выполняет задержанную обработку прерываний на низком IRQL

 

  • Асинхронный вызов процедуры (APC)

o Выполняется на APC_LEVEL или PASSIVE_LEVEL в определенном контексте потока

 

  • Контекст Потока Пользовательского Режима

o Выполняется на PASSIVE_LEVEL в пользовательском режиме в контексте потока

o Потоки, переходившие к режиму ядра для обработки системного вызова

 

  • Контекст Потока Режима Ядра

o NTOSKRNL & Driver Worker Thread Context

§ Выполняется на уровне PASSIVE_LEVEL в контексте системного потока

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

o Контекст рабочего системного потока

§ Выполняется на уровне PASSIVE_LEVEL в контексте системного потока

§ Исполняющие подпрограммы, вызванные системными исполняющими потоками ExpWorkerThread ()

 


Таблица дескрипторов прерываний

(Interrupt Dispatch Table)

 

§ Прерывания сгенерированы:

o Устройствами для уведомления о событии

o Компонентами программного обеспечения для вызовов ОС

§ Исключения происходят, когда выполнение

команд генерирует событие, которое не может быть стандартно обработано ЦП

§ Элементы Таблицы содержат адреса обработчиков прерываний и обработчиков исключений

o Ядро инициализирует IDT

o ЦП ищет IDT и диспетчеризирует обработчики

§ Записи IDT (индексированные векторами) классифицированы как:

o Исключения (0x00 – 0x1f)

§ Синхронны с потоком команд ЦП

§ Примеры: Divide By Zero, Page Fault, Access Violation

o Программные прерывания (0x2a – 0x2e)

§ Прерывания Операционной системы (вызывающие службы ОС)

§ Примеры: int 2e(KiSystemService), int 2A(KiGetTickCount)

o Аппаратные прерывания (0x31 – 0xfd)

§ Асинхронны с потоком команд ЦП

§ Примеры: NIC, RTC, Клавиатура, Звуковые карты, Serial Port, IPI, Profile, APIC Error, ACPI и т.д.

 

 


Единицы работы

 

§ В ОС предусмотрен механизм to execute a custom driver provided

function на PASSIVE_LEVEL в контексте системного потока

§ WORK_QUEUE_ITEM использовался в NT4 и предшествующих

§ IO_WORKITEMs были представлены в Win2K

· Ссылка устанавливается на объект устройства так, чтобы она не была удалена, пока выполняется рабочая единица подпрограммы

 


Пулы памяти

 

§ Выделение динамической памяти в режиме ядра осуществляется из пулов памяти, то есть из хипов в режиме ядра

§ Страничный пул – страничная память

§ Нестраничный пул – page locked физически фрагментированная память

(такой пул используется обработчиками прерываний)

§ Session Pool – особый страничный пул, в котором располагаются данные сессии

§ Специальный пул – страничный/нестраничный с возможностью обнаружения нарушения целостности данных (используется для отладки)

§ Страничные/нестраничные пулы растут до установленного максимума системы

§ Максимальный размер пулов задается при системном запуске в реестре на основании количества физической памяти в системе

§ У системы есть multiple страничные пулы во избежание сериализации вызовов пула

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

§ Это полезно для отладки повреждений и утечек данных пула

 

 

 


Структура данных пула

 

§ Система содержит структуру POOL_DESCRIPTOR для каждого пула

§ Переменная PoolVector содержит массив указателей, которые указывают на дескрипторы страничных/нестраничных пулов

§ Точка входа страничного пула указывает на массив дескрипторов пула

§ Точка входа нестраничного пула указывает на единственный дескриптор пула

 

§ Блокам пула предшествует структура POOL_HEADER которая содержит отслеживаемую информацию о выделенных и свободных блоках

§ POOL_HEADER содержит:

· Тег пула для блока пула

· Указатель EPROCESS, который является квотой, взимаемой за блок пула

· Индекс пула, которому блок принадлежит (только для страничных пулов)

· Размер блока пула и размер предыдущего блока

 

 


Расположение объектов

 

§ Данные объектов хранятся в нестраничном пуле

§ Диспетчер объектов использует известные теги для объектов

§ Может использоваться для идентификации объектов в пуле

§ Например, у объектов файла есть тег 'Файл'

§ Дескрипторы защиты размещаются в страничном пуле

§ Указатель на дескриптор защиты хранится в заголовке объекта

§ Таблица указателей размещается в страничном пуле. Каждый элемент таблицы:

§ описывается типом HANDLE_TABLE_ENTRY

§ содержит указатель на объект и биты Granted Access (с разрешенным доступом)

 


Таблица описания памяти (MDL)

§ Структура данных MDL состоит из:

§ заголовка фиксированной длины (MDL)

§ переменного числа PFN entries

· MDL.Size включает длину списка PFN

§ Буфер описан MDL

§ Не должен запускаться на границе страницы

· MDL.ByteOffset - смещение запускающегося VA с начала первой страницы

§ Длина не должна зависеть от размера страницы

· MDL.ByteCount - фактическое число байтов, описанных MDL

§ MDL может описать страницы ядра/пользователя

§ Буфер, описанный MDL, может быть отображен

от пользователя к VAS ядра или наоборот

 

 

 


Объектные Отношения

 

§ DRIVER_OBJECT – представляет изображение драйвера устройства

§ Содержит главу списка объектов устройства, создаваемых драйвером

§ DEVICE_OBJECT – представляет логическое или физическое устройство

§ Содержит указатель на объект драйвера, который создал его

§ Часть списка объектов устройства в драйвере

§ FILE_OBJECT – представляет открытый экземпляр устройства

§ Содержит указатель на объект устройства, открытый экземпляр которого он представляет

 


Строки Unicode

 

Ядро Windows хранит строки в формате Unicode. Строки, передаваемые функциям ядра, находятся почти всегда в формате Unicode. Для их представления используется структура UNICODE_STRING

 

Структура UNICODE_STRING:

 

Буфер, на который указывает UNICODE_STRING.Buffer, обычно выделяется из пула подкачиваемой страничной памяти.

 

UNICODE_STRING.Length содержит число байтов (не WCHARS). Завершающий символ UNICODE NULL в это число не включен.

 

 

Команды отладчика для просмотра строк:

  • ‘du’ отображает символы Unicode
  • ‘! du’ отображает физический адрес символов Unicode
  • ‘.enable_unicode’ позволяет/запрещает отладчику напечатать PUSHORTs как строки Unicode
  • ‘dS’ отображает структуру UNICODE_STRING

 


Двусвязные списки

 

Большинство структур данных ядра хранятся как части двусвязных списков. Структура LIST_ENTRY используется для создания и обслуживания таких списков. Список состоит из поля ListHead (тип LIST_ENTRY) и различного числа элементов списка (List Elements, включающих структуру типа LIST_ENTRY).

 

Поля Flink & Blink в структуре LIST_ENTRY указывают на вложенную структуру LIST_ENTRY, а не на List Element.

 

Структуры LIST_ENTRY НИКОГДА не содержат нулевых указателей. Когда список пуст, поля Flink и Blink ListHead указывают непосредственно на ListHead.

 

 


Уровни прерываний (IRQLs)

 

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

 

IRQL - приоритет, связанный с каждым прерыванием. Чем больше IRQL, тем выше приоритет прерывания.

 

Когда процессор обслуживает прерывание, IRQL процессора повышен.

 

Когда процессор находится на определенном IRQL, все последующие прерывания, равные либо ниже данного IRQL, “застревают” на этом же процессоре (в контроллере PIC).

 

На многопроцессорных системах каждый процессор может находиться на своем IRQL. Текущий IRQL процессора сохранен в Области Управления Процессором (KPCR – processor control block).

 

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

 

Код режима ядра может менять IRQL процессора.

 

Код пользовательского режима всегда выполняется на PASSIVE_LEVEL.

 

Таблица приоритетов прерываний для процессоров x86:

 

Уровень IRQL Назначение
Уровни аппаратных прерываний
  HIGH_LEVEL Machine check, NMI. Немаскируемое прерывание. Предназначено для остановки системы и маскирования всех прерываний. Вызывается из KeBugCheckEx.
  POWER_LEVEL Power failure. Предназначено на тот случай, если происходит пропадание электропитания. Реально никогда не использовалось.
  IPI_LEVEL Inter-processor interrupt. Используется для того, чтобы попросить другой процессор выполнить какие-то действия, например, обновить translation look-aside buffer (TLB) cache.
  CLOCK_LEVEL Clock interrupt. Используется для обновления системного времени и учета выделяемого потокам времени.
  PROFILE_LEVEL Profiling timer interrupt. Используется системным таймером для профилирования ядра, если включен такой режим. Собирается информация о выполняемых функциях путем анализа стека.
3 – 26 DEVICE_LEVEL DIRQLx – device interrupts. Уровни устройств. Они назначаются операционной системой. Алгоритм отличается на x86 (one CPU: 27 – IRQ number; multiple CPUs: round-robin в диапазоне IRQL устройств) и x64/IA64 (IRQ number / 16).
Уровни программных прерываний
  DISPATCH_LEVEL/ DPC_LEVEL Dispatch code and DPCs. Уровень программных прерываний. На этом уровне работает планировщик потоков (диспетчер). Повышение IRQL процессора до этого уровня прекращает переключение задач на данном процессоре. При этом планировщик все еще может работать на других процессорах. Обработчики аппаратных прерываний тоже могут работать, потому что их IRQL выше. Блокирование/ожидание на уровне DISPATCH_LEVEL вызывает deadlock. К страничной памяти нельзя получить доступ на уровне IRQL >= DISPATCH_LEVEL. Память, к которой выполняется обращение на уровнях прерываний DISPATCH_LEVEL и выше, должна быть резидентной (non-paged).
  APC_LEVEL APCs and page faults. На уровне APC_LEVEL работает обработчик пробоя страницы. Применяются приоритеты потоков. Каждый поток может независимо устанавливать для себя уровень APC_LEVEL, т.е. этот уровень программных прерываний локален для потока.
  PASSIVE_LEVEL/ LOW_LEVEL Thread switching. Фактически не является уровнем прерывания и существует для полноты. Уровень PASSIVE_LEVEL означает обычную работу процессора при выполнении потока. На этом уровне применяются приоритеты потоков. Если поток выполняет системный вызов, который приводит к переходу из пользовательского режима в режим ядра, IRQL не изменяется.

 

Таблица приоритетов прерываний для процессоров x64:

 

Уровень IRQL
  HIGH_LEVEL/ PROFILE_LEVEL
  POWER_LEVEL/ IPI_LEVEL
  CLOCK_LEVEL
  SYNC_LEVEL
3 – 11 DEVICE_LEVEL
  DISPATCH_LEVEL/ DPC_LEVEL
  APC_LEVEL
  PASSIVE_LEVEL/ LOW_LEVEL

 

Поток, работающий в ядре, может подымать и опускать уровень прерываний с помощью функций KeRaiseIrql() и KeLowerIrql(). Но чаще всего это происходит неявно при работе с объектами синхронизации. При блокировке объекта происходит поднятие уровня до соответствующего объекту значения. При освобождении объекта выполняется снижение уровня. Функции KeRaiseIrql() и KeLowerIrql() реализованы в HAL. В реализации используется оптимизация под названием lazy IRQL.

 

При написании обработчиков прерываний следует учитывать, что на уровнях DPC/Dispatch и выше нельзя выполнять ожидание объектов, если это требует переключения процессора на другой поток. Дело в том, что переключение на другой поток выполняется планировщиком на уровне DPC/Dispatch, который в данном случае замаскирован, и поэтому возникает блокировка. Отсюда следует, что в обработчиках прерываний на уровнях DPC/Dispatch и выше можно работать лишь с физической (non-paged) памятью. Объясняется это тем, что попытка доступа к странице, которой нет в памяти, вызывает прерывание, в ответ на которое менеджер памяти вынужден инициировать подкачку страницы с диска и дождаться завершения операции. Ожидание означает переключение на другой поток через вызов прерывания уровня DPC/Dispatch, но оно замаскировано. Нарушение правила приводит к тому, что система обваливается с кодом IRQL_NOT_LESS_OR_EQUAL. В библиотеке WDK существует программа Driver Verifier, которая позволяет выявить ошибки такого рода. Библиотека WDK определяет допустимый уровень IRQLs для каждой API-функции ядра. Драйверы устройств должны придерживаться этих рекомендаций.

 

IRQL API:

 

KeRaiseIrql()

KeLowerIrql()

KeGetCurrentIrql()

KeRaiseIrqlToDpcLevel()

 


 

Любой код в режиме ядра работает в одном из следующих контекстов:

 

  • Процедура обработки прерывания (ISR)

o Выполняется в DIRQLx в произвольном контексте потока

o Связана с аппаратными прерываниями

 

  • Отложенный вызов процедуры (DPC)

o Выполняется на DISPATCH_LEVEL в произвольном контексте потока

o Выполняет задержанную обработку прерываний на низком IRQL

 

  • Асинхронный вызов процедуры (APC)

o Выполняется на APC_LEVEL или PASSIVE_LEVEL в определенном контексте потока

 

  • Контекст Потока Пользовательского Режима

o Выполняется на PASSIVE_LEVEL в пользовательском режиме в контексте потока

o Потоки, переходившие к режиму ядра для обработки системного вызова

 

  • Контекст Потока Режима Ядра

o NTOSKRNL & Driver Worker Thread Context

§ Выполняется на уровне PASSIVE_LEVEL в контексте системного потока

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

o Контекст рабочего системного потока

§ Выполняется на уровне PASSIVE_LEVEL в контексте системного потока

§ Исполняющие подпрограммы, вызванные системными исполняющими потоками ExpWorkerThread ()

 


Процедура обработки прерывания (ISR)

 

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

 

Периферийное устройство –> Контроллер прерываний –> IDT –> Объект прерывания (ISR-адрес, IRQL, Spinlock, Dispatch code – копируется из шаблона KiInterruptTemplate) –> KiInterruptDispatch или KiChainedDispatch (raise IRQL, grab Spinlock, call Driver ISR, drop Spinlock, lower IRQL) –> Driver ISR (read from device, acknowledge-interrupt, request DPC).

 

При написании драйвера бывает нужно подсоединить-отсоединить свой обработчик прерывания. Это делается с помощью функций IoConnectInterrupt() и IoDisconnectInterrupt().

Таблица дескрипторов прерываний

(Interrupt Dispatch Table)

 

§ Прерывания сгенерированы:

o Устройствами для уведомления о событии

o Компонентами программного обеспечения для вызовов ОС

§ Исключения происходят, когда выполнение

команд генерирует событие, которое не может быть стандартно обработано ЦП

§ Элементы Таблицы содержат адреса обработчиков прерываний и обработчиков исключений

o Ядро инициализирует IDT

o ЦП ищет IDT и диспетчеризирует обработчики

§ Записи IDT (индексированные векторами) классифицированы как:

o Исключения (0x00 – 0x1f)

§ Синхронны с потоком команд ЦП

§ Примеры: Divide By Zero, Page Fault, Access Violation

o Программные прерывания (0x2a – 0x2e)

§ Прерывания Операционной системы (вызывающие службы ОС)

§ Примеры: int 2e(KiSystemService), int 2A(KiGetTickCount)

o Аппаратные прерывания (0x31 – 0xfd)

§ Асинхронны с потоком команд ЦП

§ Примеры: NIC, RTC, Клавиатура, Звуковые карты, Serial Port, IPI, Profile, APIC Error, ACPI и т.д.

 

 



Поделиться:


Последнее изменение этой страницы: 2016-08-26; просмотров: 369; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 44.201.24.171 (0.164 с.)