Профілювальники додатків

[ виправити ] текст може містити помилки, будь ласка перевіряйте перш ніж використовувати.

скачати











РОЗРАХУНКОВО-ПОЯСНЮВАЛЬНА ЗАПИСКА

до курсового проекту на тему:

Профілювальники додатків

Зміст

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.3.1. Тема IRP пакету

2.4.3.2. Стек IRP-пакету

2.4.3.3. Функція обробки пакетів IRP_MJ_CREATE

2.4.3.4. Функція обробки пакетів IRP_MJ_CLOSE

2.4.3.5. Функція обробки пакетів IRP_MJ_DEVICE_CONTROL

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.1.4.1. IOCTL_LAST_CLIENT

3.1.4.2. IOCTL_LOCK_INFO і IOCTL_UNLOCK_INFO

3.1.4.3. IOCTL_PROCESS_FIRST і IOCTL_PROCESS_NEXT

3.1.4.4. IOCTL_THREAD_FIRST і IOCTL_THREAD_NEXT

3.1.4.5. IOCTL_OPEN_THREAD

3.1.4.6. IOCTL_CLOSE_THREAD

3.1.4.7. IOCTL_GET_THREAD_CONTEXT

3.2. Для користувача додаток

4. Технічний розділ

4.1. Вибір операційної системи і середовища програмування.

4.2. Інтерфейс

4.3. Системні вимоги

5. Висновок.

6. Список використаної літератури.

1. Введення

Дуже часто при розробці програмного забезпечення виникає необхідність, простежити за його роботою: скільки часу його потоки виконуються в режимі ядра, скільки - в режимі користувача, скільки часу вони проводять в очікуванні, а також кількість перемикань контексту з одного режиму в іншій. Все це важливо, тому що кожний з режимів має свої особливості. У режимі ядра код виконується швидше, але існує потенційна можливість пошкодження даних / коду системи. На противагу режиму ядра, призначений для користувача режим обмежений у наданих йому сервісах так, щоб його код не міг привести до краху системи. Для цієї ж мети в призначеному для користувача режимі виконуються додаткові перевірки, що дозволяють предотваратіть виконання шкідливих інструкцій. Тому швидкість виконання коду користувацького режиму істотно нижче. Кількість перемикань контексту теж впливає на швидкість виконання коду, так як це операція є досить дорогою (близько 2000 тактів). Це було добре помітно при розробці лабораторних робіт та курсового проекту по машинній графіці: при малюванні зображення попіксельно за допомогою функції SetPixel, швидкість промальовування була незрівнянно менше, ніж при використанні буфера користувацького режиму, в який поступово заносилася інформація про колір відповідних елементів буффера пікселям. Це відбувалося зарахунок того, що при використанні функції SetPixel відбувалося два перемикання контексту (з користувальницького режиму в режим ядра і назад) на один піксель, а при використанні буфера, що зберігає контекстно незалежне уявлення кольору, - ті ж два перемикання, але один раз на промальовування цілого кадру.

Таким чином, можливість дізнатися вищевказану статистичну інформацію про цільове програмному забезпеченні, дозволить своєчасно помітити так звані «вузькі» місця в програмі, які заважають поліпшенню продуктивності програми в цілому.

2. Аналітичний розділ

2.1 Технічне завдання

Метою представленого курсового проекту є розробка простого профіліровщики додатків, який в себе включає:

Legacy-драйвер, який має:

Періодично оновлювати інформацію про процеси та їх потоках;

Надавати базову інформацію про процеси та їх потоках;

Надавати апаратний контекст обраного потоку;

Забезпечувати безпечний звернення до цієї інформації від декількох призначених для користувача додатків-клієнтів.

Для користувача додаток, що дозволяє:

Коректно встановлювати і видаляти драйвер без необхідності перезавантаження системи;

Звертатися до драйвера з запитами для отримання різної інформації.

2.2 Огляд архітектури Windows NT 5.x

У Windows додатку відокремлені від самої операційної системи. Код її ядра виконується в привілейованому режимі процесора (режим ядра), який забезпечує доступ до системних даними та обладнанням. Код додатків виконується в непривілейованому режимі процесора (призначений для користувача режим) з неповним набором інтерфейсів, обмеженим доступом до системних даними і без прямого доступу до обладнання. Коли програма користувацького режиму викликає системний сервіс, процесор перехоплює виклик і перемикає викликає потік в режим ядра. Після закінчення системного сервісу операційна система перемикає контекст потоку назад в призначений для користувача режим і продовжує його виконання.

