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"DeviceSpectator"
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"DosDevicesSpectator"
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). Они содержат счетчик числа пользователей, счетчик рекурсии и переменную, в которой запоминается идентификатор потока. Мьютексы ведут себя точно так же, как и критические секции. Однако, если последние являются объектами пользователь ского режима, то мьютексы -- объектами ядра. Кроме того, единственный объект-мью текс позволяет синхронизировать доступ к ресурсу нескольких потоков из разных процессов; при этом можно задать максимальное время ожидания доступа к ресурсу.
Именно благодаря этому мьютексу обеспечивается требование по безопасности при обращении к хранимой информации.
Инициализируется работа таймера:
Таймер необходим для того, чтобы с определённым интервалом обновлять хранимую информацию.
Для этого создаётся объект ядра «таймер»:
status = _ExAllocatePool( g_pTimer, NonPagedPool, sizeof( KTIMER ) );
KeInitializeTimerEx( g_pTimer, SynchronizationTimer );
Замечание: память под объекты ядра должна выделяться исключительно в нестраничном пуле (ключевое слово NonPagedPool).
Таймеры могут быть двух типов:
SynchronizationTimer -- по истечении указанного временного интервала или очередного периода, он переводится в сигнальное состояние, пока один из потоков, ждущих его, не будет пробуждён. Тогда же таймер переводится в несигнальное состояние.
NotificationTimer -- по истечении указанного временного интервала или очередного периода, он переводится в сигнальное состояние, причём пробуждаются все потоки ожидающие на нём. Такой таймер остаётся в сигнальном состоянии до тех пор, пока он не будет явно переведён в несигнальное.
Для того, чтобы выполнять какую-то полезную работу по таймеру, необходимо зарегистрировать DPC-процедуру OnTimer(). Для неё необходимо создать собственный DPC-объект, который будет периодически ставится в общесистемную очередь:
status = _ExAllocatePool( g_pTimerDpc, NonPagedPool, sizeof( KDPC ) );
KeInitializeDpc( g_pTimerDpc, OnTime, NULL );
Далее, в силу того, что в данном драйвере по таймеру должны выполняться действия, требующие пользовательского контекста, необходимо их вынести из функции OnTimer(), которая является DPC-процедурой, а следовательно, во время её выполнения доступен лишь системный контекст. Тем не менее, необходимо обеспечить приемлемую синхронность выполнения необходимой работы с моментом извлечения DPC-объекта функции из очереди для обработки. Для этого создадим поток, который будет посвящён ожиданию некоторого события:
OBJECT_ATTRIBUTES objectAttributes;
InitializeObjectAttributes( &objectAttributes, NULL, OBJ_KERNEL_HANDLE,
NULL, NULL );
status = PsCreateSystemThread( &hThread, THREAD_ALL_ACCESS, &objectAttributes,
NULL, NULL, UpdateThreadFunc, NULL );
KeInitializeEvent( g_pUpdateEvent, SynchronizationEvent, FALSE );
Замечание: объекты ядра «события» по своему типу идентичны объектам ядра «таймер».
При поступлении этого события поток будет обновлять системную информацию о процессах и их потоках. Объект этого события будем переводить в сигнальное состояние в функции OnTimer(). Данный способ синхронизации позволил обеспечить выполнение необходимых действий через заданный интервалом с точностью до милисекунды, что следует из нижеприведённых сообщений, перехваченных программой DebugView от отладочной версии драйвера:
0.00075233 [Spectator] ^^^^^^^^ OnTime ^^^^^^^^
0.00116579 [Spectator] ======== LockInfo ========
0.00118814 [Spectator] ======== ReloadInfo ========
0.99727142 [Spectator] ^^^^^^^^ OnTime ^^^^^^^^
1.00966775 [Spectator] ======== LockInfo ========
1.00968981 [Spectator] ======== ReloadInfo ========
1.99729049 [Spectator] ^^^^^^^^ OnTime ^^^^^^^^
2.05610037 [Spectator] ======== LockInfo ========
2.05632067 [Spectator] ======== ReloadInfo ========
2.99727035 [Spectator] ^^^^^^^^ OnTime ^^^^^^^^
2.99741030 [Spectator] ======== LockInfo ========
2.99743295 [Spectator] ======== ReloadInfo ========
3.99727631 [Spectator] ^^^^^^^^ OnTime ^^^^^^^^
3.99739385 [Spectator] ======== LockInfo ========
3.99741673 [Spectator] ======== ReloadInfo ========
4.99728107 [Spectator] ^^^^^^^^ OnTime ^^^^^^^^
4.99742365 [Spectator] ======== LockInfo ========
4.99744749 [Spectator] ======== ReloadInfo ========
5.99728870 [Spectator] ^^^^^^^^ OnTime ^^^^^^^^
5.99742651 [Spectator] ======== LockInfo ========
5.99744844 [Spectator] ======== ReloadInfo ========
Здесь OnTime - момент входа в процедуру таймера OnTimer, LockInfo - момент, когда пробудился поток, отвечающий за обновление информации, ReloadInfo - момент, когда информация была действительно обновлена.
Как видно из перехвата, в первые две секунды периодичность не на высоком уровне, но потом ситуация стабилизируется и точность улучшается, как и было заявлено, до одной миллисекунды.
После всех этих действий, наконец, запускается таймер:
LARGE_INTEGER dueTime = RtlConvertLongToLargeInteger( 0 );
BOOLEAN existed = KeSetTimerEx( g_pTimer, dueTime, g_timerPeriod, g_pTimerDpc );
Здесь dueTime - время до первого вызова процедуры OnTime(), а g_timerPeriod - период дальнейших вызовов.
Вконце концов, в процедуре DriverEntry происохдит обнуление счётчика пользовательских приложений-клиентов, получивших описатель данного драйвера: pDeviceExtension->clientCount = 0;
Благодаря одной этой переменной становится возможным одновременное обращение к драйверу сразу нескольких пользовательских приложений. Единственным ограничением для них ялвяется эксклюзивность доступа к информации о процессах и их потоках.
- 1. Введение
- 2. Аналитический раздел
- 2.1 Техническое задание
- 2.2 Обзор архитектуры Windows NT 5.x
- 2.3 Классификация драйверов
- 2.4 Общая структура Legacy-драйвера
- 2.4.1 Процедура DriverEntry
- 2.4.2 Процедура DriverUnload
- 2.4.3 Рабочие процедуры обработки IRP-пакетов
- 2.4.4 ISR - процедура обработки прерываний
- 2.4.5 DPC - процедура отложенного вызова
- 3. Конструкторский раздел
- 3.1 Legacy-драйвер
- 3.1.1 Процедура DriverEntry
- 3.1.2 DriverUnload
- 3.1.3 DispatchCreate и DispatchClose
- 3.1.4 DispatchDeviceControl
- 3.2 Пользовательское приложение
- 4. Технический раздел
- 4.1 Выбор операционной системы и среды программирования
- 4.2 Интерфейс
- 4.3 Системные требования
- 3.7. Профилировщик
- Профилировщик
- 15. Производительность профилировщика основания
- 8.1.3. Использование sql Profiler (профилировщика)
- Техническая характеристика профилировщиков
- 8.16.Профилировщик .
- § Е17-17. Планировка и уплотнение основанияили выравнивающего слоя профилировщиком дс-502а (д-345а) Техническая характеристика профилировщика дс-502а (д-345а)
- Техническая характеристика профилировщиков
- Профилировщиками дс-97, дс-108