Модель драйвера пристрою Linux проти Windows: архітектура, інтерфейси API та порівняння середовища створення

Драйвери пристроїв є частинами операційної системи, які полегшують використання апаратних пристроїв через певний інтерфейс програмування, щоб програмні додатки могли керувати та управляти пристроями. Оскільки кожен драйвер є специфічним для конкретної операційної системи, вам потрібні окремі драйвери для пристроїв Linux, Windows або Unix, щоб увімкнути використання вашого пристрою на різних комп'ютерах. Саме тому, коли наймаєте розробника драйверів або виберете постачальника послуг з R & D, важливо подивитися на їхній досвід розробки драйверів для різних платформ операційної системи.

Першим кроком у розробці драйверів є розуміння відмінностей в тому, як кожна операційна система обробляє свої драйвери, основну модель драйвера та архітектуру, яку вона використовує, а також наявні інструменти розробки. Наприклад, модель драйвера Linux сильно відрізняється від Windows. Хоча Windows допомагає розділити розробку драйверів та розробку ОС і поєднує драйвери та ОС через набір викликів ABI, розробник драйверу пристроїв Linux не покладається на будь-який стабільний ABI або API, а кодований драйвер - це інсталяція в ядро. Кожна з цих моделей має власний набір переваг та недоліків, але важливо знати їх усіх, якщо ви хочете надавати всебічну підтримку своєму пристрою.

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

1 Архітектура драйвера пристрою

Архітектура драйвера пристрою Windows відрізняється від того, що використовується в драйверів Linux, при цьому будь-який з них має власні плюси і мінуси. Відмінності в основному впливають на те, що Windows - це ОС закритого типу, тоді як Linux - відкрите джерело. Порівняння архітектур драйверів пристроїв Linux та Windows допоможе нам зрозуміти основні відмінності драйверів Windows та Linux.

1.1 Архітектура драйвера Windows

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

Запити від додатків обробляються частиною ядра Windows, що називається менеджером IO, який перетворює їх в пакети запитів на ІВ (ІРП), які використовуються для ідентифікації запиту та передачі даних між шаблонами драйверів.

WDM забезпечує три види драйверів, які складаються з трьох шарів:

  • Драйвери фільтрів забезпечують необов'язкову додаткову обробку ІРП.
  • Функціональні драйвери є основними драйверами, які реалізують інтерфейси для окремих пристроїв.
  • Водії автобусів обслуговують різні адаптери та контролери шини, що приймають пристрої.

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

1.2 Архітектура драйвера Linux

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

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

Драйвер пристрою Linux підтримує три види пристроїв:

  • Символьні пристрої, що реалізують інтерфейс байтового потоку.
  • Блокувати пристрої, які приймають файлові системи та виконують ІО з багатобайтовими блоками даних.
  • Мережеві інтерфейси, які використовуються для передачі пакетів даних через мережу.

Linux також має апаратний рівень абстракції, який діє як інтерфейс фактичного обладнання для драйверів пристроїв.

2 API драйверів пристрою

Обидва інтерфейсу Linux і Windows драйверів керуються подіями: код драйвера виконується лише тоді, коли відбувається певна подія: або коли користувацькі додатки хочуть щось з пристрою або коли на пристрої є щось сказати ОС.

2.1 Ініціалізація

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

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

2.2 Іменування та заява пристроїв

Реєстрація пристроїв у Windows

Драйвер пристрою Windows повідомляється про нові підключені пристрої в його зворотному дзвінку AddDevice. Потім він починає створювати об'єкт пристрою, який використовується для ідентифікації цього конкретного екземпляра драйвера для пристрою. Залежно від виду драйвера об'єкт пристрою може бути об'єктом фізичного пристрою (PDO), об'єктом функціонального пристрою (FDO) або об'єктом фільтра пристрою (FIDO). Об'єкти пристрою можуть бути укладені в стовпці, у нижній - це PDO.

Об'єкти пристрою існують протягом усього часу підключення пристрою до комп'ютера. Структура DeviceExtension може бути використана для зв'язування глобальних даних з об'єктом пристрою.

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

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