Далі схематично показана та частина архітектури Windows, яка порушена даними курсовим проектом. Тут не вказуються такі компоненти, як: процеси підтримки системи, процеси сервісів, підсистеми оточення, рівень абстрагування від устаткування і підтримка вікон та графіки.

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

DLL підсистем - в Windows для користувача програми не можуть викликати сервіси операційної системи безпосередньо, замість цього вони працюють з однією або кількома DLL підсистем, призначення яких полягає у трансляції документованих функцій у відповідні внутрішні (і звичайно недокументовані) виклики системних сервісів.

Виконавча система - містить базові сервіси операційної системи, які забезпечують управління пам'яттю, процесами і потоками, захист, введення-виведення та взаємодія між процесами.

Ядро - містить низькорівневі функції операційної системи, які підтримують, наприклад, планування потоків, диспетчеризацію переривань і виключень. Воно також надає набір процедур і базових об'єктів, що застосовуються виконавчою системою для реалізації структур більш високого рівня.

Драйвери - служать для розширення функціональних можливостей ядра.



2.3 Класифікація драйверів

На відміну від користувальницького додатка, драйвер не є процесом і не має потоку виконання. Замість цього управління драйверу передається в результаті запиту на ввід / вивід від призначеного для користувача програми або драйвера, або виникає в результаті переривання. У першому випадку контекст виконання драйвера точно відомий - це прикладна програма. У другому випадку контекст виконання може бути як відомим, так і випадковим - це залежить від контексту виконання функції викликає драйвера. У третьому випадку контекст виконання випадковий, оскільки переривання (і, відповідно, виконання коду драйвера) може відбутися при виконанні будь-прикладної програми.

По розташуванню в стеку драйверів:

Драйвери вищого рівня - отримують запити від призначеного для користувача програми та взаємодіють з нижчестоящими драйверами;

Проміжні драйвери - отримують запити від вищих драйверів і взаємодіють з нижчестоящими драйверами;

Драйвери нижчого рівня - отримують запити від вищих драйверів, здійснюють кінцеву обробку пакетів запитів.

Також виділяють поняття монолітного драйвера - драйвера вищого рівня, не взаємодіє ні з якими іншими драйверами.

У зв'язку з удосконаленням моделі драйверів Windows (WDM - Windows Driver Model), в якій були додані підтримка Plug and Play та енергозберігаючі технології, драйвера стали розділяти на:

Успадковані драйвера (Legacy-драйвера, драйвера «в стилі NT») - драйвера, написані в старому манері, без підтримки нововведень;

WDM-драйвера - драйвера, які відповідають усім вимогам розширеної моделі WDM.

2.4 Загальна структура Legacy-драйвера

Legacy-драйвер має такі основні точки входу:

DriverEntry - процедура завантаження драйвера;

DriverUnload - процедура вивантаження драйвера;

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

ISR-процедура (Interrupt Service Routine) - процедура обробки переривання;

DPC-процедура (Deferred Procedure Call) - процедура відкладеного виклику.

2.4.1 Процедура DriverEntry

Дана процедура є у будь-якому драйвері і викликається диспетчером введення / виводу при завантаженні драйвера.

Legacy-драйвери виконують в ній істотно більшу роботу, ніж WDM-драйвера, так як вони змушені виконувати роботу процедури AddDevice, обов'язкової для WDM-драйверів. Крім рішення ініціалізаційний завдань та реєстрації точок входу робочих процедур обробки підтримуваних IRP-пакетів і процедури вивантаження драйвера, тут:

Визначається апаратне забезпечення, яке драйвер буде контролювати;

Створюються об'єкти пристроїв (функція IoCreateDevice) для кожного фізичного або логічного пристрою під управлінням даного драйвера;

Для пристроїв, які повинні бути видимі для користувача додатків, створюються символьні посилання (функція IoCreateSymbolicLink);

При необхідності, пристрій підключається до об'єкта переривань. У випадку, якщо ISR-процедура вимагає використання DPC-процедури, то відповідний їй об'єкт створюється і ініціалізується на цьому етапі;

