Смекни!
smekni.com

Профилировщик приложений (стр. 2 из 4)

Удаление символьной ссылки из пространства имен, видимого пользовательскими приложениями (IoDeleteSymbolicLink);

Удаление объекта устройства (IoDeleteDevice);

Освобждение памяти, выделенной драйверу в процессе работы.

2.4.3 Рабочие процедуры обработки IRP-пакетов

Все функции, зарегистрированные в процедуре DriverEntry путём заполнения массива MajorFunction, вызываются Диспетчером ввода/вывода для обработки соответсвующих запросов от клиентов драйвера. Эти запросы всегда оформлены в виде специальных структур данных – IRP-пакетов, память под которые выделяется Диспетчером ввода/вывода в нестраничном системном пуле. Структура IRP-пакета такова, что он состоит из заголовка фиксированного размера и IRP-стека, размер которого зависит от количества объектов устройств в стеке.

2.4.3.1 Заголовок IRP пакета. Структура заголовка IRP-пакета имеет следующие поля:

Поле IoStatus типа IO_STATUS_BLOCK содержит два подполя:

Status содержит значение, которое устанавливает драйвер после обработки пакета;

В Information чаще всего помещается число переданных или полученных байт.

Поле AssociatedIrp.SystemBuffer типа PVOID содержит указатель на системный буфер для случая, если устройство поддерживает буферизованный ввод/вывод;

Поле MdlAddress типа PMDL содержит указатель на MDL-список, если устройство поддерживает прямой ввод вывод;

Поле UserBuffer типа PVOID содержит адрес пользовательского буфера для ввода/вывода;

Поле Cancel типа BOOLEAN - это индикатор того, что пакет IRP должен быть аннулирован.


2.4.3.2 Стек IRP-пакета. Основное назначение ячеек стека IRP-пакета состоит в том, чтобы хранить функциональный код и параметры запроса на ввод/вывод. Для запроса, который адресован драйверу самого нижнего уровня, соответствующий IRP пакет имеет только одну ячейку стека. Для запроса, который послан драйверу верхнего уровня, Диспетчер ввода/вывода создает пакет IRP с несколькими стековыми ячейками – по одной для каждого объекта устройства.

Каждая ячейка IRP-стека содержит:

MajorFunction типа UCHAR – это код, описывающий назначение операции;

MinorFunction типа UCHAR – это код, описывающий суб-код операции;

DeviceObject типа PDEVICE_OBJECT – это указатель на объект устройства, которому был адресован данный запрос IRP;

FileObject типа PFILE_OBJECT – файловый объект для данного запроса;

Parameters типа union – применение зависит от значения MajorFunction.

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


Каждая процедура обработки IRP пакетов должна в качестве параметров принимать:

Указатель на объект устройства, для которого предназначен IRP запрос;

Указатель на пакет IRP, описывающий этот запрос;

2.4.3.3 Функция обработки пакетов IRP_MJ_CREATE. Данная функция предназначена для обработки запросов на получение дескриптора драйвера от пользовательских приложений или вышестоящих драйверов. Как правило, эта функция просто помечает IRP-пакет, как завершённый.

2.4.3.4 Функция обработки пакетов IRP_MJ_CLOSE. Данная функция предназначена для обработки запросов на закрытие дескриптора драйвера от пользовательских приложений или вышестоящих драйверов. Как правило, эта функция просто помечает IRP-пакет, как завершённый.

2.4.3.5 Функция обработки пакетов IRP_MJ_DEVICE_CONTROL. Данная функция позволяет обрабатывать расширенные запросы от клиентов пользовательского режима, служат чаще всего для обмена данными между приложением и драйвером. Такой запрос может быть сформирован посредством вызова функции DeviceIoControl из пользовательского режима.

Здесь используются IOCTL-коды (I/O Control code), часть из которых предопределена операционной системой, а часть может создаваться разработчиком драйвера. Такой код задаётся в запросе Диспетчером ввода/вывода при формировании IRP-пакета.

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

Метод доступа к данным, обеспечиваемый Диспетчером ввода/вывода, определяется в IOCTL-коде. Такими методами могут быть:

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

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

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

METHOD_NEITHER: при данном методе передачи не производится проверка доступности памяти, не выделяются промежуточные буфера и не создаются MDL. В IRP-пакете передаются виртуальные адреса буферов в адресном пространстве инициатора запроса ввода/вывода.

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

С точки зрения драйвера, доступ к буферным областям, содержащим данные или предназначенным для данных, осуществляется с помощью следующих полей структур [1]:

