Ядро операційної системи
Ядро́ (англ. Kernel) — центральна частина операційної системи, що реалізовує інтерфейс між прикладними процесами та обладнанням комп'ютера. Завантажується в оперативну пам'ять комп'ютера і безпосередньо взаємодіє з апаратурою, забезпечуючи керування апаратними засобами (при цьому використовуються драйвери (модулі ядра) підключеного в систему обладнання), підтримку одночасної роботи багатьох користувачів (багатокористувацький режим), підтримку паралельного виконання багатьох процесів в системі (багатозадачність). Зазвичай ядро робить ці об'єкти доступними для прикладних процесів через механізми міжпроцесної взаємодії і системних викликів.
Кожна операційна система реалізує ці завдання по-різному, залежно від своєї реалізації та дизайну. Наприклад, монолітні ядра виконують весь код операційної системи для збільшення продуктивності в одному адресному просторі, мікроядра запускають більшість служб операційної системи в просторі користувача як сервери, що спрямовані на підвищення експлуатаційної надійності та модульності операційної системи.
Складові частини ядра операційної системи
Основне завдання ядра — керування ресурсами комп'ютера та керування їхньою доступністю іншим програмам для запуску і використання. Як правило, основними ресурсами ядра є:
- Центральний процесор — головна частина комп'ютерної системи, відповідає за функціонування та виконання програм. Ядро бере на себе відповідальність за прийняття рішень про кількість процесорного часу, який виділяється для запущених програм.
- Оперативна пам'ять. Пам'ять використовується для зберігання команд і даних процесів. Як правило, обидва ці елементи повинні бути в пам'яті для можливості виконання програми. Одним з головних обов'язків ядра є керування ресурсами оперативної пам'яті комп'ютера. Наприклад, у сучасних операційних системах ядро надає процесам віртуальний адресний простір, що може бути розміром більшим ніж доступна оперативна пам'ять.
- Будь-які пристрої введення та виведення (I/O), підключені до комп'ютера, такі як клавіатура, миша, дисководи, принтери, монітори тощо. Ядро виділяє можливість запиту від програм для виконання операцій вводу/виводу відповідного пристрою і надає користувачеві зручні абстракції основних функцій пристрою.
Ключові аспекти, необхідні для керування ресурсами є визначення домену виконання (адресного простору), а також механізму захисту, який використовується для роботи з доступом до ресурсів домену. Ядра також зазвичай надають методи для синхронізації і взаємодії між процесами (так механізм міжпроцесної взаємодії або IPC). Ядро може виконувати ці функції самостійно або покладатися на деякі процеси, які запускаються ним забезпечення умов для інших процесів, хоча у цьому випадку ядро повинне надати деякі засоби IPC, щоб дозволити цим спеціальним процесам доступ до внутрішньої структури прикладних процесів. Нарешті, ядро має забезпечити запущені програми методами, які дозволяють робити запити на доступ до цих об'єктів.
Керування процесами
Основним завданням ядра є можливість виконання програм і їхня підтримка за допомогою апаратних абстракцій. Процес визначає, до яких частин пам'яті може отримати доступ застосунок. (часто поняття «застосунок», «процес» і «програма» використовуються як синоніми). Керування процесами у ядрі має враховувати вбудовані апаратні засоби для захисту пам'яті.
Для запуску програми ядро зазвичай визначає адресний простір для процесів, завантажує програмний код у пам'ять, готує стек для програми і починає виконання програмного коду в заданому місці.
Багатозадачність ядра дозволяє давати користувачеві ілюзію того, що число процесів, які можуть одночасно працювати на комп'ютері більше, ніж максимальне число процесів, що комп'ютер фізично здатний одночасно виконувати. Як правило, максимальна кількість одночасних процесів у системі дорівнює кількості процесорів.
У витискальній багатозадачності ядро надає кожному процесу деяку фіксовану кількість часу, при цьому швидко перемикаючись між кожним процесом так, що у користувача виникає відчуття одночасності виконання цих процесів. Кількість часу, яка виділяється для кожного процесу визначається за допомогою алгоритму планування виконання задач. Цей алгоритм також визначає пріоритетність виконання процесів. Ядро зазвичай цим процесам деякий інтерфейс для взаємодії між собою: так звані засоби взаємодії між процесами, а основними підходами є спільна пам'ять, обмін повідомленнями і виклик віддалених процедур.
Інші операційні системи (зокрема на менших, менш потужних комп'ютерах) можуть використовувати кооперативну багатозадачність, де кожен процес виконується неперервно до тих пір, поки цей процес не пошле спеціальний запит до ядра, що дає дозвіл на початок виконання іншого процесу. Старі версії Windows та Mac OS використовували кооперативну багатозадачність, але зрештою, із зростанням потужності комп'ютерів, вони перейшли на витискальну багатозадачність.
Операційна система також може мати підтримку багатопроцесорності; в цьому випадку програми і нитки можуть виконуватися на різних процесорах. Для цього ядро повинно бути повторно використовуваним, що означає можливість запуску двох або більше частин коду одночасно.
Керування пам'яттю
Ядро має повний доступ до системної пам'яті і надає процесам безпечний доступ до неї у разі необхідності. Найчастіше першим кроком задля досягнення цього є створення віртуальної пам'яті, зазвичай це досягається за допомогою файлу підкачування та сегментної адресації пам'яті. Віртуальна адресація дозволяє ядру зробити певну фізичну адресу видимою під іншою віртуальною адресою. Простори віртуальних адрес можуть бути різними для різних процесів; пам'ять, до якої має доступ один процес під віртуальною адресою, може мати зовсім іншу віртуальну адресу в просторі адресу іншого процесу. Це дозволяє абстрагувати різні процеси один від одного, що запобігає використанню однієї ділянки пам'яті різними процесами.
На багатьох системах віртуальна адреса може посилатися на дані, що ще не завантажені. Шар абстракції, забезпечений віртуальним адресуванням, дозволяє операційній системі використовувати інші накопичувачі даних, такі як тверді диски або флеш-пам'ять. В результаті операційна система дозволяє використовувати більше пам'яті, ніж фізично є на комп'ютері.
Віртуальне адресування також дозволяє створення віртуальних розділів пам'яті у двох розділених областях, одна з який зарезервована для ядра, а друга для прикладних програм (простір користувача). Застосунки не мають можливості доступу до пам'яті простору ядра, що досягається використанням спеціальних захисних механізмів процесора.
Керування пристроями
Для виконання корисних функцій ядру потрібно мати доступ до периферійних пристроїв, які керуються ним за допомогою драйверів. Наприклад, для того, щоб показати щось на екрані, програма спочатку надсилає запит до ядра, яке перенаправляє його до драйверу відеокарти, який відповідальний за фактичне відображення пікселів.
Ядро повинне зберігати список усіх наявних пристроїв. Цей список може бути відомий заздалегідь (наприклад у вбудованих системах, де ядро переписується кожен раз при зміні апаратного забезпечення), сконфігурований користувачем (найчастіше на старих системах або системах, які не призначені для персонального користування) або знайдений операційною системою під час часу виконання.
В системах plug and play менеджер пристроїв спочатку сканує різні порти, а потім робить спробу розпізнати підключені пристрої і знайти для них потрібний драйвер.
Реалізація керування пристроями дуже відрізняється в різних операційних системах, але, в усякому випадку, ядро повинне надати інтерфейс вводу/виводу, щоб дозволити драйверам мати доступ напряму до пристрою через якийсь порт або ділянку пам'яті.
Системні виклики
Щоб дійсно виконати потрібну роботу, процес має мати доступ до служб, наданих ядром. Реалізація цього інтерфейсу різна у кожному ядрі, але більшість ядер надає бібліотеку на C і програмний інтерфейс до неї, яка в свою чергу звертається до функцій ядра. Метод, за допомогою якого здійснюється звертання до ядра дуже варіюється в залежності від реалізації. Якщо застосовується ізоляція пам'яті, то користувацький процес не має прямого доступу до ядра, тому що це було би порушенням правил доступу центрального процесора. Серед наявних методів виділяються:
- Використовуючи симульоване програмно переривання. Цей метод доступний на більшості апаратних платформ, а тому дуже поширений.
- Використовуючи шлюз виклику. Шлюз виклику — спеціальна адреса у пам'яті ядра у місці, відомому процесору. Коли процесор помічає виклик до цієї адреси, він натомість перенаправляє його до цільового місця без порушення правил доступу. Такий метод потребує апаратної підтримки, але апаратне забезпечення для нього досить поширене.
- Використовуючи спеціальну інструкцію системного виклику. Цей спосіб потребує специфічної апаратної підтримки. Інструкції системних викликів (syscall, sysenter) наявні у сучасних моделях процесорів x86 і активно використовуються сучасними ОС.
- Використовуючи чергу в пам'яті. Програма, що робить велику кількість запитів, але не має потреби чекати результату кожного із них може додавати ці запити в спеціальну ділянку оперативної пам'яті, яка періодично сканується ядром.[джерело?]
Підходи до проектування ядра
Монолітне ядро
У монолітному ядрі усі служби операційної системи запускаються в одній області пам'яті із ниттю ядра. Цей підхід забезпечує глибокий і потужний доступ до апаратних засобів. Деякі фахівці, такі як розробник операційної системи UNIX Кен Томпсон, дотримуються думки, що «реалізація монолітного ядра значно простіша ніж мікроядра». Основним недоліком монолітних ядер є залежність між системними компонентами — помилка у системному драйвері може призвести до збою всієї ОС — цей факт робить великі ядра складними у підтримці.
Мікроядро
Мікроядро складається із простої абстракції над апаратним забезпеченням, включаючи набір примітивів чи системних викликів для реалізації таких мінімальних служб ОС як керування пам'яттю, багатозадачність та інтерфейс міжпроцесної взаємодії. Інші служби, включаючи ті, що зазвичай забезпечуються ядром, реалізовуються як застосунки простору користувача і називаються серверами. Мікроядра легше підтримувані, ніж монолітні ядра, але дуже велика кількість системних викликів та перемикань контексту можуть сповільнити систему, бо вони зазвичай мають більші накладні витрати.
При використанні мікроядра, всю решту операційної системи можна реалізувати як звичайні прикладні програми, що дозволяє використання мов програмування високого рівня. Також можна використовувати різні ОС на одному мікроядрі, динамічно міняти ОС в реальному часі та запускати більше за одну ОС одночасно.
Порівняння монолітних та мікроядер
Із зростанням розміру ядра, все більше проблем стають очевидними. Перш за все це збільшені вимоги до об'єму оперативної пам'яті. Це дещо пом'якшується удосконаленням системи віртуальної пам'яті, але не всі комп'ютерні архітектури підтримують її. Щоб зменшити системні вимоги ядра також дуже ефективним є видалення невикористовуваного коду, але це може бути досить складною задачею при не очевидних взаємозалежностях між компонентами.
До початку 1990-х, внаслідок недоліків монолітних ядер у порівнянні з мікроядрами, монолітні ядра вважалися застарілими більшістю дослідників. Саме тому розробка ядра Linux як монолітного ядра стала темою відомої дискусії між Лінусом Торвальдсом та Ендрю Танембаумом. Суть цієї суперечки представлена у статті Дискусія Танембаума-Торвальдса.
Гібридне ядро
Гібридні ядра є компромісом між монолітними та мікроядрами. Це припускає запуск деяких служб (наприклад мережевого стеку та файлової системи) у просторі ядра для зменшення накладних витрат традиційного мікроядра, але більшість служб запускаються у просторі користувача.
Наноядро
Для зменшення вимог до оперативної пам'яті, наноядро делегує практично всі служби, включаючи найбільш основні, такі як контролери переривань і таймер, до драйверів пристроїв.
Екзоядро
Екзоядро — це тип ядра, що не абстрагує апаратні засоби у теоретичну модель, замість цього воно розподіляє всі апаратні ресурси, включаючи процесорний час, пам'ять та блоки диску, між усіма програмами. Програма, запущена на екзоядрі може використовувати бібліотеку операційної системи для симуляції абстракцій розповсюджених ОС чи розробки програмо-залежних абстракцій для кращої швидкодії.
Історія розвитку різних типів ядер
Ранні ядра операційних систем
Щиро кажучи, ОС чи ядро не є передумовою можливості запуску програмного забезпечення на комп'ютері. Програми можна виконати на «голому залізі» за умови, що автор програми готовий пожертвувати шаром апаратної абстракції та доступом до можливостей ОС. Більшість комп'ютерів у період 1950—1960 років працювали саме таким чином і їх потрібно було перезапускати при закінченні виконання кожної програми. Зрештою були написані невеликі допоміжні програми, такі як програма-завантажувач і зневаджувач, які залишалися у пам'яті між запуском різних застосунків. Це і стало початком зародження розвитку ядер операційної системи.
У 1969 році була представлена RC 4000 Multiprogramming System, що включала невелике ядро «на якому можна створити ядра операційних систем для різних цілей у впорядкований спосіб». Сьогодні такий метод називається мікроядерним підходом.
Операційні системи з розподілом часу
Протягом десятиліття, що передувало появі Unix, обчислювальна потужність комп'ютерів значно зросла — до етапу, коли операторам комп'ютера потрібні були нові шляхи використання вільних ресурсів на їхніх машинах. Однією з основних подій у цей період стала розробка ОС з розподілом часу, які дозволяли багатьом користувачам одночасно працювати за одним комп'ютером, почергово отримуючи фрагменти обчислювального часу.
Впровадження ОС із розподілом часу зіткнулося із безліччю проблем. Однією з них було те, що значно почастішали випадки злому таких систем для присвоєння собі більшої кількості обчислювального часу. З цієї причини безпека та керування доступом стали основними пріоритетами проекту Multics у 1965 році. Іншим поточним питанням був правильний розподіл ресурсів між користувачами: було помічено, що більшість часу користувач просто дивиться на екран, не використовуючи наданих ресурсів, а тому пріоритет повинен надаватися активним користувачам. Нарешті, системи як правило надавали багатошарову ієрархію пам'яті, розділення цього цінного ресурсу привело до сильних зрушень у системах віртуальної пам'яті.
Amiga
Amiga, платформа розроблена компанією Commodore International і випущена у 1985 році була в числі перших (і найуспішніших) домашніх комп'ютерів на мікроядерній операційній системі. AmigaOS мала невелике, проте потужне ядро, яке реалізовувало витискальну багатозадачність на апаратній платформі, подібній до Apple Macintosh (на якій використовувалася кооперативна багатозадачність) і мало розвинену систему динамічних бібліотек, що дозволяло легке розширення.
Unix
Під час стадії проектування Unix програмісти вирішили створити систему, яка моделювала би кожен високорівневий пристрій, доступний ОС, як звичайний файл. Наприклад принтери представлялися як файли, що доступні за певною фіксованою адресою, і коли відбувався запис у файл, то це було сигналом принтеру до роздрукування введеного тексту. Така віртуалізація доступу до пристроїв дозволяла користувачам керувати всією системою, використовуючи звичайні утиліти керування файловою системою. Розширенням цієї парадигми у Unix стала концепція конвеєра, що дозволяє покрокове розбиття процесу, пропускаючи дані файлу через низку однозадачних консольних програм.
Згідно з концепцією Unix, операційна система складається із двох частин: великого набору службових програм, що виконують більшість операцій, і ядра ОС, що запускає програми. За цією концепцією, із позиції програмування, єдиною відмінністю між ними є те, що ядро запускається у режимі супервізора і слугує завантажувачем та керівною програмою для невеликих службових застосунків, що складають решту системи.
Через деякий час ця концепція була дещо змінена, бо трактування всього як файлу чи потоку байтів було не так зручно застосовне, як раніше. Хоча комп'ютерний термінал і можна розглядати як файл або потік байтів, це саме не можна зробити для графічного інтерфейсу користувача. Ще одною проблемою було мережеве сполучення. Навіть якщо його можна порівняти із доступом до файлу на високому рівні, то низькорівнева пакетно-орієнтована архітектура повинна бути призначена для окремих шматків даних, а не всього файлу. Зі зростанням можливостей комп'ютерів операційні системи Unix та побудовані на її базі стали перенасичені програмним кодом. Якщо ядра у 70-80-ті роки мали не більше 100 000 рядків коду, то сучасні Unix-подібні ОС, такі як Linux мають понад 13 мільйонів рядків коду.
Сучасні ОС, похідні від Unix, як правило базуються на монолітних ядрах з можливістю завантаження модулів. Ця група включає Linux та варіанти BSD, такі як FreeBSD, DragonFly BSD, OpenBSD і NetBSD.
Mac OS
Mac OS вперше було представлено у 1984 році. Вона була основною операційною системою персонального комп'ютера Macintosh від Apple Computer. Хоча перші випуски Mac OS не мали таких основних можливостей як багатозадачність та ієрархічна файлова система, ці недоліки були усунуті до версії Mac OS 9. Mac OS X має повністю відмінне гібридне ядро від попередніх версій, яке має назву XNU і є частиною проекту Darwin.
Microsoft Windows
Перша версія Microsoft Windows була представлена у 1985 році як додаток до MS-DOS. Через залежність від іншої ОС, перші версії Windows прийнято називати операційним середовищем (на відміну від поняття операційної системи). Повноцінною ОС Windows стала з появою серії Windows 9x, що включала такі покращення як 32-бітне адресування та витискальну багатозадачність. Паралельно з основною, Microsoft також розробляла серію Windows NT, що була призначена для дуже досвідчених і бізнес користувачів.
Windows XP, випущена у жовтні 2001 року, з'єднувала у собі ці дві серії, об'єднуючи стабільність ядра NT із зручністю користування Windows 9x. Windows NT архітектурно вважається гібридним ядром, де служба графічного інтерфейсу та деякі інші допоміжні програми вбудовані в ядро, а решта служб запускається у просторі користувача.