1.6 Уровни запроса прерываний
В каждый момент времени центральный процессор находится на одном из уровней IRQL (Interrupt Request Level – уровень запросов прерываний). Уровни IRQL располагаются в порядке убывания от HIGHEST_LEVEL до PASSIVE_LEVEL. Каждому из прерываний (прерывания от внешних устройств, системные часы, и т.д.) соответствует свой уровень IRQL. Специальным действиям операционной системы также назначены IRQL. Они отмечены в нижней части приведённой таблицы:
Таблица 1.6.1. Уровни запросов прерываний.
Уровень | Назначение |
HIGHEST_LEVEL | Наивысший уровень. Все прерывания заблокированы |
POWER_LEVEL | Прерывания по отказу питания |
IPI_LEVEL | Межпроцессорное взаимодействие |
CLOCK2_LEVEL | Прерывание по системному таймеру 2 |
СLOCK1_LEVEL | Прерывание по системному таймеру 1 |
PROFILE_LEVEL | Прерывание по таймеру замера производительности |
уровни DRQL | Обычные прерывания устройств |
DISPATCH_LEVEL | Диспетчеризация потоков и выполнение отложенных процедур |
APC_LEVEL | Выполнение асинхронного вызова процедуры |
PASSIVE_LEVEL | Обычное исполнение кода потока |
Общее правило обработки уровней запросов прерываний гласит, что прерывания с IRQL, меньшим, чем у выполняемого в данный момент кода, маскируются. Во время исполнения кода потока (пользовательского или системного) устанавливается наименьший IRQL = 0 (PASSIVE_LEVEL). Работа драйвера чаще всего выполняется на уровне IRQL = 2 (DISPATCH_LEVEL). Уровни, лежащие над ним, называются DIRQL (Device IRQL) и выставляются для обработчиков прерываний от внешних устройств (ISR – interrupt service routine). Даже во время выполнения ISR драйвера может произойти прерывание с большим IRQL, например, принадлежащее другому драйверу.
Чем выше текущий уровень IRQL исполняемого кода, тем меньше функций ему доступно. Так, например, диспетчер потоков работает на уровне
DISPATCH_LEVEL, и, следовательно, не будет вызываться, пока на процессоре с уровнем большим или равным DISPATCH_LEVEL исполняется другой код. Таким образом, на уровнях DISPATCH_LEVEL и выше отключается переключение потоков. Функции ожидания диспетчерских объектов (события, мьютексы, семафоры) с отличным от нуля временем, обращение к файлам, подкачка отсутствующих в физической памяти страниц – всё это также становится недоступным. Для корректного сохранения запросов в файле фильтр в таких случаях должен применять специальную методику.
1.7 Уведомление о завершении запроса нижестоящим драйвером
При отслеживании обмена данными драйвер-фильтр может получать уведомления о том, что некоторый переданный запрос был завершён нижестоящим драйвером. Механизм уведомления заключается в том, что вызовом специальной функции IoSetCompletionRoutine фильтр обращается к стеку в пакете IRP. В позиции стека, следующей за текущей позицией, он устанавливает в специальном поле адрес функции завершения (completion routine). Затем при передаче пакета по цепочке позиция стека увеличивается.
Когда нижестоящий драйвер отправляет пакет запроса на завершение (вызовом IoCompleteRequest), подсистема ввода / вывода начинает просматривать стек внутри этого пакета от конца к началу. Если в какой-то позиции стека определена функция завершения, управление передаётся ей. Отработав, функция возвращает результат, сигнализирующий об успехе, ошибке или необходимости дальнейшей обработки запроса.
При первых двух вариантах подсистема ввода / вывода переходит к следующей позиции стека и продолжает просмотр, пока не будет достигнуто его начало. После этого запрос завершается нормальным образом.
При третьем же варианте просмотр стека немедленно прекращается и запрос не будет завершён. Эта возможность реализована для того, чтобы драйвер-фильтр мог выполнить какие-либо действия над пакетом запроса после того, как тот будет обработан в нижестоящем драйвере. После такой «дополнительной обработки» пакет снова должен быть отправлен на завершение.
1.8 Работа с файлами в режиме ядра
Поскольку протоколируемая информация должна сохраняться в файле на диске, следует рассмотреть основные функции уровня ядра, используемые при работе с файлами.
Для открытия файла из драйвера режима ядра используется универсальная функция ZwCreateFile. Универсальность этой функции состоит в том, что с ее помощью производится и открытие существующих файлов, и создание новых.
Специфика системной функции ZwCreateFile состоит в том, что она имеет протокольных параметров даже больше, чем пользовательский вызов CreateFile. Существенная часть входной информации об открываемом объекте поступает внутри структуры OBJECT_ATTRIBURTES, которую следует предварительно создать и заполнить соответствующими конкретными данными. Для ведения учетной информации открытого объекта используется структура данных IO_STATUS_BLOCK, которую следует предоставить при вызове (инициализировать ее не следует).
Представим основные параметры функции ZwCreateFile в следующей таблице:
Таблица 1.8.1. Параметры функции ZwCreateFile
Тип параметра | Описание параметра |
OUT PHANDLE pHandle | Указатель на переменную, куда следует поместить дескриптор открытого объекта |
IN ACCESS_MASK DesiredAccess | Характеристика доступа к объекту. Для фалов чаще всего используются значения GENERIC_READ или GENERIC_WRITE |
IN POBJECT_ATTRIBUTES pObjAttributes | Указатель на заполненную вызывающим кодом структуру данных, которая описывает имя, местоположение и некоторые другие характеристики открываемого объекта |
OUT PIO_STATUS_BLOCK pIOStatus | Указатель на буфер, в котором будет размещена информация об открытом объекте в формате структуры IO_STATUS_BLOCK |
IN PLARGE_INTEGER AllocationSize | Начальный размер файла в байтах. Ненулевое значение принимается во внимание только при создании и перезаписи файла |
IN ULONG FileAttributes | Атрибуты открываемого файла. Типовым является значение FILE_ATTRIBUTE_NORMAL |
IN ULONG SharedAccessFlags | Описывает, разрешен ли совместный доступ, например, FILE_SHARE_READ – для чтения |
IN ULONG CreateDispositionFlags | Способ открытия файла, например, FILE_OPEN_IF – если не существует, создать |
IN ULONG CreateOptions | Комбинация флагов создания, например, FILE_SYNCHONOUS_IO_NONALERT – все операции над файлом выполняются как синхронные (DesiredAccess должен включать флаг SYNCHRONIZE) |
IN PVOID EaBuffer | Для драйверов устройств следует указывать NULL |
IN ULONG EaLength | Для драйверов устройств следует указывать 0 |
Для заполнения структуры атрибутов объекта используется функция
InitializeObjectAttributes. Опишем ее параметры в следующей таблице:
Таблица 1.8.2. Параметры функции InitializeObjectAttributes
Тип параметра | Описание параметра |
OUT POBJECT_ATTRIBUTES pObjAttributes | Указатель на переменную, куда следует поместить атрибуты объекта |
IN PUNICODE_STRING ObjectName | Имя объекта, HANDLE которого создается |
IN ULONG Attributes | Флаги атрибутов объекта, при открытии файла как правило используются флаги OBJ_CASE_INSENSITIVE и OBJ_KERNEL_HANDLE |
IN HANDLE RootDirectory | Дескриптор корневой директории для объекта, описатель атрибутов которого создается. Если ObjectName полностью описывает путь к объекту, то значению RootDirectory присваивается NULL |
IN PSECURITY_DESCRIPTOR SecurityDescriptor | Дескриптор безопасности. Если указано NULL, то применяется стандартный дескриптор |
Запись в файл выполняется системной функцией ZwWriteFile:
Таблица 1.8.3. Параметры функции ZwWriteFile
Тип параметра | Описание параметра |
IN HANDLE FileHandle | Дескриптор открытого или модифицированного файлового объекта |
IN HANDLE Event | Для драйверов устройств следует указывать NULL |
IN PIO_APC_ROUTINE | Для драйверов устройств следует указывать NULL |
IN PVOID ApcContext | Для драйверов устройств следует указывать NULL |
OUT PIO_STATUS_BLOCK pioStatusBlock | В поле pIoStatusBlock->Information по завершении вызова находится число реально записанных байт |
IN PVOID Buffer | Буфер с данными для записи |
IN ULONG Length | Размер записываемой порции данных |
IN PLARGE_INTEGER pByteOffset | Указатель на переменную где содержится смещение в файле от его начала, по которому следует производить запись |
IN PULONG Key | Для драйверов устройств следует указывать NULL |
Для закрытия дескриптора объекта следует применять функцию ZwCloseKey.
Следует отметить, что функции работы с файлами могут работать только на уровне IRQL, равном PASSIVE_LEVEL. Это приводит к необходимости применения специальной методики при протоколировании обмена данными с USB‑накопителем.
... системы и их особенностями в целом, мы разобрались, теперь самое время приступить к более детальному, конкретному рассмотрению многообразия ОС, которое обычно начинается с рассмотрения краткой истории появления и развития. Операционная система Unix Считается, что в появлении Юникса в частности виновата... компьютерная игра. Дело в том, что Кен Томпсон непонятно чего ради создал игрушку «Space ...
0 комментариев