Драйвер віртуального диска

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

скачати

Кафедра "Програмне забезпечення ЕОМ та інформаційні технології»
Курсовий проект
з системного програмування
Розрахунково-пояснювальна записка
Тема:
«Драйвер віртуального диска»

ЗМІСТ
ВСТУП
1. АНАЛІТИЧНИЙ РОЗДІЛ
1.1. Постановка завдання
1.2. Архітектура Windows 2000
1.3. Багатошарова архітектура драйверів
1.4. Архітектура драйверів пристроїв зберігання
1.5. Вибір файлової системи
2. КОНСТРУКТОРСЬКИЙ РОЗДІЛ
2.1. Структура класового драйвера
2.2. Організація внутрішнього зберігання даних диска
2.3. Доступ до переданих даними
2.4. Обробка запитів Plug and Play
2.5. Обробка розширених запитів
2.6. Структура драйвера
2.7. Розрахунок геометрії диска
3. ТЕХНОЛОГІЧНИЙ РОЗДІЛ
3.1. Вибір і обгрунтування мови та середовища програмування
3.2. Структури даних класового драйвера
3.3. Блокування вивантаження пристрої
3.4. Процедури драйвера віртуального диска
3.4.1. Ініціалізація драйвера
3.4.2 Обробка запитів запису / читання
3.4.3. Обробка розширених запитів
3.4.4. Обробка запитів Plug and Play
3.4.5. Вивантаження драйвера
3.5. Програма налаштування параметрів віртуального диска
3.6. Установка драйвера
4. ЕКСПЕРИМЕНТАЛЬНО-ДОСЛІДНИЙ РОЗДІЛ
4.1. Опис експериментів
4.2. Результати експериментів
ВИСНОВОК
СПИСОК ВИКОРИСТАНОЇ ЛІТЕРАТУРИ

ВСТУП
В даний час всі ускладнюються програмні продукти та комплекси, що призводить до зростання обсягу оброблюваних даних, ускладнення структури їх подання та зберігання на комп'ютері. Все це призводить до підвищення вимог до швидкості читання і запису даних з файлів а також зменшенню часу доступу до кожного файлу окремо.
Найпоширенішим пристроєм для зберігання даних в даний час є жорсткий диск, який вміє зберігати великі обсяги даних. Час доступу до довільного місця на диску залежить від швидкості переміщення голівки, що зчитує. Але швидкодія механіки диска має межу, і час відповіді для жорсткого диска на кілька порядків вище, ніж для оперативної пам'яті. Тому продуктивність при множинних операціях читання і запису до різних даними катастрофічно падає.
Для вирішення даної проблеми існують такі засоби підвищення продуктивності. Всі записувані і зчитуються дані не відразу пишуться на диск, а зберігаються в певній області оперативної пам'яті - кеші. Але розмір кешу не великий і в ньому зберігаються лише кілька або самих останніх операцій читання запису або найчастіших, в залежності від стратегії кешування.
Однак буферизація тільки на основі оперативної пам'яті в підсистемі введення-виведення виявляється недостатньою - різниця між швидкістю обміну з оперативною пам'яттю, куди процеси поміщають дані для обробки, і швидкістю роботи зовнішнього пристрою часто стає занадто значної, щоб в якості тимчасового буфера можна було б використовувати оперативну пам'ять - її обсягу може просто не вистачити. Для таких випадків необхідно передбачити особливі заходи, і часто як буфер використовується дисковий файл, званий також спула-файлом (від spool - шпулька, теж буфер, тільки для ниток). Типовий приклад застосування спулінга дає організація виведення даних на принтер. Для документів, що друкуються обсяг у кілька десятків мегабайт - не рідкість, тому для їх тимчасового зберігання (а друк кожного документа займає від декількох хвилин до десятків хвилин) обсягу оперативної пам'яті явно недостатньо.
Інший підхід для зберігання даних у пам'яті - створення Ram дисків. У систему додається віртуальний диск, а образ диска розташований в оперативній пам'яті. Такий підхід дозволяє підвищити швидкодію, коли додаток використовує звернення випадкового читання, випадкового запису. Так наприклад, значну частину всіх звернень до даних в сучасних СУБД складають випадкові запити на читання даних. Інша перевага віртуальних дисків - їх можна використовувати для бездискових робочих станцій для проміжних файлів.

1. АНАЛІТИЧНИЙ РОЗДІЛ
1.1 Постановка завдання
До розробляється драйверу віртуального диска пред'являються наступні вимоги:
Розроблюваний драйвер повинен додавати в операційну систему новий віртуальний диск
Для збільшення швидкості передачі, драйвер повинен працювати у блочному режимі передачі даних
Драйвер повинен бути конфігурованим, для драйвера можна задати ім'я, яке присвоюється диску і його розмір
Драйвер повинен визначати обсяг вільної фізичної пам'яті і обмежувати розмір створюваного віртуального диска.
Драйвер може динамічно вивантажуватися із системи і завантажуватися в неї без необхідності перезавантажувати комп'ютер
Оскільки операції читання запису, створення, перейменування і видалення файлів виконує драйвер файлової системи, то потрібно забезпечувати зчитування і запис необхідних файлової системі даних.
Робота драйвера не повинна впливати на роботу інших драйверів.
1.2 Архітектура Windows 2000
Найбільш поширені реалізації Windows 2000 для платформи Intel x86 в одно-або багатопроцесорних конфігураціях, проте існують також версії для DEC Alpha і MIPS. Дана операційна система використовує захищений режим центрального процесора, реалізує механізми віртуальної пам'яті і багатопоточності.
Виконуваний код у Windows 2000 може виконуватися в двох рівнях привілеїв: користувальницькому режимі і режимі ядра. Рівень привілеїв накладає певні обмеження: у режимі користувача не можуть виконуватися привілейовані інструкції процесора і не дозволено звернення до захищених сторінок в пам'яті. Ці обмеження накладаються для забезпечення безпеки роботи системи. Для користувача додаток не повинно мати можливість - в результаті помилки або навмисно - вносити зміни в критичні таблиці операційної системи або в пам'ять інших додатків. Зокрема, такі обмеження забороняють користувача додатком безпосередньо керувати зовнішніми пристроями, тому що кожне з них є ресурсом, що розділяється.
У Windows 2000 забезпечення обміну даними і управління доступом до зовнішнього пристрою як до ресурсу покладається на його драйвер. Введення і виведення в драйверах здійснюється пакетами - IRP (Input / Output Request Packet). Запити на введення / висновок, що їх посилають додатками або іншими драйверами, обробляються драйвером, після чого запитуючої програмою в тому ж пакеті надсилається статус завершення операції. Загальний принцип взаємодії проілюстрований на REF _Ref41953342 \ * Lower \ * MERGEFORMAT рис. 1 .

Рис. SEQ Рис. \ * ARABIC 1. Архітектура введення / виведення Windows 2000.

1.3 Багатошарова архітектура драйверів
Розглянемо як будується архітектура драйверів. Операційна система Windows ® підтримує багатошарову архітектуру драйверів. Кожен пристрій обслуговується ланцюжком драйверів, званої стеком драйверів. Кожен драйвер в стеку ізолює апаратно залежні можливості від вищого рівня.
На рис. 2 показані типи драйверів, які можуть обслуговувати пристрій. У дійсності, деякі з цих типів можуть бути відсутні в стеку.