Виділення пам'яті, необхідної для роботи драйвера.

2.4.2 Процедура DriverUnload

Диспетчер введення / виведення викликає данну процедуру при динамічній вивантаженні драйвера. Ця процедура виконує дії, «зворотні» тим, що виконуються у процедурі DriverEntry.

Для Legacy-драйверів характерні наступні кроки:

Для деяких типів апаратури необхідно зберегти її стан в системному реєстрі, тому що при подальшій завантаженні драйвера ці дані можуть бути використані;

Якщо переривання дозволені для обслуговується пристрою, то процедура вигружкі повинна заборонити їх і зробити відключення від об'єкта переривань. Ситуація, коли пристрій буде породжувати переривання для неіснуючого об'єкта переривання, неминуче призведе до краху системи;

Видалення символічного зв'язку з простору імен, видимого користувацькими додатками (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). Вони містять лічильник числа користувачів, лічильник рекурсії та змінну, в якій запам'ятовується ідентифікатор потоку. М'ютекси поводяться точно так само, як і критичні секції. Однак, якщо останні є об'єктами користувач ського режиму, то м'ютекси - об'єктами ядра. Крім того, єдиний об'єкт-ма текс дозволяє синхронізувати доступ до ресурсу декількох потоків з різних процесів; при цьому можна задати максимальний час очікування доступу до ресурсу.

Саме завдяки цьому мьютекс забезпечується вимога з безпеки при зверненні до інформації, що зберігається.

Ініціалізується робота таймера:

Таймер необхідний для того, щоб з певним інтервалом оновлювати збережену інформацію.

Для цього створюється об'єкт ядра «таймер»:

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;

Завдяки однієї цієї змінної стає можливим одночасне звернення до драйвера відразу декількох призначених для користувача додатків. Єдиним обмеженням для них ялвяется ексклюзивність доступу до інформації про процеси та їх потоках.

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 даного розділу.

3.1.4.4 IOCTL_PROCESS_FIRST і IOCTL_PROCESS_NEXT. Вхідні дані: [немає]

Вихідні дані: структура з базової ннн про процес.

Ця пара IOCTL-запитів дозволяє їх ініціатору послідовно проссматрівать структури, що описують запущені процеси в системі. Кожен з них викликає однойменну функцію ProcessFirst () і ProcessNext () відповідно. Перша функція встановлює вказівник на перший запис, а друга переміщає покажчик на наступну, якщо така є. Результатом виконання кожної з цих функцій є заповнена структура з інформацією оп процесі, якщо не досягнуть кінець списку. У тому випадку, коли кінець списку все-таки досягається, IRP-пакет, тим не менш, позначається як успішно оброблений, але значення кількості переданих байтів встановлюється рівним нулю, що і дозволяє користувацькому додатком правильно розпізнати таку ситуацію і своєчасно припинити посилати драйверу подальші IOCTL_PROCESS_NEXT -запити.

3.1.4.5 IOCTL_THREAD_FIRST і IOCTL_THREAD_NEXT. Вхідні дані: [немає]

Вихідні дані: структура з базової ннн про потік.

Як і в попередньому пункті, ця пара IOCTL-запитів дозволяє їх ініціатору послідовно проссматрівать структури, що описують потоки обраного процесу. Логіка обробки цих запитів аналогічна отримання інформації про процеси.

3.1.4.6 IOCTL_OPEN_THREAD. Вхідні дані: права доступу, унікальний ідентифікатор цільового потоку.

Вихідні дані: описувач цільового потоку.

При обробці даного IOCTL-запиту здійснюється спроба відкрити описувач потоку, що має вказаний ідентифікатор з правами, які були запитані користувальницьким додатком-клієнтом.

3.1.4.6 IOCTL_CLOSE_THREAD. Вхідні дані: описувач цільового потоку.

Вихідні дані: [немає].

Під час обробки цього IOCTL-запиту здійснюється спроба закрити описувач потоку, відкритий раніше за допомогою IOCTL_OPEN_THREAD-запиту.

3.1.4.7 IOCTL_GET_THREAD_CONTEXT. Вхідні дані: структура апаратного контексту, описувач цільового потоку.

Вихідні дані: структура апаратного контексту.