Реєстрація пристроїв на Linux

На прикладах Linux користувачі отримують доступ до пристроїв за допомогою записів файлової системи, зазвичай розташованих в каталозі / dev. Модуль створює всі необхідні записи під час ініціалізації модуля, викликаючи функції kernel, такі як register_chrdev. Програма видає відкритий системний виклик, щоб отримати дескриптор файлу, який потім використовується для взаємодії з пристроєм. Цей дзвінок (та подальші системні виклики з повернутого дескриптора, як читання, запис або закриття) потім відправляються на функції зворотного виклику, встановлені модулем, у структури, такі як file_operations або block_device_operations.

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

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

В даний час Linux використовує значення 32-біт для основних-другорядних пар, з бітами 12, виділеними для основної кількості, що допускає до різних драйверів 4096. Основні-другорядні пари є характерними для характерних та блочних пристроїв, тому персонажний пристрій та блоковий пристрій можуть використовувати ту саму пару без конфліктів. Мережеві інтерфейси ідентифікуються символьними іменами, такими як eth0, які знову відрізняються від великих і другорядних чисел як персонажів, так і блоків.

2.3 Обмін даними

Обидва Linux та Windows підтримують три способи передачі даних між додатками на рівні користувача та драйверами на рівні ядра.

  • Буферний вхід-вихід, який використовує буфери, керовані ядром. Для операцій написання ядро ​​копіює дані з буфера користувача-пробілу до ядра-виділеного буфера і передає його драйверу пристрою. Читання є однаковими, причому ядро ​​копіює дані з буфера ядра в буфер, який надається програмою.
  • Прямий вхід-вихід, який не потребує копіювання. Замість цього ядро ​​виводить користувальницький буфер у фізичну пам'ять, щоб він залишався там, не обмінюючись, поки відбувається передача даних.
  • Картування пам'яті також може бути організовано ядром таким чином, щоб ядра та користувальницькі космічні програми мали доступ до однакових сторінок пам'яті з різними адресами.

Режими драйвера IO на Windows

Підтримка Буферний IO це вбудована функція WDM. Буфер доступний драйверу пристрою через поле AssociatedIrp.SystemBuffer структури IRP. Водій просто читає або пише до цього буфера, коли йому потрібно спілкуватися з користувальницьким простором.

Прямий IO на Windows опосередковано списки дескрипторів пам'яті (MDL). Це напівпрозорі структури, доступні через поле MdlAddress IRP. Вони використовуються для визначення фізичної адреси буфера, виділеного користувальницькою програмою, і прикріплені до тривалості запиту IO.

Третій варіант для передачі даних на Windows називається METHOD_NEITHER. У цьому випадку ядро ​​просто передає водієві віртуальні адреси вхідних і вихідних буферів користувача, не перевіряючи їх або не переконуючи їх в фізичну пам'ять, доступну драйверу пристрою. Драйвер пристрою відповідає за обробку деталей передачі даних.

Моделі драйверів IO на Linux

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

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

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

3 Середовище розробки драйвера пристрою

3.1 Рамки драйверу пристрою

Драйвер Windows Kit

Windows - це операційна система із закритим кодом. Microsoft надає a Драйвер Windows Kit щоб полегшити розробку драйверів пристроїв Windows не постачальниками корпорації Майкрософт. Комплект містить все, що необхідно для створення, налагодження, перевірки та додавання драйверів пристроїв для Windows.

Модель драйвера Windows визначає чіткий інтерфейс для драйверів пристроїв. Windows підтримує джерело та подвійну сумісність цих інтерфейсів. Скомпільовані драйвери WDM, як правило, сумісні з передньою версією: тобто старший драйвер може працювати на новій системі так само, як і без перекомпіляції, але, звичайно, вона не матиме доступу до нових функцій, наданих ОС. Проте драйвери не гарантують сумісність із зворотним зв'язком.

Вихідний код Linux

У порівнянні з Windows, Linux - це операційна система з відкритим кодом, тому весь вихідний код Linux - SDK для розробки драйверів. Формальний компонент для драйверів пристроїв відсутній, але ядро ​​Linux включає в себе численні підсистеми, що надають загальні послуги, такі як реєстрація драйверів. Інтерфейси до цих підсистем описані у файлах заголовків ядра.

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

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