Рис. 2 Багатошарова архітектура драйверів
Над стеком знаходяться додатки. Вони обробляють запити користувача або інших програм і викликають або підсистему Win 32 або клієнт драйвер режиму користувача.
Клієнт драйвер режиму користувача обробляє запити від додатків або від підсистеми Win32. При отриманні запитів, які вимагають обробки в режимі ядра, він викликає потрібний клієнт драйвер режиму ядра або процедуру обробки запиту через підсистему Win32. Клієнт драйвер реалізуються як динамічні бібліотеки (DLL).
Клієнт драйвер режиму ядра обробляє запити подібно клієнт драйверу в режимі користувача, за винятком того, що обробка йде в режимі ядра.
Класовий і міні класовий драйвер пристрою надає основний набір сервісів обслуговування. Класовий драйвер надає апаратно незалежну підтримку для специфічного класу пристроїв.
Міні класовий драйвер обробляє окремі операції для влаштування з цього класу.
Порт-драйвер (для деяких пристроїв це драйвер хост-контролера чи хост-адаптера) виконує необхідні операції вводу / виводу для порту, хаба або іншої фізичної пристрої, через яке здійснюється підключення. Від типу пристрою і шини через яку він підключений залежить чи буде присутній цей драйвер в стеку.
Всі драйвера пристроїв зберігання мають порт-драйвер. Наприклад, SCSI порт драйвер виконує введення / виведення через шину SCSI.
Міні порт-драйвер обробляє специфічні операції порт-драйвера.
Драйвер апаратної шини знаходиться в самому низу стека. Microsoft надає ці драйвера для всіх шин, як частина операційної системи.
1.4 Архітектура драйверів пристроїв зберігання
Для того, щоб визначити який тип драйвера потрібно розробити, розглянемо більш докладно структуру взаємодія драйверів пристроїв зберігання в ланцюжку драйверів.
Класові і фільтр-драйвера для пристроїв зберігання надають інтерфейс взаємодії між драйверами високого рівня, розташованими в стеку над ними, і порт-драйвером надаються операційною системою.

Рис. 3 Архітектура драйверів пристроїв зберігання
Запит вводу / виводу від додатків користувача обробляється підсистемою вводу / виводу. Підсистема формує пакет запиту на ввід / вивід (IRP), який приходить класовому драйверу пристрою введення виведення через один або кілька проміжних драйверів фільтрів верхнього рівня (як наприклад драйвер файлової системи).
Класовий драйвер введення виведення доповнює отриманий пакет IRP додатковим SCSI блоком запиту (SRB) і надсилає запит порт драйверу через фільтр драйвери нижнього рівня.
Порт-драйвер отримані пакети SRB перетворює до формату для передачі по апаратній шині, і посилає їх адаптера головної шини (HBA), через драйвер вводи виведення шини і один або кілька фільтр драйверів.
Завдяки тому, що віртуальний диск не є реальним фізичним пристроєм, нам не потрібно проводити доступ до апаратної шині і формувати спеціальні пакети SRB. Дані в оперативній пам'яті вже доступні на рівні класового драйвера; недоцільно і не потрібно передавати IRP пакети на нижній рівень.
Таким чином, потрібно розробити класовий драйвер, в який відбуватиметься вся обробка запитів і звернення до даних диска для нашого класу пристроїв віртуального диска.
1.5 Вибір файлової системи
Для того, щоб почати роботу з диском, диск розбивається на розділи. Розділ - це безперервна частина фізичного диска, яку операційна система надає користувачеві як логічний пристрій (використовуються також назви логічний диск і логічний розділ). Саме з логічними пристроями працює користувач, звертаючись до них по символьним іменам, використовуючи, наприклад, позначення А, В, С. В окремому випадку, коли всі дисковий простір охоплюється одним розділом, логічний пристрій являє фізичний пристрій в цілому.
Виходячи з того, що обсяг диска обмежений обсягом наявної вільної оперативної пам'яті, то недоцільно розбивати віртуальний диск на декілька розділом. Створення одного логічного розділу дозволяє спростити логіку пристрої та заощадити місце, яке використовується для опису кожного розділу.
На кожному логічному пристрої може створюватися лише одна файлова система. Для дискових накопичувачів Windows підтримує файлові системи FAT та NTFS.
При виборі файлової системи віртуального диска враховувалися наступні положення. Рамдіск не призначений для довготривалого зберігання даних і при відключенні харчування всі дані втрачаються. Як наслідок проблема фрагментації файлів втрачає свою актуальність і гостроту. Обсяг диску обмежений і потрібно, щоб файлова система під файли метаданих використовувала мінімум вільного місця.
Відмінні властивості NTFS [REF _Ref104536223 \ r \ * MERGEFORMAT 2 ], То що вона орієнтована для підтримки великих файлів, відновлюваності після збоїв і відмов програм і апаратури управління дисками - все це призводить до значного розміру метаданих. Тому мінімальний розмір тому дорівнює 10 Мб, а на практиці використання NTFS виправдано для логічних дисків від 400 Мб.
Файлова система FAT відноситься до ФС з глобальним індексом, і тому метадані складаються з мітки томи, глобальної таблиці диска і Конєв каталогу. Все інше місце вільне і відводиться під створювані файли і каталоги. Той факт, що при кожній операції читання / запису йде звернення до таблиці FAT не впливає на продуктивність, тому що час доступу для оперативної пам'яті мізерно мало в порівнянні з жорстким диском. Також існує кілька версія FAT: 12, 16 і 32. FAT 12 можна використовувати на маленьких дисках від 1 Мб. Використання FAT 32 в основному призначена для томів обсягом у кілька Гб, і мінімальний розмір тому обмежений 512 Мб, цього вона не підходить для віртуального диска.
Таким чином для нашого віртуального диска буде використовуватися файлові системи FAT 12 (коли обсяг диска не перевищує 16 Мб) і FAT 16.