Цей IOCTL-запит найбільш повно використовує можливості API-виклику DeviceIoControl, так як тут задіяні обидва, вхідний і вихідний, буфери. На вхід надходить структура для апаратного контексту з початкові полем CONTEXT:: ContextFlags, що вказує які групи регістрів апаратного контексту повинні бути повернуті в цій структурі при вдалому завершенні запиту. У цьому проекті запрошується весь апаратний контекст.

3.2 для користувача додаток

Для користувача додаток включає в себе два класи: CDialog і CDriver. Як зрозуміло з назв ці класи відповідають відповідно за взаємодію з користувачем через діалогове вікно програми та взаємодія з драйвером переважно через IOCTL-запити.

При запуску примірник користувальницького додатка насамперед намагається встановити драйвер, в тому випадку, якщо це не було зроблено раніше іншим примірником. Якщо установка викликала помилку, то користувачеві видається відповідне повідомлення, в якому в текстовому вигляді вказується причина її виникнення, якщо вона була передбачена, інакше - просто вказується її код. Користувач може запросити установку драйвера ще раз, давши позитивну відповідь на відповідну пропозицію програми. Така процедура повторяться до тих пір, поки установка драйвера не пройде успішно який користувач відмовиться від повторної спроби.

Після цього завантажується спадаючий список запущених процесів, відсортованих в алфавітному порядку за своїм іменам, вибирається перший процес зі списку, і вже його потоки відображаються у другому спадаючому списку. Ці списки оновлюються кожен раз, коли користувач хоче вибрати інший процес чи потік, тому що для цього йому потрібні останні відомості.

Далі створюється таймер, робота якого ніяк не пов'язана з роботою таймера драйвера. Цей таймер відповідає за періодичний висновок отриманої від драйвера інформації на форму діалогу.

Ця інформація виходить через драйвера, як уже говорилося, за допомогою API-виклику DeviceIoControl:

BOOL DeviceIoControl

(HANDLE,

DWORD,

LPVOID, DWORD,

LPVOID, DWORD,

LPDWORD,

LPOVERLAPPED);

HANDLE - описувач пристрою, якому надсилається запит;

DWORD - код IOCTL-запиту;

LPVOID - адреса вхідного буфера;

DWORD - довжина вхідного буфера;

LPVOID - адреса вихідного буфера;

DWORD - довжина вихідного буфера;

LPDWORD - кількість переданих байтів;

LPOVERLAPPED - структура, необхідна при використанні асинхронного виконання запиту, чого немає в цьому додатку.

Використання цього API-виклику повністю инкапсулирован в класі CDriver, в якому для виконання кожного запиту реалізований окремий метод з ім'ям, близьким до назви IOCTL-запиту, що забезпечує інтуїтивне розуміння інтерфейсу цього класу.

Також цей клас інкапсулює в собі використання Менеджера управління сервісами (SCM - Service Control Manager), за допомогою якого здійснюється динамічна установка, запуск, зупинка та видалення драйвера.

4. Технічний розділ

4.1 Вибір операційної системи і середовища програмування

В якості операційної системи була обрана система Widows. Це обумовлено тим, що операційна система DOS вже застаріла в силу багатьох причин (ми вже пішли від ОС, що працюють в однозадачной режимі), а інших операційних систем для персональних машин з гарним інтерфейсом, дійсно зручних для користувача, ще немає. Windows по колишньому залишається найпоширенішою ОС для ПК. Крім того різні середовища розробки програмних продуктів розроблені саме під Windows:

Visual C + +, Visual Basic, Borland C + + Builder, Delphi та інші.

Мовою написання користувацької програми був обраний С + +. Мова С + + дає дуже багаті можливості для програмістів і, мабуть є найбільш поширеним в їхньому середовищі. Це дуже потужний операторний мову. Крім того, він забезпечує достатню свободу в написанні програм, у той час як Pascal ставить дуже вузькі рамки, зокрема, в описі змінних і не дає можливості побудови складних операторних виразів. Мовою написання драйвера був обраний С. Застосування цієї мови забезпечує переносимість меджу системами: максимум, що доведеться зробити - це перезібрати драйвер. В якості середовища розробки була вибрана Microsoft Visual Studio. Net, оскільки вона дає потужні та зручні кошти не тільки візуальної розробки інтерфейсу програмного продукту, але і настройки проектів, що дозволяє ефективно організувати своє робоче місце.