METHOD_BUFFERED METHOD_IN_DIRECT или METHOD_OUT_DIRECT METHOD_NEITHER
InputБуфер с данными Использует буферизацию (системный буфер)Адрес буфера в системном адресном пространстве указан в pIrp->AssociatedIrp.SystemBuffer Клиентский виртуальный адрес в Parameters. DeviceIoControl. Type3InputBuffer
Длина указана в Parameters.DeviceIoControl.InputBufferLength
OutputБуфер для данных Использует буферизацию (системный буфер)Адрес буфера в системном адресном пространстве указан в pIrp-> AssociatedIrp. SystemBuffer Использует прямой доступ, клиентский буфер преобразован в MDL список, указатель на который размещен в pIrp->MdlAddress Клиентский виртуальный адрес в pIrp->UserBuffer
Длина указана в Parameters.DeviceIoControl.OutputBufferLength

2.4.4 ISR – процедура обработки прерываний

Эту функцию драйвер регистрирует, чтобы она получала управление в момент, когда аппаратура, обслуживаемая драйвером, передала сигнал прерывания. Задача этой функции выполнить минимальную работу и зарегистрировать процедуру отложенного вызова (DPC) для обслуживания прерывания. Вызов диспетчером прерываний ядра может произойти в любом контексте: как ядра, так и пользовательского процесса.

2.4.5 DPC – процедура отложенного вызова

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


3. Конструкторский раздел

Так выглядит схема взаимодействия пользовательского приложения с драйвером через компоненты системы:

3.1 Legacy-драйвер

В Legacy-драйвере данного курсового проекта реализованы следующие процедуры:

DriverEntry;

DriverUnload;

DispatchCreate (обработка IRP_MJ_CREATE-пакета);

DispatchClose (обработка IRP_MJ_CLOSE-пакета);

DispatchDeviceControl (обработка IRP_MJ_DEVICE_CONTROL-пакета).

3.1.1 Процедура DriverEntry

Здесь выполняются типичные для инициализации драйвера драйвера действия.

Регистрируются точки входа в драйвер:

pDriverObject->DriverUnload = SpectatorDriverUnload;

PDRIVER_DISPATCH * majorFunction = pDriverObject->MajorFunction;

majorFunction[ IRP_MJ_CREATE ] = SpectatorDispatchCreate;

majorFunction[ IRP_MJ_CLOSE ] = SpectatorDispatchClose;

majorFunction[ IRP_MJ_DEVICE_CONTROL ] = SpectatorDispatchDeviceControl;

Создаётся объект устройства с именем DEVICE_NAME:

#define DEVICE_NAME L"\Device\Spectator"

RtlInitUnicodeString( &deviceName, DEVICE_NAME );

status = IoCreateDevice

(pDriverObject,

sizeof( DEVICE_EXTENSION ),

&deviceName,

FILE_DEVICE_SPECTATOR,

FILE_DEVICE_SECURE_OPEN,

FALSE,

&pDeviceObject);

Для созданного обекта устройства регистрируется символьная ссылка SYMBOLIC_LINK:

#define SYMBOLIC_LINK L"\DosDevices\Spectator"

RtlInitUnicodeString( &symbolicLink, SYMBOLIC_LINK );

status = IoCreateSymbolicLink( &symbolicLink, &deviceName);

Создаётся объект ядра мьютекс:

NTSTATUS CreateMutex()

{BEGIN_FUNC( CreateMutex );

NTSTATUS status = STATUS_SUCCESS;

status = _ExAllocatePool( g_pMutex, NonPagedPool, sizeof( KMUTEX ) );

if ( NT_SUCCESS( status ) )

{KeInitializeMutex( g_pMutex, 0 );

status = STATUS_SUCCESS;}

END_FUNC( CreateMutex );

return ( status );}

Впервые загружается информация о процессах и их потоках:

if ( LockInfo() == STATUS_SUCCESS )

{ReloadInfo();

UnlockInfo();}

Функции LockInfo() и UnlockInfo() являются просто напросто функциями-обёртками для функций LockMutex() и UnlockMutex() соответственно. Первая из последних двух функций ожидает на объекте ядра мьютекс.

Объекты ядра «мьютексы» гарантируют потокам взаимоисключающий доступ к един ственному ресурсу. Отсюда и произошло название этих объектов (mutual exclusion, mutex). Они содержат счетчик числа пользователей, счетчик рекурсии и переменную, в которой запоминается идентификатор потока. Мьютексы ведут себя точно так же, как и критические секции. Однако, если последние являются объектами пользователь ского режима, то мьютексы — объектами ядра. Кроме того, единственный объект-мью текс позволяет синхронизировать доступ к ресурсу нескольких потоков из разных процессов; при этом можно задать максимальное время ожидания доступа к ресурсу.