Построение надежных операционных систем, допускающих наличие ненадежных драйверов устройств



Скачать 403.78 Kb.
Pdf просмотр
страница1/2
Дата17.11.2016
Размер403.78 Kb.
Просмотров583
Скачиваний0
  1   2

Построение надежных операционных систем, допускающих наличие
ненадежных драйверов устройств
Йоррит Хердер (Jorrit N. Herder), Херберт Бос (Herbert Bos), Эндрью Таненбаум (Andrew S.
Tanenbaum)
Перевод -
Сергей Кузнецов
По материалам сайта: http://www.citforum.ru/
Оригинал: A Lightweight Method for Building Reliable Operating Systems Despite Unreliable Device
Drivers (
http://www.minix3.org/doc/reliable-os.pdf
), Technical Report IR-CS-018, January 2006
Я некоторое время колебался, стоит ли переводить этот материал после публикации
своего (очень подробного) пересказа замечательной, на мой взгляд, статьи "Можем ли мы
сделать операционные системы надежными и безопасными". В данном случае мы имеем
дело не со статьей, а с техническим отчетом, и, конечно, текст является менее
качественным (в частности, имеются повторы). Но, подумав, я решил, что высокие
технические качества статьи перевешивают ее небольшие литературные недостатки.
Во-первых, здесь изложены все основные технические идеи ОС MINIX 3. Их простота и
эффективность воодушевляют. Во-вторых, в отчете содержится очень хороший обзор
литературы по современным операционным системам. Мне кажется, что этот материал
может быть полезен всем людям, интересующимся технологией операционных систем и,
прежде всего, преподавателям и студентам ВУЗов. Я проявил дополнительную заботу о
читателях и нашел в Internet свободные ссылки на полные тексты большинства статей,
перечисленных в списке литературы. Эти ссылки добавлены мною в список литературы. К
сожалению, остальные тексты доступны только подписчикам электронных библиотек
ACM
и
IEEE Computer Society
.
Сергей Кузнецов
Аннотация
Хорошо известно, что в большинстве случаев аварийные отказы операционных систем происходят из-за ошибок в драйверах устройств. Поскольку драйверы обычно подключаются к адресному пространству ядра, драйвер, содержащий ошибки, может затереть таблицы ядра и привести к аварийному отказу или останову системы. Нам удалось значительно смягчить эту проблему засчет сокращения размеров ядра до абсолютного минимума и выполнения каждого драйвера в виде отдельного непривилегированного процесса в пользовательском адресном пространстве. Кроме того, мы реализовали POSIX-совместимую операционную систему в виде набора процессов, выполняемых в пользовательском режиме. В режиме ядра выполняется только крошечное ядро, состоящее менее чем из 3800 строк исполняемого кода, которое выполняет начальную обработку прерываний, запускает и останавливает процессы и обеспечивает IPC. За счет перемещения практически всей операционной системы в несколько защищенных процессов, выполняемых в пользовательском режиме, мы уменьшили последствия сбоев, поскольку сбой драйвера больше не является фатальным и не приводит к потребности перезагрузки системы. В действительности, в состав нашей системы входит сервер
реинкарнации, который разработан для борьбы с такими ошибками и часто обеспечивает полное восстановление, прозрачное для приложения и обеспечивающее отсутствие потери данных. Для достижения максимальной надежности в своей разработке мы руководствовались принципами простоты, модульности, наименьшей авторизации и отказоустойчивости. В этой статье обсуждается наш облегченный подход, и приводятся данные о его эффективности и надежности.
Кроме того, наша разработка сравнивается с другими подходами к защите драйверов с
использованием обертывания ядра и виртуальных машин.
Совершенство достигается не тогда,
когда уже нечего прибавить,
а когда уже ничего нельзя отнять
Антуан де Сент-Экзюпери. Планета людей [9]
1. Введение
1.1 Почему у систем случаются отказы?
1.2 Решение: правильная изоляция сбоев
1.3 Вклад этой статьи
2. Разработка операционной системы
2.1 Проблемы монолитных систем
2.2 Системы с минимальным ядром
2.3 Принципы разработки
3. Свойства надежности
3.1 Сокращение числа ошибок в ядре
3.2 Снижение потенциального влияния ошибок
3.3 Восстановление после сбоев
3.4 Ограничение злоупотреблений переполнениями буферов
3.5 Обеспечение надежного IPC
3.6 Ограничение IPC
3.7 Избегание тупиков
3.8 Унификация прерываний и сообщений
3.9 Ограничение функциональных возможностей драйвера
3.10 Запрещение доступа к портам ввода-вывода
3.11 Проверка параметров
3.12 Отлавливание плохих указателей
3.13 Укрощение бесконечных циклов
3.14 Проверка DMA
4. Анализ надежности
4.1 Сервер реинкарнации
4.2 Надежность уровня приложений
4.3 Результаты проверки надежности
5. Измерения производительности
5.1 Результаты тестирования системных вызовов
5.2 Результаты тестирования дискового ввода-вывода
5.3 Результаты тестирования приложений
5.4 Сетевая производительность
5.5 Размер кода
6. Родственные исследования
6.1 Изоляция драйверов в программном обеспечении
6.2 Изоляция драйверов с использованием виртуальных машин
6.3 Средства безопасности, основанные на языках
6.4 Виртуальные машины и экзоядра
6.5 Драйверы, выполняющиеся в пользовательском режиме в монолитном ядре
6.6 Разработки минимальных ядер
6.7 Односерверные операционные системы
6.8 Мультисерверные операционные системы
7. Заключение
8. Благодарности
9. Литература