3.2 Створення системи для драйверів пристроїв

Драйвер Windows Kit додає підтримку розробки драйверів для Microsoft Visual Studio і включає в себе компілятор, який використовується для побудови коду драйвера. Розроблення драйверів пристроїв Windows набагато відрізняється від розробки програми для користувача-простору в IDE. Microsoft також надає Підприємство Windows Driver Kit, що дозволяє створювати середовище командного рядку, подібне до Linux.

Linux використовує Makefiles як систему збирання для драйверів пристроїв в дереві та поза її дерева. Система побудови Linux досить розвинена, і зазвичай драйвер пристрою потребує не більше ніж декілька рядків для створення робочої двійкової системи. Розробники можуть використовувати будь-який IDE поки він може обробляти базу вихідних кодів Linux і запускати марку, або вони можуть легко компілювати драйвери вручну з терміналу.

3.3 Підтримка документації

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

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

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

3.4 Налагодження підтримки

Обидва Linux та Windows мають засоби реєстрації, які можуть бути використані для відстеження кодів драйверів. У Windows для цього буде використовуватися функція DbgPrint, тоді як на Linux ця функція називається printk. Однак не кожну проблему можна вирішити, використовуючи лише журнал і вихідний код. Іноді точок зупинки корисніше, оскільки вони дозволяють вивчати динамічну поведінку коду драйвера. Інтерактивна налагодження також важлива для вивчення причин аварій.

Windows підтримує інтерактивну налагодження за допомогою налагоджувача WinDbg на рівні ядра. Для цього потрібні дві машини, підключені через послідовний порт: комп'ютер для запуску налагоджуваного ядра, а інший для запуску відладчика та управління налагоджуваною операційною системою. Комплект драйверів Windows включає в себе налагоджувальні символи для ядра Windows, завдяки чому структури даних Windows будуть частково видно в налагодженні.

Linux також підтримує інтерактивну налагодження за допомогою KDB і KGDB. Підтримка налагодження може бути вбудована в ядро ​​та активована під час завантаження. Після цього можна або налагодити систему безпосередньо через фізичну клавіатуру, або підключитися до неї з іншого комп'ютера через послідовний порт. KDB пропонує простий інтерфейс командного рядка, і це єдиний спосіб відлагодження ядра на одній машині. Однак у KDB відсутня підтримка налагодження на рівні джерела. KGDB забезпечує більш складний інтерфейс через послідовний порт. Це дозволяє використовувати стандартні дебагери додатків, такі як GDB, для налагодження ядра Linux, як і будь-яке інше додаток для користувачів.

4 Дистрибутивні драйвери пристроїв

4.1 Встановлення драйверів пристроїв

У Windows встановлені драйвери описуються текстовими файлами під назвою INF-файли, які зазвичай зберігаються в каталозі C: ​​WindowsINF. Ці файли надаються постачальником постачальника драйверів і визначають, які пристрої обслуговуються драйвером, де можна знайти драйвери, версію драйвера тощо.

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

У Linux деякі драйвери вбудовані в ядро ​​і залишаються назавжди завантаженими. Неістотні компоненти будуються як модулі ядра, які зазвичай зберігаються в каталозі / lib / modules / kernel-version. Цей каталог також містить різні конфігураційні файли, такі як modules.dep, що описують залежності між модулями ядра.

Хоча ядро ​​Linux може завантажувати деякі модулі під час завантаження, зазвичай навантаження на модуль контролюється програмами користувача. Наприклад, процес init може завантажувати деякі модулі під час ініціалізації системи, а демон daemon відповідає за відстеження знову підключених пристроїв і завантаження відповідних модулів для них.

4.2 Оновлення драйверів пристроїв

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

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

4.3 Міркування щодо безпеки

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

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

5. Висновок

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

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

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

джерело

Залишити коментар

Цей сайт використовує Akismet для зменшення спаму. Дізнайтеся, як обробляються ваші дані коментарів.