Розробка модулів ядра linux частина 16

Цей контент є частиною серії: Розробка модулів ядра Linux

Слідкуйте за виходом нових статей цієї серії.

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

параметри компіляції

Параметри компіляції модуля можна перевизначити, змінивши змінні, визначені в сценарії, що виконує складання, наприклад:

Таким же чином можна доповнити визначення необхідних препроцесорну змінних (які спрацюють в збираному коді), специфічних для збірки модуля:

(Зверніть увагу на знак + =).

Примітка. Звідки беруться змінні, що не певні явно в тексті файлу Makefile, як, наприклад, EXTRA_CFLAGS. Або звідки беруться правила складання за замовчуванням (як в прикладі використання ассемблерного коду, представленому в попередній статті)? І як можна подивитися ці правила? Утиліта make має безліч значень за замовчуванням (змінних, суфіксів і т.д.), найважливішими з яких є правила обробки суфіксів, а також визначення внутрішніх змінних оточення. Вся ця інформація називається базою даних make і може бути виведена по ключу -p. Але обсяг виведеної інформації буде дуже великий, тому розумно відправити цей висновок в файл, а вже потім вивчити його:

Лістинг 1. Визначення внутрішніх змінних оточення make

Тепер можна використовувати всі ці змінні в власних сценаріях збірки Makefile.

Збірка модулів в деталях

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

Далі будуть розглядатися різні особливості процедури складання (make) проектів модулів ядра. Також буде представлений і проаналізовано набір сценаріїв збірки (Makefile) для найбільш часто зустрічаються ситуацій, наприклад: збірка кількох модулів в проекті, збірка модуля об'єднанням декількох файлів з вихідним кодом і т.д.

Як одночасно зібрати кілька модулів?

У раніше згадуваному файлі Makefile може бути виконана одночасна збірка будь-якої кількості модулів:

Лістинг 2. Одночасна збірка двох модулів

Як зібрати модуль разом з відносяться до нього програмами?

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

Лістинг 3. Одночасна збірка модуля і призначених для користувача програм

Особливість такої спільної збірки полягає в тому, що і модуль, і призначені для користувача процеси включають (за допомогою директиви #include) одні й ті ж загальні та узгоджені визначення (приклад, в тому ж архіві ioctl.tgz):

Такий сполучний файл містить загальні визначення, як для коду простору ядра, так і для призначених для користувача додатків. Як приклад показано вміст файлу ioctl.h:

У зв'язку з цим може виникнути певна проблема, так як при складанні додатків і модулів (що використовують спільні визначення) для пошуку системних (#include <.> ) Заголовків файлів використовуються різні каталоги за замовчуванням. / Usr / include для процесів і / lib / modules / `uname -r` / build / include для модулів. Прийнятним рішенням буде включення до загального заголовки (ioctl.h) фрагмента подібного виду:

Лістинг 4. Умовні визначення для ядра і для додатків

При всій схожості імен заголовків файлів (а іноді і повний збіг, наприклад: ) Це будуть включення з абсолютно різних наборів API (API поділюваних бібліотек * .so для простору користувача та API ядра - для модулів).

призначені для користувача бібліотеки

На додаток до набору додатків простору користувачів може виявитися зручним зібрати у вигляді єдиної бібліотеки цілий ряд спільно використовуваних цими додатками функцій (так усувається дублювання коду, спрощується внесення змін і поліпшується структура проекту). Фрагмент Makefile з архіву time.tgz, який буде представлений в одній з наступних статей, демонструє, як це зробити, не вказуючи в явному вигляді всі цілі збірки (перераховані списком у змінній OBJLIST) для кожного такого об'єктного файлу, що включається в бібліотеку (реалізує окрему функцію бібліотеки). В даному випадку збирається статична бібліотека libdiag.a:

Лістинг 5. Збірка користувальницької статичної бібліотеки

У лістингу 5 збираються дві мети prog і lib. об'єднані в одну загальну мету all. При бажанні статичну бібліотеку можна замінити на динамічну (що розділяється), що може бути затребуване в великих проектах. При цьому в Makefile потрібно внести тільки незначні зміни (всі інші файли проекту залишаються в незмінному вигляді).

Лістинг 6. Збірка користувальницької вашої бібліотеки

Примітка. У разі побудови вашої бібліотеки необхідно також забезпечити розміщення новоствореної бібліотеки (в нашому прикладі це libdiag.so) на шляху, де вона буде знайдена динамічним загрузчиком. Розміщення в "поточному каталозі" в даному випадку не підходить: відносні імена каталогів не застосовуються для пошуку динамічних бібліотек. Вирішити це завдання можна різними способами:

Але всі ці питання відносяться вже до системного адміністрування і виходять за рамки даної статті.

висновок

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

Ресурси для скачування

Схожі статті