2. КОНСТРУКТОРСЬКИЙ РОЗДІЛ
2.1 Структура класового драйвера
Драйвер реалізується як набір процедур, кожна з яких призначена для реалізації окремого типу звернень до драйвера з боку диспетчера введення / виводу. Процедури, які необхідно підтримувати класовому драйверу наведені в таблиці 1.
Таблиця 1
Процедура
Опис
DriverEntry
Виконується при завантаженні драйвера операційною системою. Тут драйвер реєструє свої інші точки входу і виконує свою загальну ініціалізацію.
Unload
Викликається при вивантаженні драйвера. Тут необхідно звільнити всі витребувані ресурси.
AddDevice
Тут створюється об'єкт-пристрій, відповідний отриманим повідомленням від менеджера пристроїв, і виконується ініціалізація даних, специфічних для даного пристрою.
DispatchPnP
Виконує обробку специфічних Plug & Play запитів, таких як ініціалізація пристрою, таких як ініціалізація пристрої, зупинка, видалення пристрою і обробляти інші запиту
DispatchPower
Виконує обробку запитів з управління живленням пристрої
DispatchSystemControl
Обробляє запити від підсистеми інструментарію Windows (WMI)
DispatchCreate,
DispatchClose,
DispatchRead,
DispatchWrite
Обслуговують запити на читання запис даних для пристрою.
2.2 Організація внутрішнього зберігання даних диска
Визначимо, яку структуру даних оптимально використовувати для зберігання даних на диску. Найпростіший спосіб - розмістити всі дані в одновимірному масиві байт, і адресувати дані за допомогою зсуву від початку диска. Слід врахувати той факт, що при запитах на читання / запис даних, в якості параметрів вказується: зсув у байтах з якого починається передача даних (яке і служить індексом першого байта для читання) і кількість байт для передачі.
Природно, що застосування більш складних структур організації даних (двовимірний масив, списки і т.д.) призведе тільки до додаткових накладних витрат по перетворенню адресації даних.
Таким чином, віртуальний диск буде зберігатися в пам'яті як одновимірний масив байт заданого розміру. Для читання даних, інформація копіюється з області пам'яті образу диска в буфер ініціатора запиту. Для запису - копіювання відбувається в зворотному напрямку.
Наступним кроком виберемо, яким чином ми зарезервуємо пам'ять для диска. Для резервування пам'яті в режимі ядра Windows надає спеціальні системні виклики. Існують наступні типи резервованій пам'яті:
Сторінкова пам'ять (Paged memory) - віртуальна пам'ять, яка може бути переміщена системою на жорсткий диск в будь-який момент часу. У випадку, якщо додаток звернутися до відсутньої у фізичній пам'яті області своєї віртуальної пам'яті, то виникає виключення з відсутності сторінки в пам'яті. У результаті системний обробник перехоплює цей виняток і завантажує у фізичну пам'ять відсутню сторінку. Однак при роботі в режимі ядра, коли рівень пріоритету дорівнює або вище DISPATCH_LEVEL, це виняток створить ситуацію, коли системний обробник не може довантажити сторінку тому його IRQL менше поточного.
Нестранічная пам'ять (Nonpaged memory) - ця пам'ять ніколи не може бути переміщена системою на жорсткий диск і завжди залишається в фізичної оперативної пам'яті. У результаті, звертатися до цієї пам'яті можна при будь-якому рівні IRQL. Обсяг даної пам'яті обмежений навіть при наявності достатнього обсягу фізичної пам'яті в Windows 2000 660 Мбайтами, а в Windows XP 1300 Мбайтами.
Оскільки звернення до образу диска відбувається при рівні привілеїв PASSIVE_LEVEL або DISPATCH_LEVEL, але в особливих ситуаціях цей пріоритет може бути вище. Тому, щоб уникнути виникнення ситуації, коли сторінка відсутня в пам'яті, і ми її не зможемо довантажити - віртуальний диск буде використовувати нестранічную пам'ять. Додатково ми збільшимо швидкодію драйвера, тому що вже не потрібно довантажувати відсутні сторінки з жорсткого диска.
2.3 Доступ до переданих даними
Розглянемо, яким чином драйвер може отримати доступ до переданих даних. Для користувача процес, викликаючи функцію API (наприклад, WriteFile), передає їй покажчик на буфер, в якому розміщується записувана інформація. Однак, рухаючись віртуальний адреса дійсно буде вказувати на записувану інформацію тільки в контексті даного процесу. Операції ж введення / виводу в драйвері відбуваються в контексті довільного процесу. Через зміну таблиць сторінкового перетворення при перемиканні процесів використовувати переданий віртуальний адреса в довільному контексті абсолютно неприпустимо.
Для операцій вводу / виводу архітектура операційної передбачає два спеціальних методу передачі буфера даних, що належить ініціатору запиту:
Буферізірованний (buffered I / O). Драйверу в пакеті запиту вводу / виводу передається покажчик на копію вихідного буфера в невивантажуваного пам'яті (поле AssociatedIrp.SystemBuffer). Підсистема вводу / виводу відповідає за точне відповідність вмісту цього буфера переданих даним. Цей метод, в основному, використовується для пристроїв, не зраджують великих обсягів даних: маніпулятори, низькошвидкісні комунікаційні лінії і т. п.
Прямий (direct I / O). У цьому випадку система блокує сторінки користувальницького буфера, щоб вони не були вивантажені на диск під час передачі даних. Розташування користувацького буферу в фізичної пам'яті описується структурою MDL (Memory Descriptor List), доступної в пакеті запиту вводу / виводу через поле MdlAddress. З цієї структурі необхідно налаштувати системну таблицю сторінок на той самий буфер у фізичній пам'яті. Це здійснюється функцією MmGetSystemAddressForMdlSafe, яка повертає віртуальний адреса буфера в системній області пам'яті. Даний метод ефективний з великими обсягами даних, наприклад, при роботі з дисковими накопичувачами.
Для драйвера віртуального диска буде використовуватися прямий метод, який забезпечує можливість обміну великими даними і високу швидкість передачі.
2.4 Обробка запитів Plug and Play
У процесі роботи диспетчер введення виведення може динамічно керувати станом пристрої: запускати, зупиняти і вивантажувати. Реалізація цих функцій драйвером пристрою зберігання забезпечує при обробці специфічних PnP IRP пакетів. У таблиці 2 приведені описи IRP пакетів, які повинні підтримуватися.
Таблиця 2
IRP_MN_Xxx
Опис
IRP_MN_START_DEVICE
Ініціалізація пристрої з заданими ресурсами
IRP_MN_QUERY_STOP_DEVICE
Перевірка здійсненності зупинки пристрої для перерозподілу ресурсів
IRP_MN_STOP_DEVICE
Зупинка пристрої з потенційною можливість. перезапуску або видалення з системи
IRP_MN_CANCEL_STOP_DEVICE
Повідомляє, що попередній запит на зупинку не отримає подальшого розвитку
IRP_MN_QUERY_REMOVE_DEVICE
Перевірка здійсненності безпечного видалення пристрою
IRP_MN_REMOVE_DEVICE
Виконати безпечне видалення пристрою
IRP_MN_CANCEL_REMOVE_DEVICE
Повідомляє, що попередній запит на видалення не отримає подальшого розвитку
IRP_MN_SURPRISE_REMOVAL
Повідомляє, що пристрій було видалено без попереднього попередження
Стосовно до віртуального диска велика частина цих повідомлень не тягне жодних додаткових дій, тому що у рамдіска немає додаткових буферів, дані з які повинні бути записані на диск при зупинці пристрою, або підтримки функцій управління електроживленням пристрою. Тому для віртуального диску існує внутрішня змінна, яка зберігає поточний стан пристрою.
Основним станом диска буде WORKING - коли диск знаходиться в робочому стані. Решта стану (наведені у таблиці 3) носять інформативний характер, щоб дізнатися поточний режим роботи диска. При операціях доступу до пристрою буде здійснюватися перевірка стану WORKING, і тільки при ньому доступ до диска дозволений.
Таблиця 3
Стан
Значення
STOPPED
Пристрій зупинено
WORKING
Пристрій працює
PENDINGSTOP
Пристрій чекає зупинки
PENDINGREMOVE
Пристрій очікує видалення
SURPRISEREMOVED
Пристрій видалено без попереднього попередження
REMOVED
Пристрій видалено
UNKNOWN
Пристрій в помилковому стані
2.5 Обробка розширених запитів
Для управління самі пристроєм диспетчер введення виведення посилає драйверу пакет з кодом управління вводом / виводом (IOCTL). Які саме коди управління буде посилатися залежить від типу пристрою. Про те які коди повинен обов'язково підтримувати класовий драйвер точно нічого не сказано. Тому наведений далі список кодів управління був отриманий в процесі налагодження драйвера, коли записувалися одержувані драйвером IOCTL і з'ясовувалася їхня функція.
Список кодів управління:
IOCTL_DISK_GET_PARTITION_INFO - повідомити про тип, розмір і природі розділу диска.
IOCTL_DISK_GET_MEDIA_TYPES,
IOCTL_DISK_GET_DRIVE_GEOMETRY - повідомити про геометрію диска (кількість циліндрів, доріжок, секторів)
IOCTL_DISK_IS_WRITABLE - перевірка чи можна на диск записувати дані
IOCTL_DISK_SET_PARTITION_INFO - змінити тип розділу
IOCTL_MOUNTMGR_QUERY_POINTS - повідомити про символічну посиланням для томи
IOCTL_MOUNTDEV_QUERY_DEVICE_NAME - повідомити про ім'я пристрою
IOCTL_DISK_FORMAT_TRACKS - форматувати доріжки
IOCTL_DISK_MEDIA_REMOVAL - блокувати витяг носія
IOCTL_DISK_CHECK_VERIFY - перевірити, змінився чи носій (для знімних дисків)
2.6 Структура драйвера
З урахуванням усього вищевикладеного потрібно розробити класовий драйвер, який має монолітну структуру. Драйвер буде мати такі частини:
Ініціалізація драйвера
Обробка запитів PnP і управління живленням
Обробка запитів запису / читання даних
Обробка розширених запитів
Вивантаження драйвера
2.7 Розрахунок геометрії диска
При отриманні розширеного запиту IOCTL_DISK_GET_DRIVE_GEOMETRY або IOCTL_DISK_GET_MEDIA_TYPES потрібно ініціатору запиту передати інформацію про геометрію диска. Драйвер віртуального диска заповнює стандартну структуру Windows DISK_GEOMETRY, де вказуються такі параметри: Cylinders - кількість циліндрів
TracksPerCylinder - кількість доріжок на циліндр
SectorsPerTrack - кількість секторів на доріжку
BytesPerSector - розмір сектора в байтах
MediaType - тип носія
Тип носія для жорстких дисків повинен бути рівний FixedMedia.
Розмір сектора приймемо рівним 512 байтам, це стандарт де-факто для майже всіх сучасних дискових накопичувачів.
Кількість доріжок і секторів буде фіксованим
TracksPerCylinder = 16 SectorsPerTrack = 32
Обчислюємо кількість циліндрів диска: Cylinders = DiskSize / (BytesPerSector * SectorsPerTrack * TracksPerCylinder),
де DiskSize - розмір диска.
Якщо кількість циліндрів перевищує 1023 (таке обмеження було введено для сумісності зі старими версіями BIOS), то кількість секторів на доріжку збільшується вдвічі і буде дорівнює 64.
Таким чином максимальний розмір рамдіска, для якого число циліндрів не перевищує 1023 дорівнює:
MaxDiskSize = 1023 * 16 * 64 * 512 = 536346624 байт = 511.5 Мбайт,
що більш ніж достатньо для нашої версії рамдіска.