1. Введение
Наиболее острой проблемой многих пользователей является ненадежность компьютеров.
Исследователи в области компьютерной науки привыкли к регулярным сбоям компьютеров и к необходимости через каждые несколько месяцев устанавливать патчи программного обеспечения. Однако подавляющее большинство пользователей считает это отсутствие надежности неприемлемым. Их внутренняя модель работы электронного устройства основывается на опыте использования телевизоров и видеомагнитофонов: вы покупаете устройство, подключаете его к сети, и оно безупречно работает в течение 10 лет. Никаких отказов, никаких регулярных обновлений программного обеспечения, никаких газетных историй об обнаружении новейших представителей бесконечной череды вирусов. Чтобы сделать компьютерные системы более похожими на телевизоры, мы ставим целью своего исследования совершенствование надежности компьютерных систем, и начинаем с операционных систем.
1.1 Почему у систем случаются отказы?
Основная причина аварийных отказов операционных систем кроется в двух принципиальных дефектах разработки, свойственных всем этим системам: наличие слишком большого числа привилегий и отсутствие адекватной изоляции сбоев. Практически все операционные системы состоят из многочисленных модулей, скомпонованных в одном адресном пространстве и образующих единую бинарную программу, которая выполняется в режиме ядра. Ошибка в любом модуле может легко привести к разрушению структур данных в каком-либо другом, не связанным с ним модуле и к мгновенному выходу системы из строя. Причиной, по которой все модули компонуются в единое адресное пространство без поддержки какой-либо защиты между модулями, является Фаустова сделка разработчиков: улучшенная производительность за цену большего числа отказов системы. Ниже мы оценим стоимость этого компромисса.
Тесно связанный вопрос относится к первопричине аварийных отказов. Ведь если бы каждый модуль был безупречным, то не возникала бы потребность в изоляции сбоев между модулями, поскольку не было бы самих сбоев. Мы утверждаем, что большая часть сбоев возникает из-за ошибок программирования, вследствие чрезмерной сложности и использования чужого кода.
Исследования показывают, что в программном обеспечении в среднем содержится от одной до шестнадцати ошибок на тысячу строк кода [27, 22, 2], и что верхняя граница этого диапазона явно занижена, поскольку учитывались только те ошибки, которые, в конце концов, удавалось обнаружить. Очевидным заключением является то, что в большем объеме кода содержится
большее число ошибок. По мере развития программного обеспечения в каждой его новой версии появляется все больше возможностей (и, соответственно, больший объем кода), и часто новая версия является менее надежной, чем предыдущая. В [22] показано, что число ошибок на тысячу строк кода стремится к стабилизации по мере роста числа выпущенных версий, но асимптотически этот показатель отличается от нуля.
Наличие некоторых из этих ошибок позволяет злоумышленникам применять вирусы и черви для заражения и повреждения системы. Так что некоторые якобы наличествующие проблемы
«безопасности» в принципе не имеют ничего общего с нарушениями мер безопасности
(например, дефектными криптографическими алгоритмами или неустойчивыми протоколами авторизации), а вызываются всего лишь ошибками в коде программ (например, переполнения буферов позволяют выполнять внедренный код). Когда в этой статье мы говорим о
«надежности», мы имеем в виду и то, что часто называют «безопасностью», – неавторизованный доступ вследствие ошибки в коде программы.
Вторая проблема состоит в привнесении в операционную систему чужого кода. Наиболее искушенные пользователи никогда бы не позволили сторонней организации вставить незнакомый код в ядро операционной системы, хотя, когда они покупают новое периферийное
устройство и инсталлируют соответствующий драйвер, они именно это и делают. Драйверы устройств обычно пишутся программистами, работающими на изготовителей периферийных устройств, и контроль качества их продукции обычно ниже, чем у поставщиков операционных систем. В тех случаях, когда драйвер относится к open-source, его часто пишет благонамеренный, но не обязательно опытный доброволец, и контроль качества обеспечивается на еще более низком уровне. Например, в Linux частота появления ошибок в драйверах устройств от трех до семи раз выше, чем в других частях ядра [7]. Даже компания Microsoft, у которой имеются стимулы и ресурсы для применения более плотного контроля качества, не может добиться намного лучших результатов: 85% всех аварийных отказов Windows XP обуславливается наличием ошибок в коде драйверов.
В последнее время появились публикации о родственных работах, посвященных изоляции драйверов устройств с использованием аппаратуры MMU [26] и виртуальных машин [19]. Эти методы концентрируются на решении проблем в унаследованных операционных системах; мы обсудим их в разд. 6. В отличие от этого, при применении нашего подхода надежность достигается путем разработки новой облегченной операционной системы.
1.2 Решение: правильная изоляция сбоев
В течение десятилетий в качестве проверенного метода оперирования кодом, не заслуживающим доверия, использовалось размещение его в отдельном процессе и выполнение в пользовательском режиме. Одним из ключевых наблюдений, полученных в исследовании, которому посвящена эта статья, является то, что мощным средством повышения надежности операционной системы является выполнение каждого драйвера в виде отдельного процесса в пользовательском режиме с минимальными требуемыми привилегиями. Таким образом, код, потенциально содержащий ошибки, изолируется, и ошибка, скажем, в драйвере принтера может привести к прекращению печати, но не к записи искаженных данных в какие-либо важные структуры данных ядра и выходу системы из строя.
В этой статье мы проводим тщательное различие между крахом операционной системы, после которого требуется перезагрузка компьютера, и сбоем или отказом сервера или драйвера, после которого в нашей системе перезагрузка не требуется. Во многих случаях дефектный драйвер, выполняемый в пользовательском режиме, может быть удален и заменен без потребности в перезапуске других частей операционной системы, выполняемых в пользовательском режиме.
Мы не рассчитываем на то, что вскоре появится код, свободный от ошибок, а если и появится, то, конечно, не в операционных системах, которые обычно пишутся на C или C++. К сожалению, в программах, написанных на этих языках, интенсивно используются указатели, обильный источник ошибок. Поэтому наш подход основан на идеях модульности и изоляции сбоев. Путем разбиения системы на большое число изолированных модулей, каждый из которых выполняется в отдельном процессе в режиме пользователя, нам удалось сократить часть системы, выполняемую в режиме ядра, до абсолютного минимума и предотвратить распространение сбоев, возникающих в других модулях. Уменьшение размеров ядра значительно сокращает число ошибок, которые оно, вероятно, должно содержать. Малый размер также позволяет понизить уровень сложности ядра и облегчить его понимание, что также способствует надежности. Поэтому мы последовали максиме Сент-Экзюпери и сделали ядро настолько небольшим, насколько это позволяют человеческие возможности: менее 3800 строк кода.
Одно из замечаний, постоянно возникающее по поводу таких разработок минимального ядра, касается замедления работы системы из-за дополнительных переключений контекста и копирования данных, которое требуется для обеспечения коммуникаций различных моделей, выполняемых в пользовательском адресном пространстве. Это опасение, в основном, существует по историческим причинам, и мы утверждаем, что эти причины, большей частью, теперь отсутствуют. Во-первых, результаты новых исследований показывают, что разработка минимального ядра не обязательно наносит ущерб эффективности [3, 23, 15]. Уменьшение
размеров ядра при наличии разумных протоколов взаимодействия серверов помогает ограничить масштабность проблемы эффективности. Во-вторых, значительное возрастание мощности компьютеров в последнее десятилетие существенно ослабляет проблему гарантированной производительности, возникающую при модульной разработке. В третьих, мы полагаем, что наступает время, когда большая часть пользователей с удовольствием пожертвует некоторой эффективностью ради улучшенной надежности.
Подробное обсуждение эффективности нашей системы мы представляем в разд. 5. Однако здесь мы кратко упомянем три предварительных показателя эффективности в поддержку нашего довода о том, что системы с минимальным ядром не обязательно должны быть медленными. Во- первых, измеренное время выполнения простейшего системного вызова getpid составляет 1.01 мсек на процессоре Athlon с частотой 2.2 Ггц. Это означает, что программа, производящая 10000 системных вызовов в секунду, тратит на переключение контекста всего 1% времени ЦП, а 10000 системных вызовов в секунду производят лишь немногие программы. Во-вторых, наша система способна в течение 4 секунд полностью произвести свою компоновку, включая ядро и все части, выполняемые в режиме пользователя (при этом компилируются 123 файла и совершается 11 редактирований связей). В третьих, время начальной загрузки системы с момента выхода из монитора многовариантной загрузки до выдачи приглашения ко входу в систему составляет менее 5 секунд. После этого операционная система, полностью совместимая с POSIX, готова к использованию.
1.3 Вклад этой статьи
Исследование, результаты которого описываются в этой статье, было направлено на выработку ответа на следующий вопрос: как избежать ситуаций, в которых серьезная ошибка в драйвере устройства (например, использование неверного указателя или наличие бесконечного цикла) приводит к аварийному отказу или зависанию всей операционной системы?
Наш подход состоял в разработке надежной мультисерверной операционной системы поверх крошечного ядра, не содержащего какого-либо внешнего, ненадежного кода. Для обеспечения должной изоляции сбоев каждый сервер и драйвер выполняется в пользовательском режиме в рамках отдельного процесса. Кроме того, мы добавили механизмы для восстановления после возникновения распространенных сбоев. Мы подробно описываем средства поддержки надежности и объясняем, почему они отсутствуют в традиционных монолитных операционных системах. Мы также обсуждаем полученные показатели эффективности системы и показываем, что средства поддержки надежности замедляют систему на 5-10%, но делают ее устойчивой к наличию неверных указателей, бесконечных циклов и других ошибок, которые привели бы к аварийному отказу или зависанию традиционных операционных систем.
Хотя ни один из отдельных аспектов нашего подхода (ядра небольшого размера, драйверы устройств, выполняемые в пользовательском режиме, или мультисерверные системы) не является новым, никто раньше не собирал вместе все эти части для построения небольшой, гибкой, модульной UNIX-подобной системы, являющейся гораздо более отказоустойчивой, чем обычные системы семейства UNIX, и теряющий только 5-10% эффективности по сравнению с нашей базовой системой, содержащей драйверы в ядре.
Кроме того, наш подход в корне отличается от других аналогичных работ, поскольку мы не
фокусируемся на массовых операционных системах. Вместо этого мы получаем надежность на основе новой, облегченной архитектуры. Вместо того чтобы добавлять вспомогательный код, повышающий надежность ненадежных систем, мы расщепляем операционную систему на небольшие компоненты и достигаем надежности за счет модульности системы. Хотя наши методы неприменимы к унаследованным операционным системам, мы надеемся, что они помогут сделать более надежными будущие операционные системы.
Мы начинаем статью со сравнения нашей разработки со структурами других операционных
систем (разд. 2) и далее переходим к пространному обсуждению средств поддержки надежности нашей системы (разд. 3). Затем мы анализируем надежность (разд. 4) и эффективность (разд. 5) системы на основе реальных измерений. В конце статьи мы анализируем некоторые смежные работы (разд. 6) и представляем свои выводы (разд. 7).
2. Разработка операционной системы
Этот проект посвящен построению более надежной операционной системы. Прежде чем подробно описывать свою разработку, мы кратко обсудим, каким образом выбор структуры операционной системы может непосредственно влиять на ее надежность. В своих целях мы будем проводить различие между двумя структурами операционных систем: монолитными
системами и системами с минимальным ядром. Существуют и другие типы операционных систем, такие как экзоядра [10] и виртуальные машины [24]. Они не имеют непосредственного отношения к данной статье, но мы вернемся к ним в разд 6.
2.1 Проблемы монолитных систем
Как показано на рис. 1, в стандартной монолитной системе ядро содержит все операционную систему, скомпонованную в едином адресном пространстве и выполняемую в режиме ядра. Ядро может быть структурировано на компоненты, или модули, показанные на рисунке в виде прямоугольников с пунктирными сторонами, но между компонентами отсутствуют защитные границы. В отличие от этого, прямоугольники со сплошными сторонами соответствуют отдельным процессам, выполняемым в режиме пользователя; каждый из этих процессов выполняется в отдельном адресном пространстве, защищаемом аппаратурой MMU (Memory
Management Unit, устройство управления памятью).
С монолитными операционными системами связан ряд проблем, свойственных их архитектуре.
Хотя некоторые из этих проблем уже упоминались во введении, мы приведем здесь их сводку:
1. Отсутствует должная изоляция сбоев.
2. Весь код выполняется на наивысшем уровне привилегированности.
3. Огромный размер кода предполагает наличие многочисленных ошибок.
4. В ядре присутствует ненадежный сторонний код.
5. Сложность систем затрудняет их сопровождение.
Этот список свойств ставит под сомнение надежность монолитных систем. Важно понимать, что эти свойства возникают не вследствие плохой реализации, а представляют собой фундаментальные проблемы, связанные с архитектурой операционной системы.
Предполагается корректность ядра, в то время, как только лишь его размер означает, что оно должно содержать многочисленные ошибки [27, 22, 2]. Более того, для всех операционных систем, в которых код выполняется на наивысшем уровне привилегированности, и не обеспечивается должное сдерживание распространения сбоев, любая ошибка может стать фатальной. Например, неправильно работающий драйвер устройства, предоставленный сторонним разработчиком, может легко разрушить ключевые структуры данных и вывести из строя всю систему. Реальность подобной угрозы следует из того наблюдения, что аварийные отказы большинства операционных систем случаются по вине драйверов устройств [7, 25].
Дополнительной проблемой является то, что огромный размер монолитных ядер делает их очень сложными и трудно понимаемыми. Без общего понимания ядра даже опытный программист может легко внести ошибки за счет недостаточной осведомленности о побочных эффектах своих
действий.
Рис. 1. Структура монолитной системы. Вся операционная система выполняется в режиме ядра без должной изоляции сбоев.
2.2 Системы с минимальным ядром
На другом полюсе находится минимальное ядро, содержащее только чистый механизм и никакой политики. Минимальное ядро включает обработчики прерываний, механизм для запуска и остановки процессов (путем загрузки регистров MMU и ЦП), планировщик и механизм поддержки межпроцессных коммуникаций; в идеальном случае больше в ядро не входит ничего. Поддержка функциональных возможностей стандартной операционной системы, представленных в монолитном ядре, перемещается в пользовательское адресное пространство, и соответствующий код больше не выполняется на наиболее привилегированном уровне.
Поверх минимального ядра возможны различные организации операционной системы. Одним из вариантов является выполнение всей операционной системы в одном сервере в пользовательском режиме, но в такой архитектуре существуют те же проблемы, что и в монолитной системе, и ошибки по-прежнему могут привести к аварийному отказу всей операционной системы, выполняемой в режиме пользователя. В разд. 6 мы обсудим некоторые работы в этой области.
Лучшим решением является выполнение каждого ненадежного модуля в пользовательском режиме в отдельном процессе, изолированном от других процессов. Мы до крайности увлеклись этой идеей и полностью раздробили свою систему, как показано на рис. 2. Все функциональные компоненты операционной системы, такие как драйверы устройств, файловая система, сервер сети и высокоуровневое управление памятью, выполняются как отдельные процессы в режиме пользователя в собственном адресном пространстве. Эту модель можно определить, как
мультисерверную операционную систему.
С логической точки зрения наши пользовательские процессы можно разбить на три уровня, хотя с точки зрения ядра все они являются всего лишь процессами. Самый низкий уровень процессов, выполняемых в пользовательском режиме, занимают драйверы устройств, каждый из которых управляет некоторым устройством. Мы реализовали драйверы для интерфейса IDE, гибких и жестких дисков, клавиатуры, дисплеев, аудио-устройств, принтеров и различных карт Ethernet.
Выше уровня драйверов находятся серверные процессы. В их число входят файловый сервер, сервер процессов, сетевой сервер, информационный сервер, сервер реинкарнации и другие. Над уровнем серверов выполняются обычные пользовательские процессы, включая различные интерпретаторы shell, компиляторы, утилиты и прикладные программы. Не считая небольшого числа исключений, серверы и драйверы являются нормальными пользовательскими процессами.

