“Звірятко іншого роду”

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

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

• Ядро не має доступу до бібліотеки функцій мови С

• Ядро програмується з використанням компілятора GNU С

• У ядрі немає такого захисту памяті, як в режимі користувача

• У ядрі не можна легко використовувати обчислення з плаваючою точкою

• Ядро використовує стек невеликого фіксованого розміру

• Оскільки в ядрі використовуються асинхронні переривання, ядро ​​є преемптівним і в ядрі є підтримка SMP, то в ядрі необхідно враховувати наявність паралелізму і використовувати синхронізацію

• Переносимість дуже важлива

Давайте розглянемо більш детально всі ці проблеми, так як всі розробники ядра повинні постійно памятати про них

Відсутність бібліотеки lib c

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

При цьому не потрібно засмучуватися, оскільки багато хто з функцій бібліотеки мови С реалізовані в ядрі Наприклад, звичайні функції роботи з рядками описані у файлі lib / stringс Необхідно лише підключити заголовний файл

і користуватися цими функціями

Заголовки

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

Відсутня найбільш відома функція printf () Ядро не має доступу до функції printf (), однак йому доступна функція print k () Функція printk () копіює форматований рядок в буфер системних повідомлень ядра (kernel log buffer), який зазвичай читається за допомогою програми syslog Використання цієї функції аналогічно використанню printf ():

printk (Hello world Рядок:% s і ціле число:% d \ n, a_string, an_integer)

Одна важлива відмінність між print f () і print k () полягає в тому, що у функції print k () можна використовувати прапор рівня виводу Цей прапор використовується програмою syslo g для того, щоб визначити, чи потрібно показувати повідомлення ядра Ось приклад використання рівня виводу:

printk (KERN_ERR Це була помилка \ n)

Функція print k () використовуватиметься протягом всієї книги У наступних розділах наведено більше інформації про функції print k ()

Компілятор GNU З

Як і всі поважають себе ядра Unix, ядро ​​Linux написано на мові С Може бути, це здасться несподіваним, але ядро ​​Linux написане не на чистому мовою С в стандарті ANSI С Навпаки, де це можливо, розробники ядра використовують раз-

особисті розширення мови, які доступні за допомогою засобів компіляції gcc (GNU Compiler Collection – колекція компіляторів GNU, в якій міститься компілятор С, використовуваний для компіляції ядра)

Розробники ядра використовують як розширення мови С ISO C991 так і розширення GNU С Ці зміни повязують ядро ​​Linux з компілятором gcc, хоча сучасні компілятори, такі як Imel С, мають достатню підтримку можливостей компілятора gcc для того, щоб ними теж можна було компілювати ядро ​​Linux У ядрі не використовуються будь-які особливі розширення стандарту С99, і крім того, оскільки стандарт С99 є офіційною редакцією мови С, ці розширення рідко призводять до виникнення помилок в інших частинах коду Більш цікаві і, можливо, менш знайомі відхилення від стандарту мови ANSI З повязані з розширеннями GNU С Давайте розглянемо деякі найбільш цікаві розширення, які можуть зустрітися в програмному коді ядра

Функції з підстановкою тіла

Компілятор GNU С підтримує функції з підстановкою тіла (inline functions) Виконуваний код функції з підстановкою тіла, як випливає з назви, вставляється у всі місця програми, де вказаний виклик функції Це дозволяє уникнути додаткових витрат на виклик функції і повернення з функції (збереження і відновлення регістрів) і потенційно дозволяє підвищити рівень оптимізації, так як компілятор може оптимізувати код викликає і викликається функцій разом Зворотною стороною такої підстановки (ніщо в цьому житті не дається задарма) є збільшення обсягу коду, збільшення використовуваної памяті і уменьше ня ефективності використання процесорного кешу інструкцій Розробники ядра використовують функції з підстановкою тіла для невеликих функцій, критичних до часу виконання Використовувати підстановку тіла для великих функцій, особливо коли вони викликаються більше одного разу або не дуже критичні до часу виконання, не рекомендується

Функції з підстановкою тіла оголошуються за допомогою ключових слів stati c і inlin e в декларації функції Наприклад,

static inline void dog(unsigned long tail_size)

