Смекни!
smekni.com

Побудова надійних операційних систем, що допускають наявність ненадійних драйверів пристроїв (стр. 6 из 9)

Таким чином, у багатьох випадках наша система може забезпечити повне відновлення на прикладному рівні. В решті випадках інформація про збої введення-виведення доводиться до користувача. Можна було б пом'якшити цю незручність шляхом використання тіньового драйвера для відновлення додатків, який використовували зіпсований драйвер в момент його фатального збою, застосовуючи методи, продемонстровані в [25]. Нам не дає зробити це брак робочої сіли.

Результати перевірки надійності

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

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

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

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

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

7. Вимірювання продуктивності

Продуктивність є проблемою, супутньої мінімальним ядер протягом десятиліть. Тому негайно постає питання: у що обходяться що обговорювалися вище зміни? Щоб розібратися в цьому, ми створили прототип, що складається з невеликого ядра і підтримуваного їм набору драйверів пристроїв і серверів, що працюють в режимі користувача. В якості основи прототипу ми почали з використання системи MINIX 2 з-за її невеликого розміру і довгої історії. Код системи вивчався багатьма десятками тисяч студентів в сотнях університетів протягом 18 років, і в останні 10 років майже не надходили повідомлення про помилки, що мають відношення до ядра; мабуть, відсутність помилок пов'язано з малими розмірами ядра. Потім ми значно змінили код, видаливши з ядра драйвери пристроїв і додавши засоби підвищення надійності, що обговорювалися в розд. 3. Таким чином, ми отримали практично нову систему MINIX 3 без потреби у написанні великого обсягу коду, не істотного для даного проекту, такого як драйвери і файлова система.

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

Тестовою системою був 2.2 GHz Athlon (більш точно, AMD64 3200) з 1 Гб основної пам'яті і 40 гігабайтним диском IDE. Жоден з драйверів не був оптимізований для роботи в режимі користувача. Наприклад, ми очікуємо, що на Pentium зможемо забезпечити захищеним чином прямий доступ драйверів пристроїв до необхідних їм портів введення-виведення, усуваючи, таким чином, багато викликів ядра. Однак для підтримки переносимості інтерфейс не буде змінюватися. Крім того, в даний час в драйверах використовується програмований введення-виведення, що набагато повільніше використання DMA. Після реалізації цих оптимізацій ми очікуємо істотного підвищення ефективності. Тим не менше, навіть при використанні існуючої системи погіршення продуктивності виявилося цілком розумним.

Результати тестування системних викликів

Перший пакет тестів містив тести чистих POSIX-сумісних системних викликів. Користувацька програма повинна була зафіксувати реальний час у тактах системних годин (на частоті 60 Гц), потім мільйони раз зробити системний виклик, після чого знову зафіксувати реальний час. Час обробки системного виклику обчислювалося як різниця між кінцевим і початковим часом, поділена на число викликів, за вирахуванням накладних витрат на організацію циклу, які вимірювалися окремо. Число ітерацій циклу було різним для кожного тесту, оскільки тестування 100 мільйонів разів виклику getpid було розумним, але читання 100 мільйонів разів з 64-магабайтного файлу зайняв би надто багато часу. Всі тести виконувалися на незавантажених системі. Для цих тестів частоти успішних звернень до кешу ЦП і кешу файлового сервера імовірно становили 100%.

Коротко проаналізуємо результати цих тестів. Виконання системного виклику getpid зайняло 0.831 мсек при використанні ядерних драйверів і 1.011 мсек при використанні драйверів, що працюють в режимі користувача. При виконанні цього виклику від користувацького процесу менеджеру пам'яті надсилається одиночне повідомлення, на яке негайно виходить відповідь. При використанні драйверів, які виконуються в режимі користувача, виклик виконується повільніше з-за наявності перевірки прав процесів на посилку таких повідомлень. При виконанні такого простого виклику істотне уповільнення викликають навіть кілька додаткових рядків коду. Хоча у відсотках різниця становить 22%, на кожен виклик витрачається лише 180 додаткових наносекунд, так що навіть при частоті 10,000 звернень в секунду втрати складають всього 2.2 мсек в секунду, набагато менше 1%. При виконанні виклику lseek проводиться набагато велика робота, і тому відносні накладні витрати знижуються до 11%. При виконанні відкриття та закриття файлу цей показник становить лише 9%.

Читання і запис 64-кілобайтний ділянок даних займає менше 90 мсек, і падіння продуктивності складає 8%. При використанні драйверів, що виконуються в режимі користувача, створення файлу, запис в нього 1 кілобайт даних і видалення даних займають 13.465 мсек. Через використання буферного кешу файлового сервера в жодному з цих тестів не викликалися драйвери, і тому ми можемо укласти, що інші зміни, не пов'язані з драйверами, сповільнюють систему приблизно на 12%.

Результати тестування дискового введення-виведення

У другому пакеті тестів ми читали з файлу і писали в файл порції від 1 кілобайт до 64 мегабайт. Тести пропускалися багато разів, так що читається файл розміщувався у 12-мегабайтним кеші файлового сервера, крім випадку 64-мегабайтним обмінів, коли обсягу кешу не вистачало. Використання внутрішнього кеша дискового контролера не блокувалося.

Як ми бачимо, різниця в продуктивності становить від 3% до 18%, у середньому – 8.4%. Однак зауважимо, що найгірший показник продуктивності отримано для 1-кілобайтний записів, але абсолютна часом зросла всього на 457 наносекунд. Це співвідношення зменшується при збільшенні обсягу введення-виведення, оскільки скорочуються відносні накладні витрати. У трьох 64-магабайтних тестах, результати яких показані на рис. 6 і 7, це співвідношення становить всього від 3% до 5%.

В іншому тесті проводиться читання з безпосереднього блокового пристрою, відповідного жорсткого диска. Запис на безпосереднє пристрій зруйнувала б його вміст, тому такий тест не виконувався. При виконанні цих тестів не використовується буферний кеш файлової системи, і перевіряється тільки переміщення бітів з диска. Як ми бачимо, в цьому випадку середній показник накладних витрат становить лише 9%.

Результати тестування додатків

Наступний набір тестів складався з реальних програм, а не простих вимірів часу виконання системних викликів. Результати наведено на рис. 8. Перший тест полягав у побудові області початкового завантаження (boot image) у циклі, що містить виклик system («make image»); тим самим, побудова вироблялося багато разів. При кожному побудові компілятор мови C викликався 123 рази, асемблер – 4 рази і компонувальник – 11 разів. Побудова ядра, драйверів, серверів і програми init, а також збірка області початкового завантаження зайняли 3.878 секунд. Середній час компіляції становило 32 мсек на файл.