4.2 Інтерфейс

Так виглядає вікно примірника користувальницького додатка «Профілювальники»:

У верхній частині діалогу знаходяться два спадаючих списку, верхній з яких відображає список запущених процесів в системі, а нижній - список потоків цього процесу. За допомогою цих елементів керування можна вказати додатком, за яким процесом і яким потоком цього процесу вести спостереження.

На діалозі є три групи:

Група «Інформація про процес»:

ProcessID - ідентифікатор процесу;

ParentID - ідентифікатор процесу-батька;

BasePriority - базовий пріоритет по-замовчуванню для потоків процесу;

ThreadCount - кількість потоків процесу;

KernelTime - сумарний час, проведений у режимі ядра потоками процесу, 1 одиниця дорівнює 100 нс;

UserTime - сумарний час, проведений у режимі користувача потоками процесу, 1 одиниця дорівнює 100 нс.

Група «Інформація про потік»:

ThreadID - ідентифікатор потоку;

BasePriority - базовий пріоритет потоку;

Priority - пріоритет потоку;

ContextSwitches - кількість перемикань контексту, здійснених потоком;

KernelTime-час, проведене в режимі ядра (1 одиниця дорівнює 100 нс);

UserTime - час, проведений у режимі користувача (1 одиниця дорівнює 100 нс).

WaitTime - момент часу, коли потік перейшов у стан очікування (відлік ведеться від моменту запуску системи).

Група «Контекст потоку»:

Тут представлений апаратний контекст потоку. Більшість додатків очікують від користувача. При спостереженні за потоками такого процесу можна взагалі не побачити які-небудь зміни. Тому для більш наочного перегляду варто запускати завдання, що вимагають великих обчислювальних витрат. Наприклад, WinAmp, за допомогою якого можна програвати музику - той потік, який за це відповідає, відразу видно по зміні регістрів загального призначення. Але найбільш часті зміни в регістрах різного призначення відбуваються в по-справжньому «великовагових» завданнях, наприклад, можна взяти курсовий проект по Машинній графіку.

4.3 Системні вимоги

Драйвер написаний з розрахунком на Windows NT версії 5.x.

Обробка запитів від несколькоіх користувальницьких додатків-клієнтів перевірена тільки на Windows XP Service Pack 2.

Висновок

У результаті роботи над проектом були реалізовані користувальницький додаток, що взаємодіє з Legacy-драйвером. З його допомогою воно отримує базову інформацію про обраний процесі, базову інформацію та апаратний контекст обраного потоку зазначеного процесу. Ця програма є базою для реалізації повноцінних профілювальником додатків для трасування цільових програм і для виявлення в них вузьких місць, що може істотно підвищити ефективність праці програміста і розробляється їм програмного забезпечення.

Список використаної літератури

  1. В. П. Солдатов «Програмування драйверів Windows». Вид. 3-є, перероб. і доп. - М.: ТОВ «Біном-Пресс», 2006 р. - 576 с.: Іл.

  2. М. Руссиновіч, Д. Соломон «Внутрішнє пристрій Microsoft Windows: Windows Server 2003, Windows XP і Windows 2000», 4-е видання.

  3. Дж.Ріхтер «Windows для професіоналів: створення ефективних Win32 програм з урахуванням специфіки 64-розрядної версії Windows» / Пер, англ - 4-е вид. - СПб; Пітер; М.: Видавничо-торговий дім "Російська Редакція", 2001.

  4. Schreiber, Sven B., 1958-Undocumented Windows 2000 secrets: a programmer's cookbook.

  5. Garry Nebbett, Windows NT/2000 Native API.

Додати в блог або на сайт

Цей текст може містити помилки.

Програмування, комп'ютери, інформатика і кібернетика | Курсова
132.1кб. | скачати


Схожі роботи:
Створення додатків на AJAX
Робота з вікнами додатків
Забезпечення продуктивності додатків
Вікна додатків в середовищі Windows
Засоби створення мультимедійних додатків
Різні способи друку з додатків
Створення Web-додатків в середовищі Delphi
Прийоми безпечного програмування веб-додатків на PHP
Розробка додатків архітектури клієнт-сервер за допомогою SQL
© Усі права захищені
написати до нас