2. Рішення: правильна ізоляція збоїв
Протягом десятиліть як перевірений методу оперування кодом, що не заслуговує довіри, використовувалося розміщення його в окремому процесі та виконання в режимі користувача. Одним з ключових спостережень, отриманих у дослідженні, якому присвячена ця стаття, є те, що потужним засобом підвищення надійності операційної системи є виконання кожного драйвера у вигляді окремого процесу в режимі користувача з мінімальними необхідними привілеями. Таким чином, код, потенційно містить помилки, ізолю, і помилка, скажімо, в драйвері принтера може призвести до припинення друку, але не до запису перекручених даних у будь-які важливі структури даних ядра і виходу системи з ладу.
У цій статті ми проводимо ретельне відмінність між крахом операційної системи, після якого потрібне перезавантаження комп'ютера, і збоєм або відмовою сервера або драйвера, після якого в нашій системі перезавантаження не потрібно. У багатьох випадках дефектний драйвер, що виконується в режимі користувача, може бути вилучений і замінений без потреби в перезапуску інших частин операційної системи, які виконуються в режимі користувача.
Ми не розраховуємо на те, що незабаром з'явиться код, вільний від помилок, а якщо і з'явиться, то, звичайно, не в операційних системах, які зазвичай пишуться на C або C + +. На жаль, у програмах, написаних на цих мовах, інтенсивно використовуються покажчики, рясний джерело помилок. Тому наш підхід заснований на ідеях модульності та ізоляції збоїв. Шляхом розбиття системи на велику кількість ізольованих модулів, кожен з яких виконується в окремому процесі в режимі користувача, нам вдалося скоротити частину системи, виконувану в режимі ядра, до абсолютного мінімуму і запобігти поширенню збоїв, що виникають в інших модулях. Зменшення розмірів ядра значно скорочує число помилок, які воно, ймовірно, має містити. Малий розмір також дозволяє знизити рівень складності ядра і полегшити його розуміння, що також сприяє надійності. Тому ми пішли максими Сент-Екзюпері і зробили ядро настільки невеликим, наскільки це дозволяють людські можливості: менше 3800 рядків коду.
Одне із зауважень, постійно виникає з приводу таких розробок мінімального ядра, стосується уповільнення роботи системи через додаткові перемикань контексту і копіювання даних, яке потрібно для забезпечення комунікацій різних моделей, які виконуються в користувацькому адресному просторі. Це побоювання, в основному, існує з історичних причин, і ми стверджуємо, що ці причини, більшою частиною, наразі відсутні. По-перше, результати нових досліджень показують, що розробка мінімального ядра не обов'язково завдає шкоди ефективності [3, 23, 15]. Зменшення розмірів ядра при наявності розумних протоколів взаємодії серверів допомагає обмежити масштабність проблеми ефективності. По-друге, значне зростання потужності комп'ютерів в останнє десятиліття істотно послаблює проблему гарантованої продуктивності, що виникає при модульної розробці. По-третє, ми вважаємо, що настає час, коли велика частина користувачів з задоволенням пожертвує деякої ефективністю задля поліпшеної надійності.
Детальний обговорення ефективності нашої системи ми представляємо в розд. 5. Однак тут ми коротко згадаємо три попередніх показника ефективності на підтримку нашого доводу про те, що системи з мінімальним ядром не обов'язково повинні бути повільними. По-перше, виміряний час виконання найпростішого системного виклику getpid складає 1.01 мсек на процесорі Athlon з частотою 2.2 Ггц. Це означає, що програма, яка виробляє 10000 системних викликів в секунду, витрачає на перемикання контексту всього 1% часу ЦП, а 10000 системних викликів в секунду виробляють лише деякі програми. По-друге, наша система здатна протягом 4 секунд повністю провести свою компоновку, включаючи ядро і всі частини, що виконуються в режимі користувача (при цьому компілюються 123 файлу і відбувається 11 редагувань зв'язків). По-третє, час початкового завантаження системи з моменту виходу з монітора багатоваріантної завантаження до видачі запрошення до входу в систему становить менше 5 секунд. Після цього операційна система, повністю сумісна з POSIX, готова до використання.
3. Вклад цієї статті
Дослідження, результати якого описуються в цій статті, було направлено на вироблення відповіді на таке запитання: як уникнути ситуацій, в яких серйозна помилка в драйвері пристрою (наприклад, використання невірного покажчика або наявність нескінченного циклу) призводить до аварійного відмови або зависання всієї операційної системи?
Наш підхід полягав у розробці надійної мультисерверного операційної системи поверх крихітного ядра, що не містить будь-якого зовнішнього, ненадійного коду. Для забезпечення належної ізоляції збоїв кожен сервер і драйвер виконується в режимі користувача в рамках окремого процесу. Крім того, ми додали механізми для відновлення після виникнення поширених збоїв. Ми детально описуємо засоби підтримки надійності і пояснюємо, чому вони відсутні у традиційних монолітних операційних системах. Ми також обговорюємо отримані показники ефективності системи і показуємо, що кошти підтримки надійності сповільнюють систему на 5–10%, але роблять її стійкою до наявності невірних покажчиків, нескінченних циклів і інших помилок, які призвели б до аварійного відмови або зависання традиційних операційних систем.
Хоча ні один з окремих аспектів нашого підходу (ядра невеликого розміру, драйвери пристроїв, які виконуються в режимі користувача, або мультисерверного системи) не є новим, ніхто раніше не збирав до купи всі ці частини для побудови невеликий, гнучкою, модульної UNIX-подібної системи, що є набагато більш відмовостійкої, ніж звичайні системи сімейства UNIX, і втрачає тільки 5–10% ефективності порівняно з нашою базовою системою, яка містить драйвери в ядрі.
Крім того, наш підхід у корені відрізняється від інших аналогічних робіт, оскільки ми не фокусуємося на масових операційних системах. Замість цього ми отримуємо надійність на основі нової, полегшеною архітектури. Замість того щоб додавати допоміжний код, який підвищує надійність ненадійних систем, ми розщеплює операційну систему на невеликі компоненти й досягаємо надійності за рахунок модульності системи. Хоча наші методи незастосовні до успадкованим операційним системам, ми сподіваємося, що вони допоможуть зробити більш надійними майбутні операційні системи.
Ми починаємо статтю з порівняння нашої розробки зі структурами інших операційних систем (розд. 2) і далі переходимо до спільному обговоренню засобів підтримки надійності нашої системи (розд. 3). Потім ми аналізуємо надійність (розд. 4) і ефективність (розд. 5) системи на основі реальних вимірів. У кінці статті ми аналізуємо деякі суміжні роботи (розд. 6) і представляємо свої висновки (розд. 7).
0 комментариев