Рис. 2. Структура нашей системы. Операционная система выполняется в виде набора изолированных пользовательских процессов поверх крошечного ядра.
Во избежание какой-либо неясности еще раз заметим, что каждый сервер или драйвер выполняется в виде отдельного пользовательского процесса с собственным адресным пространством, полностью отделенным от адресного пространства ядра и других серверов, драйверов и процессов пользователей. В нашей архитектуре процессы не разделяют какое-либо адресное пространство и могут общаться друг с другом только с использованием механизма IPC, обеспечиваемого ядром. Этот аспект является критическим для надежности, поскольку он предотвращает распространение сбоев одного сервера или драйвера на другие серверы или драйверы подобно тому, как ошибка при компиляции программы, возникающая в одном процессе, не влияет на то, что делает браузер в другом процессе.
При работе в пользовательском режиме возможности процессов операционной системы ограничены. Поэтому для поддержки выполнения требуемых от них задач серверами и драйверами ядро экспортирует ряд системных вызовов, которые могут производиться авторизованными процессами. Например, драйверы устройств больше не имеют привилегий на непосредственное выполнение ввода-вывода, но могут запросить у ядра выполнения соответствующих действий от своего имени. Кроме того, серверы и драйверы могут запрашивать сервисы друг у друга. Все такие IPC производятся путем обмена небольшими сообщениями фиксированного размера. Этот обмен сообщениями реализуется путем обращений к ядру, которое до выполнения запрашиваемого действия проверяет, авторизован ли соответствующим образом вызывающий процесс.
Рассмотрим типичный вызов ядра. Компоненту операционной системы, выполняемому в пользовательском режиме в некотором процессе, может потребоваться скопировать данные в другое адресное пространство или из него, но ему невозможно доверить возможность доступа к физической памяти. Взамен этого обеспечиваются вызовы ядра для копирования из допустимых виртуальных адресов или в эти адреса сегмента данных целевого процесса. Этот вызов предоставляет гораздо более слабые возможности, чем запись в любое слово физической памяти, но все-таки эти возможности достаточно мощны, и поэтому возможность такого вызова предоставляется только процессам операционной системы, которым требуется копирование блоков данных из одного адресного пространства в другое. Для обычных пользовательских процессов подобные вызовы запрещены.
После приведения этого описания структуры операционной системы мы можем теперь объяснить, каким образом пользовательские процессы получают сервисы операционной системы, определенные в стандарте POSIX. Пользовательский процесс, желающий выполнить, например, вызов READ, формирует сообщение, содержащее номер системного вызова и
(указатели на) параметры, и обращается к ядру с запросом посылки этого небольшого запросного сообщения файловому серверу, являющемуся другим пользовательским процессом.
Ядро обеспечивает блокировку вызывающего процесса до тех пор, пока его запрос не будет обработан файловым сервером. По умолчанию все коммуникации между процессами запрещаются по соображениям безопасности, но этот запрос достигает цели, поскольку
коммуникации с файловым сервером явно разрешаются обычным пользовательским процессам.
Если запрашиваемые содержатся в буферном кэше файлового сервера, то он производит вызов ядра с запросом копирования этих данных в буфер пользователя. Если у файлового сервера отсутствуют требуемые данные, то он посылает сообщение дисковому драйверу с запросом нужного блока. Тогда дисковый драйвер выдает команду диску на чтение этого блока прямо по адресу внутри буферного кэша файлового сервера. Когда передача данных с диска завершается, дисковый драйвер посылает файловому серверу ответное сообщение, содержащее состояние запроса (успех или причина неудачи). После этого файловый сервер производит вызов ядра с запросом копирования блока в пользовательское адресное пространство.
Эта схема проста и элегантна; она позволяет отделить серверы и драйверы от ядра и позволяет заменять их простым образом, что способствует модульности системы. Хотя здесь требуется до четырех сообщений, они передаются очень быстро (в пределах 500 наносекунд на сообщение в зависимости от ЦП). Если и отправитель, и получатель готовы к коммуникации, то ядро копирует сообщение прямо из буфера отправитель в буфер получателя без его перемещения в адресное пространство ядра. Кроме того, число копирований данных является точно таким же, как в монолитной системе: диск помещает данные прямо в буферный кэш файлового сервера, и имеется одно копирование из этого кэша в адресное пространство пользовательского процесса.



Поделитесь с Вашими друзьями:
  1   2


База данных защищена авторским правом ©nethash.ru 2019
обратиться к администрации

войти | регистрация
    Главная страница


загрузить материал