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

Рис. 7.4

Майстер оновлення

WDM драйвера пристрою

Для установки / оновлення WDM драйвера потрібна наявність INF-файлу, в якому опи

сани характеристики драйвера (назва файлу, місце установки, дані про виробника і т. д.).

Для установки NT драйвера INF файл не потрібен, але потрібно написати програму, позво ляющую за допомогою спеціальних функцій WINAPI включити драйвер в систему. Те ж саме, в принципі, можна зробити і вручну, прописавши відповідні значення ключів в систем ном реєстрі. Цей тип драйвера використовується для роботи з пристроями не PnP, а також при написанні драйверів, не працюють з яким або обладнанням. Зокрема, NT подоб ний драйвер можна використовувати при роботі з паралельним, послідовним портом, звуковою картою та іншими пристроями в системі. Такий драйвер порівняно нескладно написати, що ми і продемонструємо в наступних прикладах.

Основи функціонування драйверів

Драйвер пристрою операційної системи – це не звичайна програма. Для пользова теля, який звик до розробки звичайних програм, технологія розробки драйвера поки жется незвичайною. У драйвері немає точки входу в програму, як наприклад, main в програмі на мові C, хоча в момент початкового завантаження операційна система викликає програму ініціалізації драйвера, відому під назвою DriverEntry. Драйвер пристрою можна представити як якийсь контейнер, в якому містяться структури даних і функції, що робить його в деякому сенсі схожим на бібліотеку динамічного компонування (dll). Обра щення до драйвера при виконанні операцій з пристроєм – це звернення до однієї з його функцій.

Це дуже спрощений опис структури драйвера, але воно дає уявлення про те, як працює драйвер пристрою. Життєвий цикл функціонування драйвера пристрою можна розділити (умовно) на три етапи:

1) ініціалізацію;

2) виконання операцій за запитами операційної системи на введення виведення даних

(Менеджер введення виведення);

3) зупинка і видалення з системи.

Спочатку драйвер пристрою має бути ініціалізованим першим. Процес ініціалізації у спрощеному вигляді показаний на рис. 7.5:

Рис. 7.5

Схема ініціалізації драйвера пристрою

Драйвер пристрою записаний на диску в форматі файлу з розширенням. Sys. Для опера ційних систем Windows загальноприйнято розташовувати файли драйверів в катклоге windows \ system32 \ drivers, хоча це зовсім не обов’язково. Відомості про встановлені драйвери хра

нятся в системному реєстрі Windows в ключі \ registry \ machine \ system \ currentcontrolset \ services. Історично склалося так, що записи про системні службах і драйверах зберігаються в одному і тому ж розділі реєстру (services). Крім усього іншого запису містять тип за пуску драйвера – по ньому система визначає, коли потрібно завантажувати драйвер. Тут є дуже важливий момент: при випробуваннях працездатності драйвера бажано не ініціати лизировать драйвер в момент завантаження системи, інакше можна взагалі не завантажитися, якщо в драйвері є яка небудь помилка.

При ініціалізації драйвера операційна система завантажує двійковий образ дискового

файлу в захищену область пам’яті і передає управління функції DriverEntry. Основне завдання цієї функції – проініціалізувати поля структур DriverObject і DeviceObject і виконан нитка прив’язку пристрою до простору імен операційної системи, щоб можна було звертатися до пристрою з програми користувача. Крім цього, тут же ініціалізує ється масив покажчиків на функції виду IRP_MJ_XXX, де XXX позначає операцію.

Наприклад, покажчик IRP_MJ_CREATE може посилатися на функцію, яка буде викликають

тися при створенні (відкриття) пристрою (наприклад, функцією CreateFile () з програми користувача). Кожен драйвер може включати різні функції, в залежності від на значення пристрою. Наприклад, в пристрій можна тільки записувати дані, але не можна читати – в цьому випадку можна не визначати функцію для IRP_MJ_READ або зробити її у вигляді програмної «заглушки».

Як у драйвері вибираються функції, які повинні виконувати в даний момент опе

рацію з пристроєм? Для цього передбачений механізм пакетів запиту введення виводу (I / O Request Packet, IRP). Наприклад, якщо програма користувача виконує запис даних в пристрій за допомогою WINAPI функції WriteFile (), то Менеджер введення виведення операцион ної системи формує пакет запиту IRP_MJ_WRITE і посилає його драйверу пристрою для виконання. У свою чергу, драйвер пристрою викликає функцію, чия адреса вказана при ініціалізації масиву покажчиків і передає їй управління. Будь NT драйвер, тим не менш, повинен обов’язково включати обробники пакетів запиту IRP_MJ_CREATE (відкритому тя пристрою) і IRP_MJ_CLOSE (закриття пристрою).

Пакет запиту IRP містить необхідні поля та адреси областей пам’яті, виділених системою для тимчасового зберігання даних при читанні запису. Ми не будемо детально рас сматривать структуру IRP, а перейдемо до аналізу функціонування драйвера.

Взаємодія драйвера пристрою з програмою користувача розглянемо на прикладі

ре читання даних з пристрою введення виводу (рис. 7.6).

На цьому малюнку показана у спрощеному вигляді операція читання даних пристрої з програми користувача. Додаток користувача викликає функцію ReadFile (), переда вая їй дескриптор відкритого пристрою. Потім Менеджер введення виводу, що працює в ре жимі ядра, по дескриптору пристрою визначає, для якого драйвера слід формування вать запит IRP_MJ_READ і готує пакет запиту. Далі викликається функція обробки, відповідна одержаному запиту, яка виконує необхідні дії з читання даних з пристрою. Тут потрібно відзначити, що навіть драйвер ніколи не виконує безпосередньо операцій з портами введення виведення пристрою, а передає управління рівнем ап паратно абстракцій, реалізованому у вигляді бібліотечних функцій в бібліотеці hal.dll. Фактично тут «закінчується» апаратна «незалежність» всіх операцій та операції проводяться на рівні конкретних апаратних засобів.

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

Рис. 7.6

Читання даних з програми користувача

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

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

Джерело: Магда Ю. С. Комп’ютер в домашній лабораторії. – М.: ДМК Пресс, 2008. – 200 с.: Іл.