Курсовая работа: Профилировщик приложений
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;
Благодаря одной этой переменной становится возможным одновременное обращение к драйверу сразу нескольких пользовательских приложений. Единственным ограничением для них ялвяется эксклюзивность доступа к информации о процессах и их потоках.
3.1.2 DriverUnload
В этой процедуре, если число клиентов драйвера равно нулю, происходит удаление всех объектов созданных для организации работы таймера, удаление мьтекса, объекта устройства и его символьной ссылки. Если же число клиентов отлично от нуля, то драйвер не выгружается, так как, в противном случае, это нарушит нормальную работу других пользовательских приложений-клиентов.
3.1.3 DispatchCreate и DispatchClose
В этих функциях происходит учёт количества открытых описателей данного драйвера полученных с помощью API-вызова CreateFile(). Сколько описателей было открыто – столько же должно быть закрыто API-вызовом CloseHandle(). Иначе драйвер по окончании работы пользовательского приложения останется в операционной системе, что, естественно, крайне не желательно.
3.1.4 DispatchDeviceControl
Эта процедура обслуживает IOCTL-запросы от пользовательских приложений посылаемые API-вызовом DeviceIoControl(). В данном курсовом проекте взаимодействие с драйвером большею частью и построено на их применении, здесь реализована основная функциональность драйвера: то, для чего он и предназначался. Поэтому данная процедура наиболее объёмна.
Сначала, назависимо от конкретного IOCTL-запроса, получается указатель на ячейку IRP-стека IRP-пакета, предназначенную для объекта устройства драйвера:
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation( pIrp );
Далее, из этой ячейки извлекается код IOCTL-запроса, на основе которого с помощью оператора switch происходит дополнительная диспетчеризация IRP-пакета.
В рассматриваемом драйвере все IOCTL-запросы используют буферизованный метод передачи данных, так как во всех запросах их объём действительно невелик. При таком подходе передачи данных в системном нестраничном пуле выделяется столько памяти, чтобы поместился больший из входного и выходного буферов. Перед началом обработки запроса содержимое входного пользовательского буфера копируется в системный буфер, а по завершении из системного – в выходной пользовательский буфер. Так как для обоих пользовательских буферов используется всего лишь один системный, то необходимо быть аккуратным при обработке данных, так как есть вероятность при записи повредить ещё непрочитанные входные данные и тогда они будут утеряны навсегда.
Длины (в байтах) пользовательских буферов, входного и выходного, извлекаются из поля Parameters ячейки IRP-стека: Parameters.DeviceIoControl.InputBufferLength и Parameters.DeviceIoControl.OutputBufferLength соответственно. А адрес системного буфера извлекается из заголовка IRP-пакета: AssociatedIrp.SystemBuffer.
3.1.4.1 IOCTL_LAST_CLIENT . Входные данные : [нет]
Выходные данные : [нет]
Данный IOCTL-запрос служит для обращения к драйверу, чтоб тот дал ответ на вопрос является ли инициатор запроса единственным клиентом, работающим с драйвером на данный момент. Этот запрос посылается драйверу каждым пользовательски приложением, когда оно собирается вот-вот завершиться. Если ответ положительный, то это приложение пытается завершить работу драйвера, иначе оно просто завершается, будучи уверенным, что есть другие клиенты, работающие с драйвером и что то приложение, которое будет завершаться последним, позаботится о выгрузке драйвера.
3.1.4.2 IOCTL_LOCK_INFO и IOCTL_UNLOCK_INFO. Входные данные : [нет]
Выходные данные : [нет]
Первый IOCTL-запрос из этой служит для захвата пользовательским приложением системной информации в монопольное пользование. Другой – соответствено, для осовбождения этого ресурса. В них просто вызываются одноимённые функции LockInfo() и UnlockInfo(), о которых было рассказано ранее, когда речь шла о процедуре DriverEntry данного раздела.