Декларація функції повинна бути описана перед будь-яким її викликом, інакше підстановка тіла не буде проведена Стандартний прийом – це розміщення функцій з підстановкою тіла в заголовних файлах Оскільки функція оголошується як статична (static), примірник функції без підстановки тіла не створюється Якщо функція з підстановкою тіла використовується тільки в одному файлі, то вона може бути розміщена у верхній частині цього файлу

У ядрі використанню функцій з підстановкою тіла слід віддавати перевагу в порівнянні з використанням складних макросів

1 Стандарт ISO C99 – це остання основна версія редакції стандарту ISO С Редакція С99 містить численні поліпшення попередньої основної редакції цього стандарту Стандарт ISO C99 вводить поіменну ініціалізацію полів структур і типcomplex

Il l

Вбудований асемблер

Компілятор gcc С дозволяє вбудовувати інструкції мови асемблера в звичайні функції мови С Ця можливість, звичайно, повинна використовуватися тільки в тих частинах ядра, які унікальні для певної апаратної платформи

Для вбудовування я ассемблерного коду використовується директив а компілятора asm ()

Ядро Limix написано на суміші мов асемблера і С Мова асемблера використовується в низькорівневих підсистемах і на ділянках коду, де потрібна велика швидкість виконання Велика частина коду ядра написана на мові програмування С

Анотація розгалужень

Компілятор gnu З має вбудовані директиви, що дозволяють оптимізувати різні гілки умовних операторів, які найбільш або найменш вірогідні Компілятор використовує ці директиви для відповідної оптимізації коду У ядрі ці директиви полягають у макроси likely () і unlikely (), які легко використовувати Наприклад, якщо використовується оператор if такого вигляду:

if (foo) {

/*.*/

}

то для того, щоб відзначити цей шлях виконання як малоймовірний, необхідно вказати:

/ * Передбачається, що значення змінної foo дорівнює нулю . * /

if (unllkely(ffoo)) {

/*.*/

}

І навпаки, щоб відзначити цей шлях виконання як найбільш імовірний

/ * Передбачається, що значення змінної foo не дорівнює нулю . * /

if (likely(foo)) {

/*.*/

}

Ці директиви необхідно використовувати тільки у випадку, коли напрям розгалуження з великою ймовірністю відомо апріорі або коли необхідна оптимізація якої частини коду за рахунок іншої частини Важливо памятати, що ці директиви дають збільшення продуктивності, коли напрям розгалуження передбачене правильно, однак призводять до втрати продуктивності при неправильному прогнозі Найбільш часто директиви unlikel y () і likel y () використовуються для перевірки помилок

Відсутність захисту памяті

Коли прикладна програма вживає незаконну спробу звернення до памяті, ядро ​​може перехопити цю помилку і аварійно завершити відповідний процес Якщо ядро ​​робить спробу некоректного звернення до памяті, то результати можуть бути менш контрольовані Порушення правил доступу до памяті в режимі ядра призводить до помилкиoops, яка є найбільш часто встречаНачальние відомості про ядро ​​Linux 41

ющейся помилкою ядра Не варто говорити, що не можна звертатися до заборонених областям памяті, разименовивать покажчики зі значенням NULL і так далі, проте в ядрі ставки значно вище

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

Не можна просто використовувати обчислення з плаваючою точкою

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

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

Маленький стек фіксованого розміру

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

Стек, доступний в режимі ядра, не є ні великим, ні динамічно змінним, він малий за обсягом і має фіксований розмір Розмір стека залежить від апаратної платформи Для платформи х86 розмір стека може бути налаштований на етапі компіляції і бути рівним 4 або 8 Кбайт Історично так склалося, що розмір стека ядра дорівнює двом сторінкам памяті, що відповідає 8 Кбайт для 32-розрядних апаратних платформ і 16 Кбайт – для 64-розрядних Цей розмір фіксований Кожен процес отримує свою область стека

Докладніше обговорення використання стека в режимі ядра дивіться в наступних розділах

Джерело: Лав, Роберт Розробка ядра Linux, 2-е видання : Пер з англ – М: ТОВ «ІД Вільямс »2006 – 448 с : Ил – Парал тит англ

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*