Налагодження та тестування модулів ядра Частина 76

У цій статті ми продовжуємо обговорення різних аспектів налагодження в ядрі при розробці модулів.

Інтерфейси простору користувача до модуля

Для контролю значень ключових змінних та їх зміни всередині модуля ці змінні можна відобразити в псевдофайлові системи/proc, а ще краще/sys. Подібний прийом часто застосовується для лічильника переривань, оброблених у драйвері, як це показано в прикладі нижче (цей приклад також доводить, що у такий спосіб можна контролювати змінні навіть усередині обробників апаратних переривань). Повний код прикладу можна знайти в архівіsimple-debug.tgz у розділі "Матеріали для скачування".

Лістинг 1. Відображення змінних модуля у систему /sys (файл mdsys.c)

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

Для перевірки завантажимо створений модуль для контролю лінії IRQ мережевого адаптера (хоча можна було вибрати і будь-яку іншу лінію IRQ, наприклад, системного таймера):

В даному випадку ми контролюємо наростаюче значення лічильника переривань, що спрацювали. Змінимо (в динаміці) початкове значення цього лічильника, від якого походить інкремент і відзначимо цю зміну у висновку:

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

Комплементарний модуль налагодження

Часто техніка створення інтерфейсів у простір/proc або/sys, як це описановище, є прийнятною, але після завершення розробки було б небажано залишати кінцевому користувачеві доступ до діагностичних та керуючих змінних, хоча б з тих міркувань, що таким чином легко порушити нормальну роботу модуля. Але переписувати код модуля перед його постачанням також не можна, оскільки будь-якою, навіть несуттєвою редактурою, можна внести значні помилки в роботу модуля. У цьому випадку для проектованого модуля на період налагодження може бути створений парний (комплементарний) модуль:

  • проектований модуль тепер не виносить критичні змінні файлові системи, а тільки оголошує їхекспортованими ;
  • комплементарний модуль налагодження динамічно встановлює зв'язок з цими змінними (імпортує ) при своєму завантаженні;
  • і створює для них інтерфейси в/proc або/sys, що дозволяють здійснювати діагностику та керування подібними змінними;
  • після завершення налагодження модуль налагодження, як технологічний компонент, просто вилучається з поставки проекту.

Для детального розгляду трансформуємо в цю схему приклад, поданий у лістингу 1. Причому зробимо це без будь-яких змін та покращень, щоб ми могли порівняти вихідний код прикладів за принципом: що було і що сталося. Повний код усіх файлів, наведених нижче, можна знайти в архівіsimple-debug.tgz розділі "Матеріали для скачування".

Лістинг 2. Файл загальних термінів (файл mdsys2.h)
Лістинг 3. Власне проектований (налагоджуваний) модуль (файл mdsys2.с)
Лістинг 4. Модуль, що створює для нього налагоджувальний інтерфейс (mdsysс.h)

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

Тепер видалимо модуль налагодження:

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

Невеликі корисні поради

Пишіть у файли протоколів

Найчастіше перезавантажуйте систему!

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

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

Для того, щоб уникнути десятків годин бездарно втраченого часу, при роботі над модулями, перезавантажуйте ОС Linux час від часу, навіть якщо вам здається, що вона працює абсолютно нормально. Після перезавантаження результати повторення щойно виконаного тесту можуть радикально помінятися!

Використовуйте природні POSIX тестери

Тут мається на увазі, що при відпрацюванні модуля завжди, перш ніж починати більш жорстке тестування драйвера, перевірте його реакцію читання і запис на природні POSIX тестери: cat для читання і echo для запису, наприклад. У цій якості можуть бути корисні й іншістандартні утиліти Linux, наприклад, cp. Можливо, дляЗабезпечуючи сумісність функціонування з POSIX-командами, вам потрібно додати до драйвера додаткову функціональність (наприклад, обробка ситуації EOF), яка може бути відсутня у формальній специфікації на продукт. Але досягнення POSIX сумісності коштує витраченої додаткової праці!

Тестуйте читання серіями

Виконуючи перевірку операцій read() , не обмежуйтесь одиночною операцією тестування. Натомість перевірте реакцію модуля на серію послідовних операцій читання. Завдяки цьому ви переконаєтеся, що драйвер не тільки правильно відпрацьовує операцію, але й нормально відновлюється після операції та готовий до виконання наступної. Іншими словами, замість одиночної операції cat (у найпростішому випадку) робіть кілька послідовних, звіряючи їхню ідентичність:

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

Висновок

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