3. ТЕХНОЛОГІЧНИЙ РОЗДІЛ
3.1 Вибір та обгрунтування мови та середовища програмування
Для розробки драйвера віртуального диска застосовувався пакет DDK (Driver Development Kit), який включає в себе всі необхідні заголовки та бібліотеки. Пакет DDK орієнтований на мову С. Драйвер розроблявся на Windows 2003 Server DDK, тому що вона включає в себе як засоби для розробки драйверів для Windows 2003 Server, так і останні версії засобів для розробки драйверів ОС Windows 2000 і XP.
В якості інтегрованого середовища розробки застосовувався Microsoft Visual C + + 6. Він надає зручний інтерфейс для написання програмного коду на C, і дозволяє компілювати його використовуючи бібліотеки пакету DDK.
Для налагодження роботи драйвера застосовувалися програми DebugView і DriverMonitor з пакету NuMega Driver Studio 2.0, які дозволяють переглядати налагодження повідомлення драйвера.
3.2 Структури даних класового драйвера
Параметри диска і його стан зберігаються в розширенні пристрої - структурі DEVICE_EXTENSION. Вона завжди доступна через об'єкт-пристрій (DEVICE_OBJECT, поле DeviceExtension), вона реалізує найбільш зручний і рекомендований Microsoft спосіб зберігання таких даних. Структуру розширення визначає розробник драйвера. Ми її визначимо так:
typedef struct _DEVICE_EXTENSION
{PDEVICE_OBJECT DeviceObject;
PDEVICE_OBJECT LowerDeviceObject;
DEVICE_STATE DevState; / / Поточний стан пристрій
IO_REMOVE_LOCK RemoveLock; / / Блокування пристрою
ULONG Flags; / / Прапор пристрої
PUCHAR DiskImage; / / Покажчик на образ диска
DISK_GEOMETRY DiskGeometry; / / Параметри геометрії диска
DISK_INFO DiskRegInfo; / / Параметри диска з реєстру
UNICODE_STRING SymbolicLink; / / Символічна посилання
} DEVICE_EXTENSION, * PDEVICE_EXTENSION;
Поля DeviceObject і LowerDeviceObject полегшують доступ до об'єктів-пристроїв з різних ділянок драйвера. Вони знадобляться надалі. Поле RemoveLock служить для блокування видалення пристрою. DiskImage - містить покажчик на одновимірний масив - образ диска. Поле DiskGeometry зберігає геометрію диска. DevState містить поточний стан пристрою. SymbolicLink - символічна посилання, яка служить для звернення до драйвера.
Поле DiskRegInfo описує параметри, які розташовані в реєстрі і для драйвера для створення диска:
typedef struct _DISK_INFO
{ULONG DiskSize; / / Розмір диска в байтах
ULONG RootDirEntries; / / Кількість елементів в корені
ULONG SectorsPerCluster; / / Секторів на кластер
UNICODE_STRING DriveLetter; / / Буква диска
} DISK_INFO, * PDISK_INFO;
3.3 Блокування вивантаження пристрої
Коли система робить видалення нижчестоящого пристрою, вона посилає йому PnP-запит з функцією IRP_MN_REMOVE_DEVICE. Якщо ж у цей момент драйвер чимось зайнятий, то він повинен захистити свій пристрій від передчасної вивантаження з пам'яті. Для цього в системі передбачено об'єкт "блокування вивантаження» (remove lock). Він може бути «захоплений» і «звільнений». Усередині цього об'єкта є лічильник. Цей лічильник встановлюється в нуль при ініціалізації об'єкту (функція IoInitializeRemoveLock). Лічильник збільшується на одиницю при захопленні об'єкту (IoAcquireRemoveLock) перед обробкою запиту і зменшується при звільненні (IoReleaseRemoveLock).
Оброблювач PnP-запиту з функцією IRP_MN_REMOVE_DEVICE гарантовано виконується в контексті системного процесу. У цьому обробнику перед видаленням об'єкта-пристрої над блокуванням виконується дія «звільнити і чекати» (IoReleaseRemoveLockAndWait). Воно зменшує лічильник на одиницю і блокує системний процес, поки лічильник не стане рівним нулю. Отже, поки драйвер обробляє хоча б один запит до пристрою, цей пристрій не буде видалено.
3.4 Процедури драйвера віртуального диска
3.4.1 Ініціалізація драйвера
Почнемо з процедури, що викликається при ініціалізації драйвера - DriverEntry. Вона визначається таким чином:
NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
Тип NTSATUS, відповідний повертається значенням, визначає тип помилки. Багато функцій драйвера повертають значення цього типу. Якщо робота проходить успішно, результат приймає значення STATUS_SUCCESS.
В цій процедурі необхідно повідомити системі інші процедури обробки пакетів IRP:
Оскільки нам потрібно зберегти параметри про шляхи реєстру і ознака того, що ініціалізіровалось чи сам пристрій, то в структурі розширення драйвера заведемо наступні поля:
DriverObject-> MajorFunction [IRP_MJ_CREATE] = RamDskCreateClose; DriverObject-> MajorFunction [IRP_MJ_CLOSE] = RamDskCreateClose;
DriverObject-> MajorFunction [IRP_MJ_READ] = RamDskReadWrite;
DriverObject-> MajorFunction [IRP_MJ_WRITE] = RamDskReadWrite;
DriverObject-> MajorFunction [IRP_MJ_DEVICE_CONTROL] = RamDskIOCtl;
DriverObject-> MajorFunction [IRP_MJ_PNP] = RamDskDispatchPnp;
DriverObject-> MajorFunction [IRP_MJ_POWER] = RamDskDispatchPower;
DriverObject-> MajorFunction [IRP_MJ_SYSTEM_CONTROL] = RamDskDispatchSystemControl;
DriverObject-> DriverExtension-> AddDevice = RamDskAddDevice;
DriverObject-> DriverUnload = RamDskUnload;
Масив MajorFunction встановлює відповідність між кодом типу запиту IRP_MJ_ NNN та диспетчерської функцією, яка його обробляє. Наш драйвер обробляє запити на відкриття та закриття пристрої (RamDskCreateClose), читання та запис (RamDskReadWrite), розширені запити обробки IOCTL (RamDskIOCtl), запити Plug and Play (RamDskDispatchPnp), функції управління живленням (RamDskDispatchPower), запити від підсистеми інструментарію Windows (RamDskDispatchSystemControl ).
Сам об'єкт функціонального пристрою, який буде відповідати за наш пристрій, створюється далі, коли система викличе процедуру RamDskAddDevice. Але для його створення необхідно дізнатися шлях реєстру з параметрами драйвера, а також необхідний ознака, що пристрій додано. Дані параметри будемо зберігати в структурі розширення драйвера. Поля і розмір структури вибираються розробником, для зберігання потрібних йому даних. Оголосимо структуру розширення драйвера наступним чином:
typedef struct _RAMDSK_DRIVER_EXTENSION
{UNICODE_STRING RegistryPath;
ULONG DeviceInitialized;
} RAMDSK_DRIVER_EXTENSION, * PRAMDSK_DRIVER_EXTENSION;
У процедурі DriverEntry ми створюємо екземпляр структури і ініціалізували її поля: копіюємо шлях реєстру RegistryPath, який передається в параметрах процедури, і скидаємо ознака DeviceInitialized того, що пристрій додано.
status = IoAllocateDriverObjectExtension (DriverObject,
RAMDSK_DRIVER_EXTENSION_KEY,
sizeof (RAMDSK_DRIVER_EXTENSION),
& DriverExtension);
/ / Збережемо шлях реєстру
driverExtension-> RegistryPath.Length = RegistryPath-> Length;
driverExtension-> RegistryPath.MaximumLength = RegistryPath-> MaximumLength +
sizeof (UNICODE_NULL);
driverExtension-> RegistryPath.Buffer = ExAllocatePoolWithTag (PagedPool,
driverExtension-> RegistryPath.MaximumLength,
RAMDSK_TAG_GENERAL);
RtlCopyUnicodeString (& (driverExtension-> RegistryPath), RegistryPath);
driverExtension-> DeviceInitialized = FALSE;
Процедура RamDskAddDevice буде викликана, коли буде виявлено пристрій. Її дія полягає у створенні об'єкта пристрою для диску і створення символічних посилань для нього.
Перевіримо, може бути вже створений об'єкт пристрій, а процедура RamDskAddDevice викликається повторно. Для цього проаналізуємо ознака DeviceInitialized розширення драйвера:
/ / Може прісутствоать тільки один пристрій, якщо повторно викликається запит
/ / AddDevice для наступного устрою, відхилив запит
if (driverExtension-> DeviceInitialized == TRUE)
{DBGPRINT (DBG_COMP_INIT, DBG_LEVEL_ERROR, ("Device exists \ n"));
return STATUS_DEVICE_ALREADY_ATTACHED;}
Якщо процедура RamDskAddDevice викликана в перший раз, створюємо об'єкт пристрій з типом дисковий накопичувач (FILE_DEVICE_DISK) і ініціалізували розширення пристрої DEVICE_EXTENSION:
status = IoCreateDevice (
DriverObject,
sizeof (DEVICE_EXTENSION),
& UniDeviceName,
FILE_DEVICE_DISK,
(FILE_DEVICE_SECURE_OPEN),
FALSE,
& FunctionDeviceObject);
Якщо створення пройшло успішно, ми читаємо параметри диска з реєстру і встановлюємо початковий стан пристрою зупинено (STOPPED) в розширенні пристрою, а також встановлюємо прапори сигналізують про те, що ми використовуємо прямий метод передачі даних (DO_DIRECT_IO) та процедури драйвера знаходяться в сторінкової пам'яті ( DO_POWER_PAGABLE):
RamDskQueryDiskRegParameters (& driverExtension-> RegistryPath, & devExt-> DiskRegInfo);
devExt-> DevState = STOPPED; / / стан зупинено
/ / Встановимо прапори
functionDeviceObject-> Flags | = DO_POWER_PAGABLE;
functionDeviceObject-> Flags | = DO_DIRECT_IO;
/ / Резервуючи пам'ять під образ диска
devExt-> DiskImage =
ExAllocatePoolWithTag (NonPagedPool,
devExt-> DiskRegInfo.DiskSize,
RAMDSK_TAG_DISK);
Останньою операцією ми резервуємо пам'ять в нестранічном пулі під образ диска (розмір образу devExt-> DiskRegInfo.DiskSize байт).
Оскільки виділена пам'ять може містити довільні дані, необхідно записати метадані файлової системи і підрахувати геометрію диска, що робиться викликом
RamDskFormatDisk (functionDeviceObject);
Створюємо символічну посилання, яка асоціює з нашим драйвером зазначену в параметрах букву диска і має формат «\ DosDevices \ X:», де «Х» - буква диска.
RtlInitUnicodeString (& uniWin32Name, DOS_DEVICE_NAME);
devExt-> SymbolicLink.MaximumLength = DOS_DEVNAME_LENGTH;
devExt-> SymbolicLink.Length = uniWin32Name.Length;
RtlCopyUnicodeString (& (devExt-> SymbolicLink), & uniWin32Name);
RtlAppendUnicodeStringToString (& (devExt-> SymbolicLink), & (devExt-> DiskRegInfo.DriveLetter));
/ / Створюємо символьне посилання в просторі імен Win32
status = IoCreateSymbolicLink (& devExt-> SymbolicLink, & uniDeviceName);
Якщо символічна посилання створена без помилок, то встановимо в розширенні пристрої прапор, що символічна посилання створена і пристрій підключається до стека пристроїв:. При вивантаженні драйвера якщо прапорець встановлений буде віддалятися і символічна посилання драйвера:
devExt-> Flags | = FLAG_LINK_CREATED;
devExt-> LowerDeviceObject = / / підключення до стека пристроїв
IoAttachDeviceToDeviceStack (functionDeviceObject, PhysicalDeviceObject);
/ / Скинемо прапор DO_DEVICE_INITIALIZING
functionDeviceObject-> Flags & = ~ DO_DEVICE_INITIALIZING;
/ / Пристрій додано успішно
driverExtension-> DeviceInitialized = TRUE;
Останньою операцією встановлюється ознака DeviceInitialized розширення драйвера, що пристрій додано.
3.4.2 Обробка запитів запису / читання
Коли користувальницький додаток або компонент ядра намагається відкрити драйвер віртуального диска для запису або читання, то диспетчер введення виведення формує запити IRP з кодом IRP_MJ_CREATE. При закритті обробляється запит IRP_MJ_CLOSE. Особливих дій в обробці цих пакетів не потрібно, тому драйвер повертає успішний статус завершення. У даному драйвері обидва пакети обробляються в одній процедурі RamDskCreateClose:
switch (irpStack-> MajorFunction)
{Case IRP_MJ_CREATE:
DBGPRINT (DBG_COMP_INIT, DBG_LEVEL_INFO, ("IRP_MJ_CREATE (% p) \ n", Irp));
COMPLETE_REQUEST (Irp, status, 0);
break;
case IRP_MJ_CLOSE:
DBGPRINT (DBG_COMP_INIT, DBG_LEVEL_INFO, ("IRP_MJ_CLOSE (% p) \ n", Irp));
COMPLETE_REQUEST (Irp, status, 0);
break;
default:
status = STATUS_NOT_IMPLEMENTED;
COMPLETE_REQUEST (Irp, status, 0);
ASSERTMSG ("BUG: we should never get here", 0);
break;
} / / Switch
В якості перевірки, якщо ми отримали пакет з іншим кодом, то повернемо помилковий статус.
Обробка запитів на запис або читання, також реалізується в одній процедурі, тому що відмінності в діях полягають у напрямку копіювання даних: з буфера в образ диска або навпаки. Для виконання запиту, потрібно, щоб віртуальний диск знаходився в стані WORKING:
if (devExt-> DevState! = WORKING)
{/ / Пристрій не готове або вилучено, скасувати будь-які запити
DBGPRINT (DBG_COMP_READ, DBG_LEVEL_WARN, ("Device not ready \ n"));
status = STATUS_INVALID_DEVICE_STATE;
COMPLETE_REQUEST (Irp, status, information);}
Далі потрібно перевірити, що передані параметри (початковий зсув Parameters.Read.ByteOffset і кількість байт Parameters.Read.Length) не виходять за межі нашого диска, щоб забезпечити коректність операції читання / запису. Додатково кількість байт повинна бути кратна розміру сектора на диску:
if (RtlLargeIntegerGreaterThan (
RtlLargeIntegerAdd (
irpStack-> Parameters.Read.ByteOffset,
RtlConvertUlongToLargeInteger (irpStack-> Parameters.Read.Length)),
RtlConvertUlongToLargeInteger (devExt-> DiskRegInfo.DiskSize)) | |
(IrpStack-> Parameters.Read.Length & (devExt-> DiskGeometry.BytesPerSector - 1)))
{DBGPRINT (DBG_COMP_READ, DBG_LEVEL_ERROR,
("Error invalid parameter \ n"
"ByteOffset:% x \ n"
"Length:% d \ n"
"Operation:% x \ n",
irpStack-> Parameters.Read.ByteOffset,
irpStack-> Parameters.Read.Length,
irpStack-> MajorFunction));
status = STATUS_INVALID_PARAMETER;
COMPLETE_REQUEST (Irp, status, information);
IoReleaseRemoveLock (& ​​devExt-> RemoveLock, Irp);
return status;}
Якщо який-небудь з параметрів не вірний, то статус обробки запиту буде дорівнює STATUS_INVALID_PARAMETER (невірні параметри).
Драйвер використовує прямий метод передачі буфера даних, нам передається MDL список для буфера користувача в параметрі Irp-> MdlAddress, який ми відображаємо в адресний простір ядра за допомогою функції MmGetSystemAddressForMdlSafe:
ASSERT (Irp-> MdlAddress! = NULL);
currentAddress = MmGetSystemAddressForMdlSafe (Irp-> MdlAddress, NormalPagePriority);
if (currentAddress == NULL)
{Status = STATUS_INSUFFICIENT_RESOURCES;
COMPLETE_REQUEST (Irp, status, information);
IoReleaseRemoveLock (& ​​devExt-> RemoveLock, Irp);
DBGPRINT (DBG_COMP_READ, DBG_LEVEL_ERROR, ("Unable to get the system-space virtual address \ n"));
return status;}
Коли адресу отриманий адреса буфера (currentAddress), можна зробити копіювання даних за допомогою функції RtlMoveMemory
information = irpStack-> Parameters.Read.Length;
switch (irpStack-> MajorFunction)
{Case IRP_MJ_READ:
RtlMoveMemory (
currentAddress,
devExt-> DiskImage + irpStack-> Parameters.Read.ByteOffset.LowPart,
irpStack-> Parameters.Read.Length);
break;
case IRP_MJ_WRITE:
RtlMoveMemory (
devExt-> DiskImage + irpStack-> Parameters.Read.ByteOffset.LowPart,
currentAddress, irpStack-> Parameters.Read.Length);
break;
default:
information = 0;
break;}
status = STATUS_SUCCESS;
COMPLETE_REQUEST (Irp, status, information);
При цьому полі information містить кількість байт, які були записані / прочитані.
3.4.3 Обробка розширених запитів
Оскільки наш драйвер звертається з віртуальним диском, при наступних запитах виконувати будь-яких дій не потрібно, просто повідомити, що запит успішно оброблений. Це запити:
case IOCTL_DISK_IS_WRITABLE: / / перевірка чи можна на диск записувати дані
{DBGPRINT (DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("IOCTL_DISK_IS_WRITABLE \ n"));
status = STATUS_SUCCESS;
break;}
case IOCTL_MOUNTMGR_QUERY_POINTS: / / повідомити про символічну посиланням для томи
{DBGPRINT (DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("IOCTL_MOUNTMGR_QUERY_POINTS \ n"));
status = STATUS_INVALID_DEVICE_REQUEST;
break;}
case IOCTL_DISK_FORMAT_TRACKS: / / Форматувати доріжки
{DBGPRINT (DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("IOCTL_DISK_FORMAT_TRACKS \ n"));
status = STATUS_SUCCESS;
break;}
case IOCTL_DISK_MEDIA_REMOVAL: / / блокувати витяг носія
{DBGPRINT (DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("IOCTL_DISK_MEDIA_REMOVAL \ n"));
status = STATUS_SUCCESS;
break;}
case IOCTL_DISK_VERIFY: / / провреіть дані
{PVERIFY_INFORMATION verifyInformation;
DBGPRINT (DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("IOCTL_DISK_VERIFY \ n"));
status = STATUS_SUCCESS;
break;}
case IOCTL_DISK_CHECK_VERIFY: / / перевірити, змінився чи носій
{DBGPRINT (DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("IOCTL_DISK_CHECK_VERIFY \ n"));
status = STATUS_SUCCESS;
break;}
Запит IOCTL_DISK_GET_PARTITION_INFO, вимагає повідомити інформацію про розділи на диску. На рамдіске є один розділ. Результатом обробки запиту буде структура PARTITION_INFORMATION
typedef struct _PARTITION_INFORMATION
{LARGE_INTEGER Помилка! Неприпустимий об'єкт гіперпосилання.; / / Зсув, з якого починається розділ LARGE_INTEGER Помилка! Неприпустимий об'єкт гіперпосилання.; / / Розмір розділу DWORD Помилка! Неприпустимий об'єкт гіперпосилання.; / / Прихованих секторів DWORD Помилка! Неприпустимий об'єкт гіперпосилання.; / / Порядковий номер розділу BYTE Помилка! Неприпустимий об'єкт гіперпосилання.; / / Тип розділу BOOLEAN Помилка! Неприпустимий об'єкт гіперпосилання.; / / TRUE - розділ є завантажувальним BOOLEAN Помилка! Неприпустимий об'єкт гіперпосилання.; / / Розпізнано чи розділ BOOLEAN Помилка! Неприпустимий об'єкт гіперпосилання.; / / TRUE - змінилися параметри розділу} Тип розділу Помилка! Неприпустимий об'єкт гіперпосилання. Для віртуального диска може бути PARTITION_FAT_12 або PARTITION_FAT_16 (він визначається при ініціалізації і зберігається в розширенні драйвера). Інші поля заповнюються таким чином: case IOCTL_DISK_GET_PARTITION_INFO:
{DBGPRINT (DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("IOCTL_DISK_GET_PARTITION_INFO \ n"));
if (irpStack-> Parameters.DeviceIoControl.OutputBufferLength <sizeof (PARTITION_INFORMATION))
{DBGPRINT (DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("Output buffer too small ... \ n"));
status = STATUS_BUFFER_TOO_SMALL; / / Потрібен буфер більше
information = sizeof (PARTITION_INFORMATION);}
else
{PPARTITION_INFORMATION outputBuffer;
outputBuffer = (PPARTITION_INFORMATION) Irp-> AssociatedIrp.SystemBuffer;
outputBuffer-> PartitionType = (UCHAR) devExt-> DiskRegInfo.PartitionType;
outputBuffer-> BootIndicator = FALSE;
outputBuffer-> RecognizedPartition = FALSE
outputBuffer-> RewritePartition = FALSE;
outputBuffer-> StartingOffset = RtlConvertUlongToLargeInteger (0);
outputBuffer-> PartitionLength = RtlConvertUlongToLargeInteger (devExt-> DiskRegInfo.DiskSize);
outputBuffer-> HiddenSectors = (ULONG) (1L);
outputBuffer-> PartitionNumber = (ULONG) (1L);
status = STATUS_SUCCESS;
information = sizeof (PARTITION_INFORMATION);}
break;}
Запити IOCTL_DISK_GET_MEDIA_TYPES, IOCTL_DISK_GET_DRIVE _GEOMETRY потрібні для повчання інформації про геометрію диска, для цього треба заповнити структуру DISK_GEOMETRY описану в розділі 2.7.:
typedef struct _DISK_GEOMETRY {
LARGE_INTEGER Помилка! Неприпустимий об'єкт гіперпосилання.; / / Кількість циліндрів MEDIA_TYPE Помилка! Неприпустимий об'єкт гіперпосилання.; / / Тип носія ULONG Помилка! Неприпустимий об'єкт гіперпосилання.; / / Кількість доріжок на циліндр ULONG Помилка! Неприпустимий об'єкт гіперпосилання.; / / Кількість секторів на доріжку ULONG Помилка! Неприпустимий об'єкт гіперпосилання.; / / Розмір сектора в байтах} DISK_GEOMETRY, * PDISK_GEOMETRY;
Всі інші параметри заздалегідь розраховані та визначені при ініціалізації, і потрібно тільки цю структуру скопіювати з розширення пристрої:
PDISK_GEOMETRY outputBuffer;
outputBuffer = (PDISK_GEOMETRY) Irp-> AssociatedIrp.SystemBuffer;
RtlCopyMemory (outputBuffer, & (devExt-> DiskGeometry), sizeof (DISK_GEOMETRY));
status = STATUS_SUCCESS;
DBGPRINT (DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("IOCTL_DISK_GET_DRIVE_GEOMETRY-OK! \ N"));
information = sizeof (DISK_GEOMETRY);
3.4.4 Обробка запитів Plug and Play
При запуску пристрою ми отримуємо IRP пакет з кодом IRP_MN_START_DEVICE:
case IRP_MN_START_DEVICE:
{KeInitializeEvent (& event, NotificationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext (Irp);
IoSetCompletionRoutine (Irp, (PIO_COMPLETION_ROUTINE) RamDskIoCompletionRoutine,
(PVOID) & event, TRUE, TRUE, TRUE);
status = IoCallDriver (devExt-> LowerDeviceObject, Irp);
if (status == STATUS_PENDING)
{KeWaitForSingleObject (& event, Executive, KernelMode, FALSE, NULL);}
if (NT_SUCCESS (status))
{/ / Успішно запущений нижній драйвер
devExt-> DevState = WORKING;}
COMPLETE_REQUEST (Irp, status, 0);
break;}
Ми пересилаємо пакет драйверу, який знаходиться нижче за допомогою виклику функції IoCallDriver. Якщо нижні драйвер знаходиться в стані обробки (STATUS_PENDING), то просто приспаний наш драйвер за допомогою функції KeWaitForSingleObject і прокидаємося за подією завершення операції нижнім драйвером.
При зупинці пристрою (IRP_MN_STOP_DEVICE), просто пересилаємо запит нижньому драйверу і встановлюємо стан пристрою STOPPED:
case IRP_MN_STOP_DEVICE:
{DevExt-> DevState = STOPPED;
Irp-> IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation (Irp);
status = IoCallDriver (devExt-> LowerDeviceObject, Irp);
break;}
При обробці запитів IRP_MN_SURPRISE_REMOVAL, IRP_MN_QUERY_ REMOVE_DEVICE, IRP_MN_QUERY_STOP_DEVICE, виробляються аналогічні дії, з установкою відповідного статусу:
case IRP_MN_QUERY_STOP_DEVICE:
{DevExt-> DevState = PENDINGSTOP;
Irp-> IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation (Irp);
status = IoCallDriver (devExt-> LowerDeviceObject, Irp);
break;}
case IRP_MN_STOP_DEVICE:
{DevExt-> DevState = STOPPED;
Irp-> IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation (Irp);
status = IoCallDriver (devExt-> LowerDeviceObject, Irp);
break;}
case IRP_MN_QUERY_REMOVE_DEVICE:
{DevExt-> DevState = PENDINGREMOVE;
Irp-> IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation (Irp);
status = IoCallDriver (devExt-> LowerDeviceObject, Irp);
break;}
case IRP_MN_SURPRISE_REMOVAL:
{DevExt-> DevState = SURPRISEREMOVED;
Irp-> IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation (Irp);
status = IoCallDriver (devExt-> LowerDeviceObject, Irp);
break;}
При видаленні пристрою IRP_MN_REMOVE_DEVICE, ми викликаємо функцію RamDskRemoveDevice, яка звільняє виділену при ініціалізації пам'ять, символічне посилання (вона буде описана далі):
case IRP_MN_REMOVE_DEVICE:
{RamDskRemoveDevice (DeviceObject, Irp);
lockHeld = FALSE;
break;}
3.4.5 Вивантаження драйвера
Вивантаження драйвера складається з двох частин: видалення об'єкта пристрої та вивантаження самого драйвера.
При видаленні об'єкта пристрої, менеджер введення виведення посилає IRP пакет IRP_MN_REMOVE_DEVICE, тоді викликається процедура RamDskRemove Device. IRP пакет передаємо далі драйверу нижнього рівня за допомогою функції IoCallDriver. Статус драйвера встановлюється в стан пристрій видалено, щоб нові запити не могли бути виконані. За допомогою функції IoReleaseRemoveLockAndWait чекаємо поки поточні запити не оброблена.
Irp-> IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation (Irp);
status = IoCallDriver (devExt-> LowerDeviceObject, Irp);
devExt-> DevState = REMOVED;
IoReleaseRemoveLockAndWait (& devExt-> RemoveLock, Irp);
driverExtension = IoGetDriverObjectExtension (DeviceObject-> DriverObject,
RAMDSK_DRIVER_EXTENSION_KEY);
ASSERT (driverExtension! = NULL);
driverExtension-> DeviceInitialized = FALSE;
RamDskCleanUp (DeviceObject);
Перед видаленням розширення об'єкта пристрою у процедурі RamDskCleanUp звільняється пам'ять під образ диска, віддаляється символічна посилання і сам функціональний об'єкт пристрою. Також далі скидається ознака DeviceInitialized ініціалізації пристрою.
При вивантаженні самого драйвера викликається процедура RamDskUnload, яка повинна обов'язково бути присутнім, щоб драйвер міг розвантажуватися.
VOID RamDskUnload (IN PDRIVER_OBJECT DriverObject)
{PRAMDSK_DRIVER_EXTENSION driverExtension;
DBGPRINT (DBG_COMP_INIT, DBG_LEVEL_INFO, ("Driver Unload \ n"));
ASSERT (DriverObject-> DeviceObject == NULL);
driverExtension = IoGetDriverObjectExtension (DriverObject, RAMDSK_DRIVER_EXTENSION_KEY);
ASSERT (driverExtension! = NULL);
if (driverExtension-> RegistryPath.Buffer)
{ExFreePool (driverExtension-> RegistryPath.Buffer);}
return;
} / / Кінець RamDskUnload ()
В цій процедурі видаляється вже розширення драйвера.
3.5 Програма налаштування параметрів віртуального диска
Щоб надати користувачеві можливість налаштувати параметри віртуального диска розроблена програма RamDskConfig.exe. Загальний вигляд програми зображено на рис. 4


Рис. 4 Програма налаштування параметрів віртуального диска
Програма дозволять призначати для рамдіска букву диска і його розмір. Щоб забезпечити коректність роботи драйвера, вибір літери диска здійснює з наявних літер, які не призначені іншим дискам у системі.
Розмір диску також задається списком зумовлених значень, але користувач може сам ввести потрібний розмір диска в поле введення.
Для збереження змін, користувачеві потрібно натиснути кнопку «ОК». При цьому відбувається рестарт драйвера з новими параметрами. Щоб не зберігати зміни, потрібно натиснути кнопку «Скасувати».
3.6 Установка драйвера
Установка драйвера здійснюється з використанням інсталяційного файлу RAMDsk.inf, що поставляється разом з драйвером. Для установки фільтра необхідно виконати наступні дії:
Відкрийте вікно Майстра обладнання (наприклад, з вкладки Устаткування вікна Властивості системи).
Виберіть пункт Додати / провести діагностику пристрої
У списку пристроїв виберіть Додавання нового пристрою
Далі вкажіть Ні, вибрати обладнання зі списку
Вкажіть тип пристрою Інші пристрої
Натисніть на кнопку Установити з диска і вставте дискету з файлами драйвера-фільтра в дисковод.
Після цього в вікні, що з'явилося виберіть шлях до дискети і натисніть ОК.
Натисніть кнопку Далі для початку встановлення.
Якщо в ході установки система видасть попередження про те, що встановлюваний драйвер не має цифрового підпису, натисніть кнопку Все одно продовжити.
Після установки натисніть кнопку Готово для закриття майстра установки
При необхідності налаштування драйвера скопіювати файл RamDskConfig.exe на диск.

4. ЕКСПЕРИМЕНТАЛЬНО-ДОСЛІДНИЙ РОЗДІЛ
4.1 Опис експериментів
Для дослідження часових характеристик використовувалася програма HD TACH 2.70, досліджувалися такі характеристики:
швидкість послідовного читання
час доступу при випадковому зверненні
завантаження процесора при операціях запису / читання
Експерименти проводилися на комп'ютері Intel Pentium III 566 МГц, 196 Мб ВП, Windows 2000 Server, розмір рамдіска 32 Мбайта.
4.2 Результати експериментів
У таблиці 4 показані результати тестування для рамдіска, для порівняння наведені результати для жорсткого диска Segate Barracuda квітня 7200
Таблиця 4 Результати тестування
Характеристика
Рамдіск
Жорсткий диск
Швидкість послідовного читання Мб / с
189,5
40,0
Час доступу при випадковому зверненні, мс
0,0
15,2
Завантаження ЦП
100%
10%
Як і очікувалося, для віртуального диска характерний миттєвий доступ до будь-якого сектору на диску, на відміну від жорстких дисків, час доступу яких залежить від швидкодії механіки.
Швидкість передачі даних для рамдіска також обмежується швидкістю обміну даними в пам'яті (для даної платформи обмін даними з оперативною пам'яттю дорівнює 250 Мб / с).
Платою за такі високі показники є повне завантаження процесора, бо він постійно працює з оперативною пам'яттю.

ВИСНОВОК
У даній роботі були досліджені питання, пов'язані з розробкою драйверів для пристроїв зберігання, способами зберігання даних, управління роботою пристрою за допомогою команд PnP. Розроблено класовий драйвер віртуального диска, який повністю задовольняє всім зазначеним вимогам.
Робота драйвера здійснюється коректно, рамдіск забезпечує повну функціональність звичайного жорсткого диска. На даному диску можна створювати, читати, записувати, видаляти файли.
Драйвер не впливає на роботу інших пристроїв, і не призводить до відчутних затримок з роботі системи.
Параметри диска змінюються динамічно і без необхідності перезавантаження системи.
Також драйвер в ході тестування показав хороші результати продуктивності, які обмежені характеристиками оперативної пам'яті комп'ютера.

СПИСОК ВИКОРИСТАНОЇ ЛІТЕРАТУРИ
1. Oney W. Programming the Microsoft Windows Driver Model. - Redmond, Washington: Microsoft Press., 1999.
2. В. Г. Оліфер, Н. А. Оліфер Мережеві операційні системи. Підручник для вузів - СПб.: Видавництво «ПІТЕР», 2004 - 544 с.: Іл.
3. Солдатов В.П. Програмування драйверів Windows. Вид. 2-е, перероб. і доп.-М.: ТОВ «Біном-Пресс», 2004.-480с., іл.
4. Microsoft Windows XP DDK Documentation.














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

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

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


Схожі роботи:
Організація віртуального диска Структура файлу образу віртуального диска
Організація віртуального диска Структура файлу-образу віртуального диска
Розробка драйвера віртуального жорсткого диска
Драйвер клавіатури реалізує функції музичного синтезатора на клавіатурі для Windows NT 5
Реінжиніринг віртуального підприємства
IRC як жанр віртуального дискурсу
Пристрій CD-диска
Дефрагментація жорсткого диска
Освіта протопланетного диска
© Усі права захищені
написати до нас