Оптимизация архитектуры.ppt
- Количество слайдов: 55
Оптимизация архитектуры программных систем 11
Содержание 1. 2. 3. 4. Понятие архитектуры программных систем Что определяет и на что влияет архитектура Архитектуры программных систем Разработка архитектуры программных систем 4. 1. Модульно-интерфейсный (структурный) подход 4. 2. Многослойная (иерархическая) структура программной системы и метод проектирования «сверху вниз» и «снизу вверх» . Формализация 5. Паттерны проектирования архитектуры программных систем 5. 1. Что такое паттерн проектирования 5. 2. Паттерн проектирования архитектуры программной системы 6. Рефакторинг архитектуры программных систем 7. Примеры применения паттерна выделения слоев 8. Архитектуры программных систем. Надежность и производительность 8. 1. Основные принципы разработки архитектуры операционных систем 8. 2. Классическая структура операционной системы 8. 3. Архитектура Windows 8. 4. Макро или микроядро? 9. Архитектура надежных операционных систем 9. 1. Причины ненадежности операционных систем 9. 2. Методы повышения надежности ОС 9. 3. Решения на основе изоляции сбоев и микроядра (проект MINIX 3) 9. 4. Результаты проекта MINIX 3 10. Исследование надежности и производительности ПС различной архитектуры 2
1. Понятие архитектуры ПС Архитектура - это базовая организация системы, воплощенная в ее компонентах, их отношениях между собой и с окружением, а также принципы, определяющие проектирование и развитие системы. [IEEE 1471] Система - это набор компонентов, объединенных для выполнения определенной функции или набора функций. Термин "система" охватывает отдельные приложения, системы в традиционном смысле, подсистемы, системы систем, линейки продуктов, семейства продуктов, целые корпорации и другие агрегации, имеющие отношение к данной теме. Система существует для выполнения одной или более миссий в своем окружении. [IEEE 1471] Миссия - это применение или действие, для которого одно или несколько заинтересованных лиц планируют использовать систему в соответствии с некоторым набором условий. Окружение, или контекст, определяет ход и обстоятельства экономических, эксплуата- ционных, политических и других влияний на систему. Заинтересованное лицо - это физическое лицо, группа или организация (или ее категории), которые заинтересованы в системе или имеют связанные с ней задачи. 3 3
В начале развития информационных технологий термин "архитектура" применялся по отношению только к аппаратному обеспечению (архитектура ЭВМ, вычислительных комплексов, сетей) и лишь позже стал применяться к программному обеспечению. В настоящее время дисциплины о программных архитектурах и их проектирования интенсивно развивается. В любой методологии проектирования (пожалуй, кроме экстремального программирования) понятие "архитектура" занимает ключевое место. Программная система - это совокупность системных, прикладных и сервисных программ, обеспечивающих решение некоторой совокупности задач. ПС могут входить в состав программных комплексов. Обычно это более масштабные системы в рамках больших технических и организационных систем. Системы в свою очередь могут состоять из подсистем. Архитектурные решения необходимы на каждом их этих уровней. В информационных технологиях архитектура используется в следующих областях: - функциональная архитектура (бизнес-архитектура); - системная архитектура (программных систем); - технологическая или инфраструктурная архитектура; - информационная архитектура (организация данных). 4
Большая часть определений архитектуры не определяет термин “компонент”. IEEE 1471 намеренно оставляет это понятие неопределенным, чтобы оно соответствовало множеству толкований, возможных в отрасли. Компонент может быть логическим или физическим, технологически-независимым или технологически-связанным, крупно- или мелкогранулированным. Определение термина "компонент" по спецификации UML 2. 0: Компонентом называется модульная часть системы, которая инкапсулирует ее содержимое; воплощение компонента является замещаемым в его окружении. Поведение компонента определяется в терминах предоставляемого и требуемого интерфейсов. Таким образом, компонент используется в качестве типа, соответствие которого описывается этими двумя интерфейсами, предоставляемым и требуемым (объединяя как статическую, так и динамическую их семантику). В отрасли не существует общепризнанного определения понятия "архитектура”. Архитектура – это: 1) набор значимых решений по поводу организации системы программного обеспечения, 2) набор структурных элементов и их интерфейсов, при помощи которых компонуется система, вместе с их поведением, определяемым во взаимодействии между этими элементами, 3) компоновка элементов в постепенно укрупняющиеся подсистемы , а также стиль архитектуры который направляет эту организацию -- элементы и их интерфейсы, взаимодействия и компоновку. [Крачтен (Kruchten)] 5
Архитектура программы или компьютерной системы - это структура или структуры системы, которые включают элементы программы, видимые извне свойства этих элементов и связи между ними. [Басс (Bass) и др. ] Архитектура - это структура организации и связанное с ней поведение системы. Архитектуру можно рекурсивно разобрать на части, взаимодействующие посредством интерфейсов, связи, которые соединяют части, и условия сборки частей. Части, которые взаимодействуют через интерфейсы, включают классы, компоненты и подсистемы. [UML 1. 5] Архитектура программного обеспечения системы или набора систем состоит из всех важных проектных решений по поводу структур программы и взаимодействий между этими структурами, которые составляют системы. Проектные решения обеспечивают желаемый набор свойств, которые должна поддерживать система, чтобы быть успешной. Проектные решения предоставляют концептуальную основу для разработки системы, ее поддержки и обслуживания. [Мак-Говерн (Mc. Govern)] Большинство определений указывают на то, что архитектура связана со структурой и поведением, а также только со значимыми решениями, может соответствовать некоторому архитектурному стилю, на нее влияют заинтересованные в ней лица и ее окружение, она воплощает решения на основе логического обоснования. 6
2. Что определяет и на что влияет архитектура 1. Архитектура определяет структуру. Структура является важнейшей характеристикой архитектуры. Структурные аспекты архитектуры проявляются многими способами, и в результате большинство определений архитектуры сознательно оставляют их неопределенными. Структурный элемент может быть подсистемой, процессом, библиотекой, базой данных, вычислительным узлом, системой в традиционном смысле, готовым продуктом и так далее. Многие определения архитектуры признают также не только сами структурные элементы, но и композиции из структурных элементов, их связи (и любые соединительные звенья, необходимые для поддержки этих отношений), интерфейсы и разбиение. И вновь, каждый из этих элементов может быть представлен разными способами. Например, соединительное звено может представлять собой сокет, быть синхронным или асинхронным, быть связанным с конкретным протоколом и так далее. Рис. 1 Пример некоторых структурных элементов показан на рис. 1. Изображена диаграмма классов UML, содержащая некоторые структурные элементы, которые представляют систему обработки заказов. Представлено три класса -- Order. Entry, Customer. Management и Account. Management. Класс Order. Entry зависит от класса Customer. Management и от класса Account. Management. 7
2. Архитектура определяет поведение. Наряду с определением структурных элементов любая архитектура определяет взаимодействия между этими структурными элементами. Это такие взаимодействия, которые обеспечивают желаемое поведение системы. На рис. 2 представлена диаграмма сценария UML, которая показывает несколько взаимодействий, которые в сумме позволяют системе поддерживать создание заказа в системе обработки заказов. Рис. 2 Сначала, деятель Sales Clerk создает заказ при помощи экземпляра класса Order. Entry. Экземпляр класса Order. Entry получает сведения о клиенте при помощи экземпляра класса Customer. Management. Затем экземпляр класса Order. Entry использует экземпляр класса Account. Management для того, чтобы создать заказ, внести в него элементы заказа, а затем разместить заказ. 8
3. Архитектура концентрируется на значимых элементах. Хотя архитектура определяет структуру и поведение, она определяет не все в структуре и не все в поведении. Она занимается только такими элементами, которые оцениваются как значимые. Значимые элементы – это главные структурные элементы, связанные с основным поведением и элементы, которые определяют значимые свойства, такие как надежность и масштабируемость. Архитектурную значимость можно назвать экономической значимостью, поскольку главный признак, по которому некоторые элементы оцениваются выше остальных - это стоимость создания и стоимость изменения. Поскольку архитектура фокусируется только на значимых элементах, она предлагает конкретную перспективу оцениваемой системы - перспективу, которая наиболее значима для разработчика архитектуры. В этом смысле архитектура - это некоторое обобщение системы, помогающее разработчику архитектуры управлять сложностью. Набор значимых элементов не является статичным и может измениться с течением времени. Он может измениться при уточнении результата требований, идентификации рисков, создании исполняемой программы. Однако относительная стабильность архитектуры несмотря на изменения, в некоторой степени является признаком хорошей архитектуры, хорошо отлаженного процесса разработки и хорошего разработчика. Если архитектура требует постоянного пересмотра при относительно небольших изменениях, это плохой признак. 4. Архитектура уравновешивает потребности заинтересованных лиц. Архитектура создается для удовлетворения комплекса потребностей заинтересованного лица. Однако часто невозможно выполнить все выраженные пожелания. Например, заинтересованное лицо может попросить, чтобы некоторая функциональность укладывалась в определенный временной промежуток, но эти две потребности (функциональность и промежуток времени) являются взаимоисключающими. 9
Потребности нескольких заинтересованных лиц: • Конечный пользователь заинтересован в интуитивно понятном и корректном поведении, производительности, надежности, удобстве использования, доступности и безопасности; • Системный администратор заинтересован в интуитивно понятном поведении, управлении и инструментах мониторинга; • Специалист по маркетингу заинтересован в конкурентоспособных функциях, времени до выхода программы, позиционировании среди других продуктов и в стоимости; • Клиент заинтересован в цене, стабильности и возможности планировать; • Разработчик заинтересован в понятных требованиях и простом и непротиворечивом принципе проектирования; • Руководитель проекта заинтересован в предсказуемости хода проектирования, планировании, продуктивном использовании ресурсов и бюджета; • Специалист по сопровождению заинтересован в понятном, непротиворечивом и документируемом принципе проекта, а также в легкости, с которой можно вносить изменения. Многие пункты из списка интересов являются нефункциональными, т. е. не влияют на функциональность системы (например, интерес по отношению к цене и планированию). Тем не менее, такие интересы формулируют свойства или ограничения системы. Нефункциональные требования очень часто являются самыми значимыми требованиями, поскольку в них заинтересован разработчик архитектуры. При выборе архитектуры необходимо учитывать основные факторы, определяющие: 1. Трудоемкость разработки - масштаб; сложность (техническую и логическую); ясность целей. 2. Основные цели разработки (цели продукта), вытекающие из требований к системе эффективность; расширяемость (масштабируемость, облегчение добавления новых свойств); адаптируемость (облегчение смены требований); простота (понимания, реализации); надежность (отсутствие ошибок). 3. Цели проекта - удобство сопровождения; стоимость; сроки реализации и т. п. 10
5. Архитектура воплощает решения на основе логического обоснования. Важный аспект архитектуры – это ее логическое обоснование. Важно обеспечить документиро-вание решений, которые привели к созданию этой архитектуры, и логические обоснования таких решений. Эта информация является значимой для многих заинтересованных лиц, особенно для тех, кто должен обслуживать систему. Она часто имеет ценность для разработчика архитектуры, когда ему нужно пересмотреть логические обоснования принятых решений. Например, эта информация используется при пересмотре архитектуры, а разработчику нужно объяснить принятые ранее решения. 6. Архитектура может соответствовать некоторому архитектурному стилю. Архитектурный стиль определяет семейство систем в терминах шаблона организации структуры. Архитектурный стиль определяет номенклатуру компонентов и типов соединительных звеньев, а также набор условий, в соответствии с которыми они могут соединяться [Шоу и Гарлан (Show and Garlan)]. Большинство архитектур построены на основе систем, которые используют сходные наборы интересов. Сходство может быть определено как архитектурный стиль или как особый вид шаблона, хотя этот шаблон часто является сложным и составным. Архитектурный стиль представляет собой кодификацию опыта, который удобно использовать многократно. Примеры архитектурных стилей включают распределенный стиль, стиль "каналы и фильтры", стиль с централизо-ванной обработкой данных, стиль, построенный на правилах и так далее. Конкретная система может демонстрировать более одного архитектурного стиля. В терминах UML шаблон - это общее решение общей проблемы в данном контексте. Применение архитектурного стиля (или шаблона) несколько облегчает жизнь разработчиков, поскольку стиль обычно документирован в терминах рационального обоснования его использования (меньше придется обдумывать) и в терминах его структуры и поведения (придется выработать меньше документации по архитектуре, поскольку вместо этого можно просто обратиться к стилю). 11
7. На архитектуру оказывает влияние ее окружение. Система размещается в некотором окружении, и это окружение оказывает влияние на архитектуру. Окружение определяет границы, в которых должна работать система, а это влияет на архитектуру. Факторы окружения, влияющие на архитектуру - это миссия бизнеса, заинтересованные в системе лица, внутренние и внешние технические ограничения. И наоборот, архитектура тоже оказывает влияние на свое окружение. Создание архитектуры изменяет окружение с технологической точки зрения - может также изменить среду в терминах навыков, доступных в пределах организации. Когда речь идет о преимущественно программных системах, то следует учитывать конкретные аспекты окружения. Для того чтобы ПС работала, ее запускают на некотором аппаратном обеспечении. Поэтому результирующая система представляет собой сочетание программного и аппаратного обеспечения, и именно это сочетание позволяет добиться таких характеристик, как надежность и производительность. Стандарт IEEE Std 12207 -1995, IEEE Standard for Information Technology -- Software Life Cycle Processes определяет систему иначе, чем определение стандарта IEE 1471 (которое концентрируется на преимущественно-программных системах), но при этом согласуется с определением системы в области системного проектирования: Системой называется интегрированный комплекс, состоящий из одного или более процессов, аппаратных устройств, программ, средств и людей, предоставляющий возможность удовлетворить данную потребность или условие. Системой называется набор ресурсов, предоставляющий сервисы, которые используются предприятием для выполнения цели или миссии бизнеса. Компоненты системы обычно состоят из аппаратного обеспечения, программного обеспечения, информационных ресурсов и сотрудников. 12
8. Архитектура оказывает влияние на структуру коллектива. Архитектура определяет группировки родственных элементов, которые адресуют данный набор интересов. Например, архитектура системы обработки заказов может иметь определенные группировки элементов для ввода заказов, ведения счетов, работы с клиентами, выполнения заказа, интеграции с внешними системами и безопасности. Каждая из этих группировок может требовать различных наборов навыков. Поэтому имеет смысл выровнять структуры групп разработчиков ПС в соответствии с архитектурой после того, как она будет определена. Однако чаще встречается ситуация, когда первоначальная группа разработчиков оказывает влияние на архитектуру, а не наоборот. Это ошибка, которой следует избегать, поскольку в результате обычно получается архитектура, далекая от идеала. "Закон Конвея" утверждает, что "если у вас есть четыре группы, работающих над компилятором, то вы получите четырехпроходный компилятор". Практически часто непреднамеренно создаются архитектуры, которые являются отражением организации, создающей архитектуру. 9. Архитектура представлена в каждой системе. Каждая система имеет архитектуру, даже если эта архитектура формально не документирована или система слишком проста. Обычно документирование архитектуры представляет собой очень ценное средство. Документированные архитектуры имеют тенденцию быть более продуманными - а, следовательно, более эффективными - чем недокументированные, поскольку процесс записи архитектуры естественным образом ведет к всестороннему обдумыванию. Если архитектура не документируется, трудно (если не невозможно) доказать, что она соответствует утвержденным требованиям в терминах адресных характеристик, таких как удобство обслуживания, заимствование передового опыта и так далее. 13
3. Архитектуры программных систем 1. Архитектуры, основанные на потоках данных. Система представляется как совокупность процессов, каждый из которых принимает данные из различных источников, а также возвращает данные в другие источники или хранилища данных. Процессы проектируются независимо друг от друга. Такие архитектуры давно используются и изображаются с помощью диаграмм потоков данных (DFD-диаграммы). Рис 3. Метамодель диаграммы потоков данных Как разновидность таких архитектур рассматриваются архитектуры типа каналы и фильтры, в которых процессы называются фильтрами, а потоки направляются через каналы. К этому классу архитектур можно отнести и метод портов. 2. Архитектуры независимых компонент. Система состоит из независимо работающих компонент, общающихся друг с другом. Компонент, называемый клиентом, выдает запрос для выполнения какого-либо действия к другому компоненту, называемому сервером. Такое отношение называется клиент-серверным. Классическая двухуровневая архитектура клиентсервер постоянно усложнялась добавлением новых уровней. Средний уровень между клиентом и сервером может играть роль шины данных, как это определено в стандарте CORBA. В объектно -ориентированном проектировании роль компонентов стали играть объекты. 14
На рис. 4 приведена эталонная модель метаобъектного средства OMG. На нижнем уровне находятся отдельные объекты. Объектные типы определяют общие свойства схожих объектов. Объектные типы в свою очередь являются экземплярами метаобъектной модели. С помощью этой метамодели описываются архитектуры OMG/CORBA, Microsoft/COM и Java/RMI. В том случае, когда в системе одновременно запускаются несколько процессов, каждый из которых может быть представлен отдельным компонентом или быть потоком в терминах Java, говорят об архитектуре параллельных взаимодействующих процессов. В нотации UML такая архитектура может быть описана диаграммой последовательностей. Рис 5. Метамодель диаграммы последовательностей Имеется и третий тип архитектур событийно-управляемых систем, в которых компоненты постоянно находятся в состоянии ожидания, пока не произойдет воздействующего на них события. 3. Архитектуры виртуальных машин. Одним из компонентов в данном случае является интерпретатор специального языка, а приложение описывается в терминах этого языка. Такой подход оправдан, если элементы можно выразить на этом языке, а также если реализуется несколько приложений. Для удобства язык может иметь не только текстовое представление, но и графическое. 15
4. Репозиторные архитектуры. Системы с этим типом архитектуры строятся вокруг данных. Широкий подкласс составляют системы баз данных, в которых репозиторий (хранилище данных) управляется с помощью СУБД. На основе Web-технологий можно организовать хранилища другого типа, которые войдут в гипертекстовую архитектуру. 5. Уровневые архитектуры. Система разбивается на иерархически упорядоченные части, называемые уровнями. Уровень образуется группой тесно связанных модулей (компонентов). Каждый уровень имеет четко определенное сопряжение только с соседними по иерархии уровнями. Клиентсерверную архитектуру также можно отнести к уровневой. Построение приложения уровень за уровнем существенно облегчает процесс разработки. 6. Сервис-ориентированные архитектуры. В последнее время де-факто становится стандартом для создания распределенных приложений технология Web-сервисов. Архитектуру этого типа можно отнести к архитектуре независимых компонент. Основное достоинство такого рода сервисов - простота создания. Упрощается взаимодействие между компонентами, поскольку основным транспортным протоколом является http. Приложение может работать не только в Intranet-, но и в Internet-сетях. Кроме того, эта технология не привязана к какой-либо одной платформе, что открывает возможность создания распределенных приложений в гетерогенных средах. Технология интегрирована с технологией DOT. NET. Логическим продолжением технологии Web-сервисов стала архитектура, ориентированная на сервисы (SOA). Ее появление является следствием новых задач и потребностей, возникающих при создании и эксплуатации современных информационных систем: - оперативное реагирование на изменение требований и быстрая адаптация к новым задачам; - оптимизация управления бизнес-процессами; - эффективное обеспечение внешних взаимодействий. 16
К основным характеристикам SOA, отличающим ее от других архитектур информационных систем, следует отнести: - распределенность; - слабосвязанные интерфейсы (отсутствие жестких связей между элементами системы), что упрощает конфигурирование системы и координирование работы ее элементов; - архитектура базируется на международных открытых стандартах; - возможность динамического поиска и подключения нужных функциональных модулей; - система строится с расчетом на процессы с использованием сервисов, ориентированных на решение определенных бизнес - задач. Рис. 6 Метамодель SOA включает следующие модели: - модель политик (POM) определяет политики, связанные с архитектурой, в основном она представляет собой ограничения, накладываемые на поведение агентов и сервисов, в том числе ограничения, связанные с требованиями безопасности и качества обслуживания; - модель, ориентированная на сервисы (SOM), сосредоточена на действиях, выполняемых сервисами; 17
- модель, ориентированная на ресурсы (ROM), сосредоточена на системных ресурсах; - модель, ориентированная на сообщения (MOM), сосредоточена на сообщениях, их структуре, способах транспортировки и др. Важную роль в этой архитектуре отводится управлению Web-сервисами, к основным функциям которого относятся развертывание, конфигурирование, обеспечение безопасности, а также функции мониторинга и диагностики. В сервисной архитектуре просматриваются черты традиционных архитектур. С помощью сценарных языков типа BPEL можно описывать новые сервисы или описывать бизнес-процессы с участием сервисов (виртуальная машина). Системы, реализованные в других стандартах (например, CORBA) легко интегрируются в SOA (соответствующий инструментарий имеется). SOA представляет собой не только основу для новых информационных технологий, но также и новую системную философию. 4. Разработка архитектуры ПС Архитектура программного обеспечения - это первичная организация системы, сформированная ее компонентами, отношениями между компонентами и внешней средой системы, а также принципами, определяющими дизайн и эволюцию системы [Recommended Practice for Architectural Description of Software-Intensive Systems, ANSI/IEEE Std 1471 -2000]. Разработка архитектуры - важный этап в жизненном цикле ПС, основное содержание концептуального проектирования. Этап анализа требований к системе завершается разработкой функциональной архитектуры (внешнее описание системы, сведения об основных компонентах и их взаимосвязях, об основных алгоритмах, основных структурах данных), этап проектирования - разработкой системной архитектуры (модульноиерархическую структура системы, включающая решения по организации данных и функциональные спецификации отдельных модулей, детализация до уровня, который делает возможным реализацию). 18
4. 1. Модульно – интерфейсный подход (структурный подход) 1. Декомпозиция системы на модули по структурному или функциональному признаку. 2. Модули и их взаимные связи образуют абстракцию системы высшего уровня. 3. Описывается каждый модуль и определяется его интерфейс. 4. Проводится декомпозиция каждого модуля и т. д. Спецификации модулей и их интерфейсов дают структурную основу для проектирования каждого модуля и всей системы в целом. Правильное определение и выделение модулей представляет собой сложную задачу. Тесно связанные между собой части системы должны входить в один и тот же модуль. Концепция слоев — одна из общеупотребительных моделей, используемых разработчиками программного обеспечения для разделения сложных систем на более простые части. В архитектурах компьютерных систем, например, различают слои кода на языке программирования, функций операционной системы, драйверов устройств, наборов инструкций центрального процессора и внутренней логики микросхем. В среде сетевого взаимодействия протокол FТР работает на основе протокола ТСР, который, в свою очередь, функционирует "поверх" протокола IР, расположенного "над" протоколом Ethernet. Конечный пользователь Программист Прикладные программы Утилиты Компиляторы Редакторы Интерпретатор ы команд Разработчик ОС Операционная система Машинный язык Микроархитектура (регистры ЦП, АЛУ) Физические устройства (контроллеры, шины, монитор и т. д. ) 19
Основные причины интереса к слоям архитектуры программных систем: Слои легко формализуются. Интуитивно понятно, что если система разбита на ряд слоев, то слой n – это компонент или набор компонентов системы, которые используют только компоненты слоя n-1 и могут быть использованы только компонентами слоя n+1. Слои обладают простой и наглядной семантикой. Как правило, в архитектуре программной системы слои представляют уровни абстракции. Слой n+1 использует слой n, следовательно, абстракция понятий слоя n+1, не ниже чем у слоя n, а в идеале – если архитектура системы эффективна, его уровень абстракции должен быть выше. Соответственно, слой n скрывает (инкапсулирует) логику работы с понятиями определенными на этом слое, позволяя, таким образом, слою n+1 реализовать работу с более сложными понятиями, организовать более сложную логику, используя выразительные средства нижележащего слоя. Слои широко распространены. Действительно, достаточно большое количество программных систем, особенно если речь идет о программных системах масштаба предприятия, имеют именно слоистую структуру. Встречается ситуация, когда строгая послойная структура системы нарушается. Как правило, это является следствием эрозии архитектуры (архитектурным дефектом) и ее устранение в большинстве случаев способно принести ощутимую пользу. Альтернативная реализация. Можно выбирать альтернативную реализацию базовых слоев – компоненты верхнего слоя способны работать без каких-либо изменений в нижележащих слоях, при условии сохранения интерфейсов. Минимизация интерфейсов. Зависимость между слоями, то есть, фактически, интерфейсы, предоставляемые нижними слоями верхним, можно свести к минимуму. Такая минимизация интерфейсов – позволяет увеличивать гибкость системы. Между уровнями можно организовать четкий интерфейс. Систему можно спроектировать методом «сверху вниз» , а реализовать методом «снизу вверх» . Уровни реализуются в соответствии с их порядком, начиная с аппаратуры и далее вверх. Каждую новую виртуальную машину можно детально проверить, после чего продолжать дальнейшую работу. Любой слой достаточно просто модифицировать, не затрагивая другие слои и не меняя межслойные интерфейсы. 20
Схема архитектурных слоев обладает и определенными недостатками: Каскадные изменения. Слои способны удачно инкапсулировать многое, но не все: модификация одного слоя подчас связана с необходимостью внесения каскадных изменений в остальные слои. Классический пример из области корпоративных программных приложений: поле, добавленное в таблицу базы данных, подлежит воспроизведению в графическом интерфейсе и должно найти соответствующее отображение в каждом промежуточном слое. Падение производительности. Наличие избыточных слоев нередко снижает производительность системы. При переходе от слоя к слою данные обычно подвергаются преобразованиям из одного представления в другое. Несмотря на это, инкапсуляция нижележащих функций зачастую позволяет достичь весьма существенного преимущества. Например, оптимизация слоя транзакций обычно приводит к повышению производительности всех вышележащих слоев. 4. 2. . Многослойная (иерархическая) структура программной системы и метод проектирования «сверху вниз» и «снизу вверх» . Формализация 1. Программная система представляется в виде иерархии слоев. 2. Верхний слой определяет виртуальную систему с желаемыми свойствами. 3. Каждый следующий слой детализирует вышележащий, выполняя для него некоторый набор функций. 4. Межслойные интерфейсы подчиняются строгим правилам. Связи внутри слоя могут быть произвольными. 5. Отдельный модуль слоя L(i) может выполнить работу самостоятельно или последующим вариантам: обратиться только к слою L(i – 1); обратиться к некоторой команде определенного слоя L(q), который выполняет требуемую функцию (i – 2 <= q <= 0); обратиться к любому последующему слою L(s), (i – 2 <= s <= 0). 21
Kn 1 Kn 2 S = { L i | i = 0, 1, 2, … n } – множество слоев программной системы; Li = { K ij | i = const ; j = 1, 2, … mi } – множество компонентов слоя Li ; Ln Ln-1 Ø; Ln-2 S = U Li = { K ij | i = 0, 1, 2, … n ; j = 1, 2, … mi } – множество компонентов системы; Kq 1 Lq L 0 G = { S, O S X S } – граф структуры системы; Gi = { Li , Oi Li X S Li } – подграф связей слоя Li ; K 01 Kij = < N, T, I, O, A > - компонент. Рис. 7 Холстед М. X. Начала науки о программах / Пер. с англ. – М: Финансы и статистика, 1981. – 128 с, ил. Сделана попытка создать метрическую теорию исследования программ. Теория основывается на простых предположе-ниях и подкрепляется статистическим материалом по результатам анализа многих программ. Исходя из простых комбинаторных соображений, и учитывая структурные особенности исследуемых языков программирования и текстов написанных на них программ, М. X. Холстед получает пригодные для практического использования соотношения между основными программными параметрами. Выводятся формулы для длины программы и ее объема. Получены важные соотношения между операторами и операндами, составляющими словарь программы, даются практические рекомендации по вычислению к определению размера операционной системы. 22
5. Паттерны проектирования архитектуры программных систем 5. 1. Что такое паттерн проектирования Шаблоны проектирования (паттерн, pattern) — это эффективные способы решения характерных задач проектирования, в частности проектирования компьютерных программ. Паттерн не является законченным образцом проекта, который может быть прямо преобразован в код, скорее это описание или образец для того, как решить задачу, таким образом, чтобы это можно было использовать в различных ситуациях. . В общем случае паттерн состоит из четырех основных элементов: 1. Имя. Сославшись на него, можно сразу описать проблему проектирования; ее решения и их последствия. Присваивание паттернам имен позволяет проектировать на более высоком уровне абстракции. С помощью словаря паттернов можно вести обсуждение с коллегами, упоминать паттерны в документации, в тонкостях представлять дизайн системы. 2. Задача (проблема, ситуация). Описание того, когда следует применять паттерн. Необходимо сформулировать задачу и ее контекст. Может описываться конкретная проблема проектирования, например способ представления алгоритмов в виде объектов. Также может включаться перечень условий, при выполнении которых имеет смысл применять данный паттерн. 3. Решение (рекомендации, примеры). Описание элементов дизайна, отношений между ними, функций каждого элемента. Конкретный дизайн или реализация не имеются в виду, поскольку паттерн - это шаблон, применимый в самых разных ситуациях. Просто дается абстрактное описание задачи проектирования и того, как она может быть решена с помощью некоего весьма обобщенного сочетания элементов. 4. Результаты (преимущества, недостатки) – это следствия применения паттерна и разного рода компромиссы. Хотя при описании проектных решений о последствиях часто не упоминают, знать о них необходимо, чтобы можно было выбрать между различными вариантами и оценить преимущества и недостатки данного паттерна. Здесь речь идет и о выборе языка и реализации. Поскольку повторное использование зачастую является важным фактором, то к результатам следует относить и влияние на степень гибкости, расширяемости и переносимости системы. 23
5. 2. Паттерн проектирования архитектуры программной системы Имя: выделение слоев Ситуация: Организовать структурные элементы системы в отдельные уровни со взаимосвязанными обязанностями таким образом, чтобы на нижнем уровне располагались низкоуровневые службы и службы общего назначения, а на более высоких - объекты уровня логики приложения. При этом взаимодействие и связывание уровней происходит сверху вниз. Связывания объектов снизу вверх следует избегать. На диаграмме представлены элементы, для которых верны следующие условия: • Исходящие связи ведут только в последний выделенные слои (слой с номером n), или их нет, если ранее не был выделен ни один слой. • Кандидаты на объединение в новый слой с номером n+1 должны обладать общим смыслом и/или функциональностью. Простейшей проверкой на наличие общности является простой критерий: если для кандидатов можно подобрать "общее определение", то можно считать что они обладают требуемой общностью. Решение: Объединить блоки в новый слой “n+1”. Для двух произвольных слоев слой, обладающий большим порядковым номером, считается "вышележащим". Важно отметить, что если в результате применения паттерна было выделено n слоев, и еще остались блоки, которые в силу ограничений не смогли быть отнесены ни к одному из выделенных слоев и формально не могут быть выделены в новый слой, то эти блоки по-умолчанию считаются n+1 слоем, который в дальнейшем именуется "чердаком". Строгие (канонические) слои. Канонический вариант слоев не допускает никаких отклонений в структуре и потому встречающийся относительно редко. На рис. 12 А) могут быть выделены следующие строгие слои: слой 1 – блоки с номерами 5, 6. Слой 2 – блоки 3, 4. Слой 3 – блоки 1, 2. Систем с четкой послойной структурой, известной как строгие слои, значительно меньше, чем систем с псевдослоями – т. е. со структурой, близкой к послойной, но с допустимыми отклонениями. 24
Нестрогие слои (Non-strict layers). Смягчение условий: нестрогие слои допускают связи от вышележащего слоя к нескольким нижележащим слоям (потенциально – ко всем), а не только к непосредственному соседу снизу. Архитектура с нестрогими слоями может быть как результатом эрозии, так и осознанным решением Рис. 12 На рис. 12 Б) показаны шесть нестрогих слоев – по одному блоку в каждом слое: слой 1 – блок 6, слой 2 – блок 5, слой 3 – блок 4 и т. д. В то же время, выделение строгих слоев даст значительно более грубую структуризацию: слой 1 – блок 6, слой 2 – блок 5. Все остальные блоки попадут на "чердак", так как дальнейшее выделение строгих слоев невозможно – блок 4 нарушает строгую структуру и обращается к первому слою в обход второго слоя. Все оставшиеся блоки используют блок 4 и, соответственно, тоже попадают на "чердак". Последствия: возможным дефектом архитектуры с нестрогими слоями является нарушение абстракции. Подобные дефекты затрудняют анализ системы. Кроме того, изменения слоя в такой архитектуре значительно сложнее локализовать – волна изменений прокатится по всем слоям, работающим с изменяемым слоем, то есть, в пессимистическом случае, по всем вышележащим слоям. 25
Поглощение сильносвязанных компонентов (СК). Смягчение условий: уже рассмотренные виды слоев можно модифицировать, позволив включать в произвольный слой СК. При таком подходе СК рассматривается, фактически, как атомарный элемент. Без подобного смягчения условий как сам СК, так и все блоки которые могли бы попасть в вышележащие слои, будут отправлены на "чердак". Действительно, рассмотренные подходы не смогут расщепить СК на слои, и он автоматически попадает на чердак, так же, как и блоки, зависящие от него. Тем не менее, не всегда СК на структурных диаграммах свидетельствуют о плохой архитектуре системы. Например, в системах с рассылкой сообщений часто встречается сильносвязанный компонент (рис 13): источник событий "знает" о зарегистрированных в нем слушателях (listeners), слушатели знают о событиях (events), отправляемых источником, а в событии содержится ссылка на его источник. Таким образом, удобно выделять слои и на тех диаграммах, которые содержат СК, именно поэтому было введено поглощение. Eventlisteners Рис. 13 Eventsource Последствия: возможным дефектом архитектуры с поглощающими слоями может стать эффект "пропавшего слоя" – дефектная связь приводит к появлению СК, которые по смыслу должны находится на разных слоях. На рис. 12 В) при использовании поглощения СК можно выделить те же слои, что и в случае применения строгих слоев к 12 А). Слой 1 – блоки 5, 6. Слой 2 – блоки 3, 4. Слой 3 – блоки 1, 2. Если применять обычный поиск строгих слоев, то все блоки окажутся на "чердаке". На рис. 12 Г) при использовании поглощения СК можно выделить слой 1 – блок 6, слой 2 – блок 5, слой 3 – блоки 3, 4, слой 4 – блоки 1, 2. Если применять обычный поиск нестрогих слоев, блоки 1, 2, 3, 4 окажутся на "чердаке", то есть структуризация в таком случае будет значительно хуже. 26
Значение паттерна выделения слоев для улучшения системы. Выделение слоев является одним из ключевых паттернов для разработки, анализа и улучшения архитектуры. Слои позволяют упорядочивать архитектурные модели и упрощать их анализ. Существуют различные разновидности слоев и различные тактики применения этого паттерна. С архитектурной точки зрения, с каждой из разновидностей слоев связаны определенные плюсы и минусы, что позволяет быстро находить и устранять различные архитектурные дефекты. Необходимо отметить, что поиск блоков, которые могут быть выделены в слой на больших диаграммах, является затруднительной задачей, но решение такой задачи достаточно легко автоматизировать. Для автоматического поиска кандидатов на объединение в новый слой достаточно перебрать блоки исследуемой структурной модели и проанализировать направления их связей (KLOCwork Architect). Тактика применения паттерна выделения слоев имеет два основных аспекта: масштаб и время. Масштаб. Выделение слоев можно применять на разных масштабах и уровнях абстракции. Слои облегчают изменение масштабов абстракции, используемые для моделирования. Если в системе найдены слои, то изменение масштабов можно производить и вручную – несколько соседних слоев достояно легко объединяются в один слой – происходит укрупнение подсистем. Понижение масштаба может быть достигнуто за счет разбиения одного слоя на несколько подслоев (это не всегда возможно, в отличие от объединения слоев). Время – когда нужно применять выделение слоев? Это достаточно хороший паттерн, чтобы с него начать проектирование, анализ или рефакторинг архитектуры системы. Выделение слоев ведет к тому, что, задача сводится к меньшим подзадачам; теперь детальный семантический анализ можно применять только к тем слоям, которые имеют непосредственное отношение к поставленной задаче. Выделение слоев особенно хорошо работает в связке с другими паттернами. Шансы на успех резко вырастут, если провести предварительную подготовку, применив как минимум "удаление мертвых блоков" и "инкапсуляцию приватных блоков", описанных в [9]. 27
6. Рефакторинг архитектуры программных систем В последнее время наблюдается тенденция к увеличению продолжительности жизненного цикла успешных программных проектов. Как следствие, растет объем унаследованного кода, поддерживаемого сообществом разработчиков. Современные методики переоценивают значение начальной фазы жизненного цикла программной системы и практически игнорируют ее дальнейшую эволюцию. 1. Зачем менять архитектуру? Потребность в изменении существующего ПО может возникнуть в ходе его модернизации. В общем случае изменения существующего программного обеспечения способны затронуть не только его код, но и его архитектуру. В качестве примеров можно привести следующие сценарии, требующие изменения архитектуры существующего ПО: Преобразования, обусловленные функциональными изменениями ПО. Желательно, чтобы внедрение новой функциональности не затронуло существующую логику системы. Также желательно, чтобы сложность внедрения новой функциональности в существующую систему не превышала сложность реализации этой функциональности в рамках нового проекта. Хорошая архитектура позволяет достичь поставленных целей. Итак, изменение существующей архитектуры – хороший шаг на пути внедрения новой функциональности, облегчающий дальнейшую эволюцию системы. Смена платформы ПО. Крайне желательно, чтобы смена платформы ПО как можно меньше затронула код, и можно было бы ограничиться изменениями только в узкой платформенно-зависимой прослойке системы. Выделение такой прослойки – архитектурная задача. Ее решение всегда сопряжено с необходимостью изменения архитектуры. Преобразования, связанные с реорганизацией компании, ведущей разработку. Пример такой реорганизации – аутсорсинг. Решение об использовании аутсорсинга - типичный шаг по оптимизации производства. Этот шаг зачастую затрудняется проблемой выделения и передачи компонентов для внешней разработки. Изменение архитектуры программной системы способно облегчить решение этой задачи. 28
2. Как представить архитектуру и ее изменения? Специфика описания архитектуры и ее изменений в отличие от программного кода связана с тем. что архитектура не имеет явного представления, за исключением тех случаев, когда она явно задокументирована. Однако даже в последнем случае трудно гарантировать соответствие задокументированной архитектуры той, которая на самом деле существует в системе. Способом описания архитектуры и ее изменений могут стать структурные модели. Для исследования архитектуры ПС может быть использована нотация структурного моделирования, (например, в KLOCwork Architect), позволяющая извлекать и редактировать структурные модели архитектуры ПС из программного кода. Модели программных систем, используемые в KLOCwork Architect, напоминают модели типа сущность-связь. Основными элементами модели являются следующие элементы: Архитектурный блок (AB), представляющих в модели структурные элементы ПС, вне зависимости от уровня абстракции, на котором идет моделирование. АВ обладают, по меньшей мере, двумя основными атрибутами: имя и тип. Типы АВ существенно зависят от уровня абстракции, на котором происходит моделирование, и задачи, в рамках которой проводятся исследование архитектуры. Например, при моделировании систем, построенных в рамках каких-либо компонентных технологий, используемым типом архитектурных блоков являются компоненты. Отношение (Relation). В модели KLOCwork Architect под отношением понимается односторонняя связь между парой архитектурных блоков. Так же, как и архитектурные блоки, отношения могут быть различных типов. 3. Рефакторинг архитектуры. Одним из наиболее успешных подходов к изменению существующего программного обеспечения является рефакторинг – изменение во внутренней структуре ПС, имеющее целью облегчить понимание ее работы и упростить модификацию, не затрагивая наблюдаемого поведения. В привычном понимании разработки ПС сначала создается дизайн системы, а потом пишется ее код. Со временем код модифицируется, и целостность системы, соответствие ее структуры изначально созданному дизайну постепенно ухудшается. Дальнейшее развитие системы постепенно сползает от направленной, проектируемой деятельности к хакерству. 29
Рефакторинг представляет собой противоположную практику. С его помощью можно взять плохой, хаотический проект и переделать его в хорошо спроектированный код. Каждый шаг этого процесса чрезвычайно прост. Например, шагом может стать перемещение поля или метода из одного класса в другой, расщепление класса и т. д. Однако суммарный эффект таких небольших изменений оказывается кумулятивным и может радикально улучшить проект. При переходе от классического рефакторинга к архитектурному меняются: объекты, с которыми идет работа. В классическом рефакторинге сущностями, с которыми идет работа, являются такие элементы, как класс, экземпляр класса. Архитектурный рефакторинг применяется к системам и компонентам; масштаб изменений. Классический рефакторинг применяется в существенно меньших масштабах – обычно последствия применения паттерна классического рефакторинга ограничивается несколькими файлами. Паттерны архитектурного рефакторинга применяются к компонентам архитектуры; описание изменений. Методы классического рефакторинга можно проиллюстрировать статическими моделями языка и фрагментами кода. Для описания архитектурного рефакторинга используются специализированные структурные модели и инструментальные средства поддержки обратной инженерии. тестирование. Трансформации можно считать корректными, если они не приводят к изменениям поведения ПС в целом. Наличие о полного набора модульных тестов позволяет убедиться в корректности трансформаций. Для архитектурного рефакторинга также встает задача автоматической проверки корректности трансформаций. Наличие модульных тестов повышает уверенность разработчика в корректности проведенных изменений применении архитектурного рефакторинга ( на настоящий момент неизвестно, насколько эффективно модульное тестирование работает с методами рефакторинга архитектуры). 30
4. Фазы архитектурного рефакторинга: фаза выявления ("раскопки") архитектуры путем автоматического построения (извлечения) модели архитектуры из программного кода; фаза трансформации архитектуры. Изменение модели (оптимизация) архитектуры с последующим проецированием их на реальный код программной системы; Выделение слоев – хорошая основа для улучшения системы. Найти строгие слои в произвольной программной системе значительно труднее, чем найти слои с некоторыми допустимыми отклонениями. Тем не менее, для каждого из допустимых отклонений известны побочные эффекты, которые можно устранять, по возможности приводя слои к строгим. фаза семантического анализа подсистем. Выявление смысловой нагрузки подсистем. Для решения подобных задач, даже в первом приближении, зачастую приходится исследовать реальный программный код; фаза проецирования изменений модели на программный код. При проецировании удаления блоков из модели необходимо определить множество строк и файлов, которое соответствует удаленному блоку в программном коде. При проецировании на код переноса блока в модели переносятся соответствующие строки и файлы в исходном коде программной системы и т. д. 31
7. Примеры применения паттерна выделения слоев Улучшение архитектуры и нестрогие слои. В работе [9] описывается случай, когда найденные нестрогие слои позволили выявить архитектурный дефект в программной системе toolbus, существенно затруднявший реализацию задачи, поставленной перед разработчиками. Программная система toolbus представляет для своих клиентов механизмы коммуникации. В системе используется набор стандартных сообщений, которые были реализованы на основе сокетов операционной системы и специализированного текстового протокола для обмена данными по сокетам (на рис 13 – за это отвечает слой “Layer 2: Protocol Implementation”). Чтобы изолировать тонкости текстового протокола от разработчиков, был реализован API системы, скрывающий как ее сокеты, так и передаваемые через них коды команд за набором процедур языка программирования (Layer 3: Toolbus API). Однако по мере развития в систему были добавлены вспомогательные механизмы (“Layer 4: Applications”). Именно эти механизмы и нарушали строгость слоистой структуры, поскольку они обращались к уровню работы сокетов в обход специфицированного API системы (связь от “Layer 4” к “Layer 3”). Рис. 13 Поскольку перед разработчиками стояла задача смены текстового протокола, используемого в системе для обмена по сокетам, подобный дефект грозил переделкой всех стандартных механизмов системы. Архитектурный рефакторинг позволил избежать столь неприятной ситуации и впоследствии значительно сократить время, затрачиваемое непосредственно на кодирование. 32
Анализ архитектуры и поглощающие слои. Примером, демонстрирующим пользу смягчения условий для поиска слоев с целью анализа архитектуры является ситуация, возникшая при анализе архитектуры Apache James 2. 2. Это мощный почтовый сервер масштаба предприятия, поддерживающий многие современные почтовые протоколы. После построения структурной модели сервера в ней были выделены основные подсистемы и предпринята попытка выделить нестрогие слои. При выделении слоев было разрешено поглощение СК. В результате была получена диаграмма 4 А). Эта диаграмма вполне осмыслена: на слое 0 оказались блок Constants. java, содержащий константы, используемые во всей системе, и блок Core, содержащий основные типы данных системы ( Message, Message. Header и пр. ). Таким образом, слой 0 содержит основные определения системы. Слой 1 содержит authorization – систему контроля доступа, mailrepository – репозиторий писем, mailstore – адаптер для mailrepository созданный для унификации работы с различными системами хранения и управления сообщениями. Слой 2 – фактически, основной, поскольку содержит собственно ядро почтового сервера. На слое 3 располагаются вспомогательные механизмы для работы с почтовым сервером – система удаленного администрирования remotemanager и система fetch – облегчающая получение писем с сервера. С другой стороны, попытка выделения нестрогих слоев, но без поглощения СК дала гораздо более слабый результат. Выделенные слои представлены на рис 14 Б). Из-за того, что в системе существует сильносвязанный компонент, в который входят блоки mailrepository и mailstore, на "чердак" попало большинство слоев системы, а дать слоям какое -либо внятное определение оказалось невозможно. Рис. 14 33
8. Архитектуры программных систем. Надежность и производительность 8. 1. Основные принципы разработки архитектуры операционных систем 1. Концепция многоуровневой иерархической вычислительной системы (виртуальной машины) с ОС многослойной структуры. 2. Разделение модулей ОС по функциям на две группы: ядро – модули, выполняющие основные функции ОС, и модули, выполняющие остальные (вспомогательные) функции. 3. Разделение модулей ОС по размещению в памяти вычислительной системы: резидентные, постоянно находящиеся в оперативной памяти, и транзитные, загружаемые в оперативную память только на время выполнения своих функций. 4. Реализация двух режимов работы вычислительной системы: привилегированного режима ( kernel mode) или режима супервизора (supervisor) и пользовательского режима (user mode) или режима задача (task mode). 5. Ограничение функций ядра (а, следовательно и числа его модулей) до минимально необходимых функций. 6. Модульное строение (однократно используемые – при загрузке ОС) и повторно используемые (привилегированные – не допускают прерываний, реентерабельные – допускают прерывания и повторный запуск, повторновходимые – допускают прерывания после завершения секций). 7. Параметрическая универсальность. Возможность генерации ОС и создания нескольких рабочих конфигураций. 8. Функциональная избыточность. 9. Функциональная избирательность. 10. Открытость, модифицируемость, расширяемость (возможность получения текстов исходных модулей). 11. Мобильность – возможность переноса на различные аппаратные платформы. 12. Совместимость – возможность выполнения приложений, рассчитанных на другие ОС. 13. Безопасность и надежность – защита от несанкционированного доступа, защита легальных пользователей друг от друга, аудит, возможность восстановления ОС после сбоев и отказов. 34
8. 2. Классическая структура операционной системы Утилиты, системные программы Интерфейс системных Менеджеры ресурсов Базовые механизмы Машинно-зависимые Средства апп. Аппаратура поддержки ОС модули ядра ОС ядра Файловая сис. , вирт. память и др. вызовов API Приложения пользователей Рис. 15 35
8. 3. Архитектура Windows Рис. 16 36
Менеджер процессов Базовые механизмы ядра Драйвер устройств Файловая система Сервер безопасности API Приложения пользователей Менеджер виртуальной памяти Утилиты. Системные программы РЕЖИМ ЯДРА Интерфейс системы ввода-вывода lы ы ы Утилиты ОС, приложения Пользовательский режим 8. 4. Макро или микро ядро? МИКРОЯДРО (режим ядра) Машинно-зависимые модули Средства аппаратной поддержки ОС Аппаратура Рис. 17 37
Клиент-серверная архитектура Сервер памяти Приложение Файл- Принт- сервер Запрос Сервер процессов Ответ Запрос Ответ РЕЖИМ ПОЛЬЗОВАТЕЛЯ РЕЖИМ ЯДРА МИКРОЯДРО А П П А Р А Т У Р А Рис. 18 38
Системный вызов Пользовательский режим Работа приложения Привилегированный режим Работа t ядра Время переключения режимов t Макроядро Микроядро Системный вызов СЕРВЕР ОС Приложение Пользовательский режим МИКРОЯДРО Привилегированны й режим t МИКРОЯДРО t t Рис. 19 t 39
Сравнительные характеристики архитектур Наименование характеристики Макроядро Микроядро Производительность высокая низкая Надежность средняя и низкая высокая Расширяемость сложная простая, эффективная Переносимость достаточно сложно высокая Гибкость хорошая высокая Межмодульные интерфейсы уникальные по слоям унифицированные, стандартные Поддержка распределенных систем средняя высокая Поддержка объектно-ориентированных ОС высокая 40
9. Архитектура надежных операционных систем 9. 1. Причины ненадежности операционных систем: 1. Очень большой объем программного кода. В ядре ОС Linux содержится более 2, 5 млн. строк кода, в ядре Windows XP - 6 млн. В ПМ имеется от 6 до 16 ошибок на 1000 строк исполняемого кода; по другим данным от 2 до 75 ошибок на 1000 строк исполняемого кода в зависимости от размера модуля. 2. Наличие некоторых из этих ошибок позволяет злоумышленникам применять вирусы и черви для заражения и повреждения системы. 3. Привнесение в операционную систему чужого кода (в основном драйверов – 85% всех аварийных отказов Windows XP обусловлен ошибками в коде драйверов). Драйверы устройств пишутся изготовителями периферийных устройств, и контроль качества их продукции ниже, чем у поставщиков операционных систем. Если драйвер относится к open-source, контроль качества обеспечивается на более низком уровне. В Linux частота появления ошибок в драйверах устройств от 3 до 7 раз выше, чем в других частях ядра. 4. Операционные системы состоят из многочисленных модулей, скомпонованных в одном адресном пространстве и образуют единую бинарную программу, которая выполняется в режиме ядра. Ошибка в любом модуле может легко привести к разрушению структур данных в каком-либо другом, не связанным с ним модуле и к мгновенному выходу системы из строя. Причина такого подхода – желание получить высокую производительность. В стандартной монолитной системе ядро содержит все операционную систему, скомпонованную в едином адресном пространстве и выполняемую в режиме ядра. Ядро может быть структурировано на компоненты, или модули, показанные на рис. 20 в виде прямоугольников с пунктирными сторонами, но между компонентами отсутствуют защитные границы. В отличие от этого, прямоугольники со сплошными сторонами соответствуют отдельным процессам, выполняемым в режиме пользователя; каждый из этих процессов выполняется в отдельном адресном пространстве, защищаемом аппаратурой MMU (Memory Management Unit, устройство управления памятью). С монолитными операционными системами связан ряд проблем, свойственных их архитектуре: 1. Отсутствует должная изоляция сбоев. 2. Весь код выполняется на наивысшем уровне привилегированности. 3. Огромный размер кода предполагает наличие многочисленных ошибок. 4. В ядре присутствует ненадежный сторонний код. 5. Сложность систем затрудняет их сопровождение. 41
Рис. 20 Этот список свойств ставит под сомнение надежность монолитных систем. Эти свойства возникают не вследствие плохой реализации, а представляют собой фундаментальные проблемы, связанные с архитектурой ОС. Предполагается корректность ядра, в то время, как лишь его размер означает, что оно должно содержать многочисленные ошибки. Более того, для всех ОС, в которых код выполняется на наивысшем уровне привилегированности, и не обеспечивается должное сдерживание распространения сбоев, любая ошибка может стать фатальной. Например, неправильно работающий драйвер устройства, предоставленный сторонним разработчиком, может легко разрушить ключевые структуры данных и вывести из строя всю систему. Реальность подобной угрозы следует из того наблюдения, что аварийные отказы большинства операционных систем случаются по вине драйверов устройств. Дополнительной проблемой является то, что огромный размер монолитных ядер делает их очень сложными и трудно понимаемыми. Без общего понимания ядра даже опытный программист может легко внести ошибки за счет недостаточной осведомленности о побочных эффектах своих действий. 9. 2. Методы повышения надежности ОС (решение проблем унаследованных ОС) 1. Консервативный подход – проект Nooks. Разработан для увеличения надежности существующих ОС, таких как Windows и Linux. Технология Nooks поддерживает монолитную структуру ядра. Цели проекта Nooks заключаются в следующем: • защитить ядра от ошибок в драйверах; • обеспечить автоматическое восстановление в случае сбоя в драйвере; • сделать все это путем минимальных изменений в существующих драйверах и ядре. Основное средство, позволяющее уберечь структуры данных ядра от уничтожения некорректными драйверами – карта страниц виртуальной памяти. При работе драйвера все внешние для него страницы переводятся в режим «открыты только для чтения» , благодаря чему драйвер может читать структуры данных ядра, которые ему необходимы, но любая попытка напрямую изменить структуры данных ядра вызовет исключение центрального процессора, которое перехватывает менеджер изоляции Nooks. Доступ к частной памяти драйвера, где хранятся стеки, куча, структуры частных данных и копии объектов ядра, разрешен на чтение и на запись. 42
Рис. 21 Оболочка вокруг каждого драйвера тщательно отслеживает все взаимодействия между драйвером и ядром. Nooks предоставляет оболочки как для экспортируемых, так и для импортируемых функций. Теперь, когда ядро вызывает функцию драйвера или драйвер вызывает функцию ядра, вызов на самом деле направляется оболочке, которая проверяет корректность параметров и управляет вызовом. Несмотря на то, что суррогаты (stubs) оболочки (на рис. 21 они изображаются как линии, указывающие внутрь и наружу драйвера) генерируются автоматически на основе прототипов функций, тело оболочки разработчикам приходится писать вручную. В целом группа Nooks написала 455 оболочек: 329 для функций, которые экспортирует ядро, и 126 — для функций, которые экспортируют драйверы устройств. Согласно экспериментам, Nooks может обнаруживать 99% фатальных ошибок драйвера и 55% не фатальных, он далеко не совершенен. Например, драйверы могут выполнять привилегированные команды, которые они выполнять не должны; могут записывать данные в некорректные порты ввода/вывода и выполнять бесконечные циклы. Более того, оболочки Nooks могут содержать ошибки. Наконец, при данном подходе невозможно запретить драйверам записывать данные в любое место памяти. Тем не менее это потенциально весьма полезный шаг к повышению надежности унаследованных ядер. 2. Паравиртуализация – проект университета Карлсруэ. Идея виртуальных машин применяется для обеспечения защиты внутри одной ОС. Поскольку процессор Pentium не является полностью виртуализируемым, было частично снято ограничение выполнения на виртуальной машине неизменяемой ОС. Допускаются изменения, гарантирующие, что ОС не делает ничего, что не может быть виртуализировано. Для отличия от истинной виртуализации этот подход был назван паравиртуализацией. Идея состоит в том, чтобы на одной из виртуальных выполнялись прикладные программы, а на других виртуальных машинах работали драйверы устройств. При крахе драйвера из строя выходит только его виртуальная машина, а основная машина сохраняет работоспособность (рис. 21). 43
Рис. 22 Поскольку драйверы устройств работают в режиме пользователя, основной вопрос заключается в том, как они будут выполнять ввод/вывод и обрабатывать прерывания. Физический ввод/вывод поддерживается за счет добавления примерно 3 тыс. строк кода к ядру Linux, благодаря чему драйверы могли использовать сервисы L 4 для ввода/вывода вместо того, чтобы делать это самостоятельно. Дополнительные 5 тыс. строк кода поддерживают взаимодействия между тремя изолированными драйверами (диска, сети и шины PCI) и виртуальной машиной, в которой выполняются прикладные программы Этот подход должен обеспечивать более высокую надежность, чем у отдельной ОС, поскольку при отказе виртуальной машины, содержащей один или несколько драйверов, она может быть перезапущена, а драйверы могут быть возвращены в исходное состояние. Здесь не предпринимаются попытки вернуть драйверы в состояние, предшествующее отказу, как это делается в Nooks. Измерения производительности показывают, что накладные расходы на использование паравиртуальных машин в такой манере составляют около 3 -8%. 9. 3. Решения на основе изоляции сбоев и микроядра (проект MINIX 3) ОС обычно пишутся на C или C++. В программах, написанных на этих языках, интенсивно используются указатели – обильный источник ошибок. Отсюда подход, основанный на идеях модульности и изоляции сбоев. Путем разбиения системы на большое число изолированных модулей, каждый из которых выполняется в отдельном процессе в режиме пользователя, удается сократить часть системы, выполняемую в режиме ядра, до абсолютного минимума и предотвратить распространение сбоев, возникающих в других модулях. Уменьшение размеров ядра значительно сокращает число ошибок. Малый размер также позволяет понизить уровень сложности ядра и облегчить его понимание, что также способствует надежности. 44
Минимальное ядро включает обработчики прерываний, механизм для запуска и остановки процессов (путем загрузки регистров MMU и ЦП), планировщик и модуль поддержки межпроцессных коммуникаций; в идеальном случае больше в ядро не входит ничего. Поддержка функциональных возможностей стандартной ОС, представленных в монолитном ядре, перемещается в пользовательское адресное пространство, и соответствующий код больше не выполняется на наиболее привилегированном уровне. Поверх минимального ядра возможны различные организации операционной системы. Рис. 23 Лучшим решением является выполнение каждого ненадежного модуля в пользовательском режиме в отдельном процессе, изолированном от других процессов (рис. 23). Все функциональные компоненты операционной системы, такие как драйверы устройств, файловая система, сервер сети и высокоуровневое управление памятью, выполняются как отдельные процессы в режиме пользователя в собственном адресном пространстве. Эту модель можно определить, как мультисерверную операционную систему. С логической точки зрения пользовательские процессы можно разбить на три уровня. Самый низкий уровень процессов, выполняемых в пользовательском режиме, занимают драйверы. Выше уровня драйверов находятся серверные процессы. В их число входят файловый сервер, сервер процессов, сетевой сервер, информационный сервер, сервер реинкарнации и другие. Над уровнем серверов выполняются обычные пользовательские процессы, включая различные интерпретаторы shell, компиляторы, утилиты и прикладные программы. Каждый сервер или драйвер выполняется в виде отдельного пользовательского процесса с собственным адресным пространством и могут общаться только с использованием механизма IPC, обеспечиваемого ядром. Это предотвращает распространение сбоев одного сервера или драйвера на другие подобно тому, как ошибка при компиляции программы, возникающая в одном процессе, не влияет на то, что делает браузер в другом процессе. При работе в пользовательском режиме возможности процессов ограничены. Поэтому для поддержки выполнения требуемых от них задач серверами и драйверами ядро экспортирует ряд системных вызовов, которые могут производиться авторизованными процессами. Например, драйверы не имеют привилегий на непосредственное выполнение ввода-вывода, но могут запросить у ядра выполнения соответствующих действий от своего имени. Серверы и драйверы могут запрашивать сервисы друг у друга путем обмена сообщениями фиксированного размера. Этот обмен реализуется путем обращений к ядру, которое до выполнения запрашиваемого действия проверяет, авторизован ли соответствующим образом вызывающий процесс. 45
4. Принципы проектирования и разработки MINIX 3 1. Простота. 2. Модульность. 3. Наименьшая авторизация. 4. Отказоустойчивость 1. Система настолько проста, что ее легко понять и с большей вероятностью поддерживать ее в корректном состоянии. Это относится как к высокоуровневому проектированию, так и к реализации. Разработка позволяет структурно избежать известных проблем, таких как исчерпание ресурсов. Например, в ядре статически объявляются все структуры данных вместо того, чтобы динамически выделять память при необходимости. Хотя это может привести к недоиспользованию некоторой памяти, этот подход является очень простым и никогда не приводит к ошибкам. Не реализовали нити. Может быть, это некоторая потеря эффективности (а может быть, и нет), но зато устраняются «состояния гонок» и синхронизация, что существенно облегчает жизнь программистам. 2. Система разделена на набор небольших независимых модулей. Такое разделение ОС на модули позволяет установить «брандмауэры» , сквозь которые не могут распространяться ошибки, что приводит к более надежной системе. Для предотвращения косвенного влияния сбоев в одном модуле на какой-либо другой модуль структурным образом уменьшена их взаимозависимость. В тех случаях, когда это невозможно из-за природы модулей, применены дополнительные средства поддержки безопасности. Например, файловая система зависит от драйверов устройств, но она разрабатывается таким образом, чтобы быть готовой к обработке сбоев драйвера. 3. Обеспечено соблюдение принципа наименьшей авторизации. Хотя изоляция сбоев помогает сдерживать их распространение, сбой в полномочном модуле может вызвать значительный ущерб. Поэтому понижен уровень привилегий пользовательских процессов до предельно допустимого минимума. В ядре поддерживаются битовые массивы и списки, определяющие возможности процессов. В частности, имеются шкала допустимых вызовов ядра и список допустимых адресов назначения сообщений. Информация об авторизации инициируется во время загрузки системы, главным образом, на основе конфигурационных таблиц, создаваемых системным администратором. 4. При разработке системы предусмотрена устойчивость к некоторым сбоям. Все серверы и драйверы управляются и отслеживаются специальным сервером, называемым сервером реинкарнации, который может справляться с двумя видами проблем. Если системный процесс завершается непредвиденным образом, это распознается, и процесс перезапускается. Периодически проверяется состояние каждого системного процесса для проверки его правильного функционирования. Если процесс функционирует неправильно, он принудительно завершается и перезапускается, но система все время продолжает работать. 46
В результате использования этих принципов проектирования надежность системы повышается, так как: 1. Уменьшается число критических сбоев. 2. Сокращается объем ущерба, который может быть причинен любой ошибкой. 3. Имеется возможность восстановления после распространенных сбоев. Эти факторы обусловлены следующими решениями проекта: 1. Сокращение числа ошибок в ядре. 3800 строк кода (менее 100 страниц листинга, включая заголовки и комментарии) – это достаточно мало, чтобы весь этот код мог понять один человек; это существенно повышает шансы на то, что со временем все ошибки удастся найти. 2. Снижение потенциального влияния ошибок. Уменьшение размера ядра не приводит к сокращению объема всего кода системы. При этом всего лишь большая часть системы начинает работать в пользовательском режиме. Это изменение оказывает глубокое влияние на надежность. У кода ядра имеется возможность полного доступа ко всему, что может делать машина. Перевод большей части ОС в пользовательский режим не приводит к сокращению общего числа имеющихся ошибок. Однако, эффект проявления ошибки при выполнении программы в пользовательском режиме является менее разрушительным, чем тот, который проявляется при выполнении программы в режиме ядра. 3. Серверы и драйверы запускаются и контролируются системным процессом, называемым сервером реинкарнации. Если процесс непредвиденным или аварийным образом завершается, это распознается, поскольку сервер процессов оповещает сервер реинкарнации о завершении сервера или драйвера, и процесс автоматически перезапускается. Кроме того, если при периодическом опросе какой-либо из этих процессов не отвечает правильным образом в течение установленного интервала времени, то сервер реинкарнации насильственно завершает и перезапускает плохо ведущие себя серверы и драйверы. В монолитных системах обычно отсутствует возможность обнаружения сбойных драйверов «на лету» . Замена на лету драйвера ядра является сложным делом, поскольку ко времени замены он может удерживать ядерные блокировки или находиться в критическом участке. 4. Ограничение злоупотреблений переполнениями буферов. Переполнения буферов являются обильным источником ошибок, наличием которых интенсивно пользуются вирусы и черви. Поскольку ядро является минимальным, и в нем используется только статическое размещение данных, возникновение проблемы в наиболее чувствительной части системы маловероятно. Если переполнение буфера случается в одном из пользовательских процессов, то проблема не является слишком серьезной, поскольку серверы и драйверы, выполняемые в пользовательском режиме, обладают ограниченными возможностями. 47
5. Обеспечение надежного IPC. В MINIX 3 в механизме синхронной передачи сообщений используются рандеву, в результате чего устраняется потребность в буферизации и управлении буферами, а также отсутствует проблема исчерпания ресурсов. Если получатель не ожидает сообщения, то примитив SEND блокирует отправителя. Аналогично, примитив RECEIVE блокирует процесс, если отсутствует сообщение, ожидающее своего получения. Это означает, что для заданного процесса в таблице процессов в любое время должен храниться единственный указатель на буфер сообщения. В дополнение к этому, имеется механизм асинхронной передачи сообщений NOTIFY, который также не является чувствительным к исчерпанию ресурсов. 6. Ограничение IPC – это мощный механизмом, который нуждается в строгом контроле. Поскольку механизм передачи сообщений является синхронным, процесс, выполняющий примитив IPC, блокируется, пока оба участника не станут готовыми. Пользовательский процесс может легко злоупотребить этим свойством для завешивания системных процессов путем посылки запроса без ожидания ответа. Поэтому имеется другой примитив IPC SENDREC, комбинирующий в одном вызове SEND и RECEIVE. Он блокирует отправителя до получения ответа на запрос. С целью защиты операционной системы этот примитив является единственным, который можно использовать обычным пользователям. В в ядре для каждого процесса поддерживается битовый массив для ограничения примитивов IPC, которые позволяется использовать данному процессу. 7. Избегание тупиков. Поскольку по умолчанию для IPC используются синхронные вызовы SEND и RECEIVE, могут возникать тупики, когда два или большее число процессов одновременно пытаются обмениваться сообщениями, и все процессы блокируются в ожидании один другого. Поэтому разработан протокол избегания тупиков, предписывающий частичное, нисходящее упорядочение сообщений. В ядре реализовано распознавание тупиков. Если вызов примитива в некотором процессе непредусмотренным образом привел бы к возникновению тупика, то выполнение примитива не производится, и вызывающей стороне возвращается сообщение об ошибке. 8. Унификация прерываний и сообщений. Базовым механизмом IPC является передача сообщений на основе рандеву, но требуются и асинхронные сообщения, например, для предоставления информации о прерываниях. Обычно, когда некоторый процесс посылает сообщение другому процессу и получатель не является готовым, отправитель блокируется. Эта схема не работает для прерываний, поскольку обработчик прерываний не может позволить себе блокировку. Вместо этого используется асинхронный механизм оповещений, при использовании которого обработчик прерываний производит вызов NOTIFY для драйвера. Если драйвер ожидает сообщение, то оповещение доставляется напрямую. Если он его не ожидает, то оповещение сохраняется в битовом массиве до тех пор, пока впоследствии драйвер не выполнит вызов RECEIVE.
9. Ограничение функциональных возможностей драйвера. Ядро экспортирует ограниченный набор функций, которые можно вызывать извне. Этот ядерный API представляет собой единственный способ взаимодействия драйвера с ядром. Драйверу не разрешается использовать любой вызов ядра. Для каждого драйвера в ядре (в таблице процессов) поддерживается битовый массив, показывающий, какие вызовы ядра может производить этот драйвер. Гранулярность вызовов ядра является достаточно мелкой. Отсутствует мультиплексирование вызовов в один и тот же номер функции. Каждый вызов индивидуально защищается собственным битом в битовом массиве. Однако на внутреннем уровне несколько вызовов может обрабатываться одной и той же ядерной функцией. Этот метод позволяет реализовать детальное управление доступом к ядру. 10. Запрещение доступа к портам ввода-вывода. Для каждого драйвера в ядре поддерживается список портов ввода-вывода, из которых он может читать, а также тех, в которые он может писать. Чтение и запись защищаются по отдельности. В отличие от этого, в монолитных системах отсутствует способ ограничения доступа внутриядерного драйвера только к небольшому числу портов ввода-вывода. Ядерный драйвер может случайно произвести запись в любой порт ввода-вывода и нанести существенный ущерб. 11. Проверка параметров. Поскольку все вызовы ядра производятся путем генерации внутреннего прерывания, ядро может выполнить ограниченную валидацию параметров до диспетчеризации вызова. Эта валидация включает проверки как исправности (sanity), так и прав доступа (permission). Например, если драйвер просит ядро записать блок данных с использованием физической адресации, то этот вызов может быть отвергнут, поскольку не у всех драйверов имеется право на такие действия. Хотя такие проверки исправности являются грубыми, это лучше, чем ничего. В монолитных системах ничто не препятствует драйверу выполнять запись по адресам, по которым нельзя писать не при каких условиях, таким как адреса в сегменте текста ядра. 12. Отлавливание плохих указателей. В программах на языках C и C++ используется множество указателей, и эти программы все время подвержены ошибкам, связанным с использованием плохих указателей. Разыменование неверного указателя часто приводит к выявлению аппаратурой ошибки сегментации. В MINIX 3 сервер или драйвер, пытающиеся разыменовать плохой указатель, принудительно завершаются, и выдается дамп памяти для будущей отладки, точно так же, как и для других пользовательских процессов. Если плохой указатель обнаруживается в части операционной системы, выполняемой в пользовательском режиме, то сервер реинкарнации немедленно замечает наличие сбойной ситуации и заменяет принудительно завершенный процесс его свежей копией. 13. Укрощение бесконечных циклов. Когда драйвер впадает в бесконечный цикл, создается угроза потребления бесконечного времени ЦП. Планировщик замечает такое поведение и постепенно понижает приоритет неисправного процесса, пока он не становится неработающим. Другие процессы могут продолжать нормально работать. После исчерпания некоторого интервала времени сервер реинкарнации заметит, что данный драйвер не отвечает на запросы, принудительно завершит и перезапустит его. В отличие от этого, когда в бесконечный цикл впадает ядерный драйвер, он потребляет все время ЦП и фактически завешивает всю систему.
14. Проверка DMA. В MINIX 3 не обеспечивается предотвращение ущерба системе по причине неверного DMA (прямой доступ к памяти). Для предотвращения перезаписи драйвером через DMA произвольной части реальной памяти требуется аппаратная защита. Однако можно обнаружить некоторые ошибки DMA следующим образом. DMA обычно запускается путем записи адреса DMA в некоторый порт ввода-вывода. Можно разработать библиотечную процедуру, которая вызывается для записи в некоторый порт ввода-вывода с предварительным декодированием в этот порт ввода-вывода с целью нахождения используемых адресов DMA и проверки их допустимости. В зависимости от аппаратуры можно поступить еще лучше. Если бы на периферийной шине имелось MMU (устройство управления памятью) можно было бы точно ограничить доступ к памяти для каждого драйвера. Для систем с шиной PCI-X предполагается разработать сервер шины PCI, ответственный за инициализацию таблиц MMU вводавывода. 9. 4. Результаты проекта MINIX 3 1. Анализ надежности. Для проверки надежности системы вручную были внесены некоторые тщательно подобранные ошибки в некоторые из своих серверов и драйверов, чтобы посмотреть, что в результате произойдет. MINIX 3 разрабатывалась для обнаружения и исправления многих ошибок, и именно это наблюдали ее разработчики. Если по какой бы то ни было причине происходил сбой некоторого компонента, это распознавалось сервером реинкарнации, который применял все требуемые средства для оживления сбойного компонента. Для понимания работы системы нужно различать два класса ошибок. Первый класс составляют логические ошибки, означающие, что сервер или драйвер придерживается протокола межмодульных взаимодействий и нормально отвечает на запросы, как если бы он успешно выполнил работу, чего в действительности не происходит. Для любой системы очень трудно, если не невозможно, отлавливать ошибки такого рода. Логические ошибки находятся за пределами этого исследования. Второй класс состоит из протокольных ошибок, при наличии которых нарушаются правила, определяющие поведение серверов и драйверов. Если серверы и драйверы не подчиняются этому правилу, предпринимается корректирующее действие. MINIX 3 разрабатывалась для борьбы с протокольными ошибками. Сервер реинкарнации – это центральный сервер, управляющий всеми серверами и драйверами операционной системы. Сервер помогает отлавливать два распространенных вида сбоев: умершие или плохо себя ведущие системные процессы и незамедлительно принимается за решение наиболее острой проблемы. Он позволяет существенно повысить надежность, обеспечивая: 1. Немедленное распознавание фатальных сбоев. 2. Периодический мониторинг состояния. 50
Рис. 24. Результаты тестирования серьезных сбоев в драйверах устройств. Если сервер реинкарнации распознает проблему, он автоматически предпринимает корректирующие действия 2. Производительность. Тестовой системой был 2. 2 GHz Athlon (более точно, AMD 64 3200+) с 1 Гб основной памяти и 40 гигабайтным диском IDE. Ни один из драйверов не был оптимизирован для работы в пользовательском режиме. На Pentium разработчики думают обеспечить защищенным образом прямой доступ драйверов устройств к требуемым им портам ввода-вывода, устраняя, таким образом, многие вызовы ядра. Однако для поддержания переносимости интерфейс не будет изменяться. Кроме того, в настоящее время в драйверах используется программируемый ввод-вывод, что гораздо медленнее использования DMA. После реализации этих оптимизаций ожидается существенное повышение эффективности. Тем не менее, даже при использовании существующей системы ухудшение производительности оказалось вполне разумным. Результаты тестирования системных вызовов. Первый пакет тестов содержал тесты чистых POSIXсовместимых системных вызовов. Пользовательская программа должна была зафиксировать реальное время, затем миллионы раз произвести системный вызов, после чего снова зафиксировать реальное время. Время обработки системного вызова вычислялось как разность между конечным и начальным временем, деленная на число вызовов, за вычетом накладных расходов на организацию цикла, которые измерялись отдельно. Число итераций цикла было разным для каждого теста, поскольку тестирование 100 миллионов раз вызова getpid было разумным, но чтение 100 миллионов раз из 64 -магабайтного файла заняло бы слишком много времени. Все тесты выполнялись на незагруженной системе. Для этих тестов частоты успешных обращений к кэшу ЦП и кэшу файлового сервера предположительно составляли 100%. 51
Рис. 25. Время системных вызовов для драйверов, выполняемых в режиме ядра, и драйверов, выполняемых в пользовательском режиме. Все значения времени представлены в микросекундах . Результаты тестирования дискового ввода-вывода. Чтение из файла и запись в файл порции от 1 килобайта до 64 мегабайт. Тесты пропускались много раз, так что читаемый файл размещался в 12 -мегабайтном кэше файлового сервера, кроме случая 64 -мегабайтных обменов, когда объема кэша не хватало. Рис. 26. Время чтения и записи порций большого файла. Значения времени приводятся в микросекундах, кроме 64 -мегабайтных операций, для которых время указывается в секундах. Разница в производительности составляет от 3% до 18%, в среднем – 8. 4%. Однако заметим, что худший показатель производительности получен для 1 -килобайтных записей, но абсолютное время возросло всего на 457 наносекунд. Это соотношение уменьшается при увеличении объема ввода-вывода, поскольку сокращаются относительные накладные расходы. В 64 -магабайтных тестах, это соотношение составляет всего от 3% до 5%. 52
Результаты тестирования приложений. Время выполнения в секундах различных тестовых программ. Первые два теста выполнялись в цикле, а остальные пропускались только по одному разу для исключения влияния со стороны кэша файловой системы. Размер кода. Скорость – это не единственный показатель, представляющий интерес; очень важным является и число ошибок. Разумным заменителем числа ошибок, вероятно, является число строк кода. Подсчет числа строк выполнялся с использованием Perl-скрипта sclc. pl, доступного в Internet. Результаты для ядра, четырех серверов (файловой системы, сервера процессов, сервера реинкарнации, информационного сервера), пяти драйверов (жесткого диска, флоппи-диска, RAM-диска, терминала, устройства журнализации) и программы init показаны на рис. 27. Рис. 27. можно видеть, что ядро состоит из 2947 строк на языке C и 778 строк на языке ассемблера (для программирования низкоуровневых функциональных возможностей, таких как перехват прерываний и сохранение регистров ЦП при переключении процессов). Всего имеется 3725 строк кода. И только этот код исполняется в режиме ядра. Интересно, что статистика размеров кода, показанная на рис. 27, представляет минимальую, но функционирующую операционную систему. Общий размер ядерной части и части, работающей в режиме пользователя, состав-ляет всего 18, 000 строк кода, необыкновенно мало для POSIX-совместимой операционной системы. 53
10. Исследование надежности и производительности ОС различной архитектуры 10. 1. Изоляция драйверов в программном обеспечении. Одним из важных исследовательских проектов, в котором предпринимается попытка построить надежную систему в присутствии ненадежных драйверов устройств, является Nooks. Целью Nooks является повышение надежности существующих операционных систем. 10. 2. Изоляция драйверов с использованием виртуальных машин. В проекте по инкапсуляции драйверов это делается с использованием понятия виртуальной машины для их изоляции от других частей системы. Когда драйвер вызывается, он запускается на другой виртуальной машине, не в той, в которой работает основная система, так что его сбой не портит основную систему. Подобно Nooks, этот подход полностью фокусируется на выполнении унаследованных драйверов для унаследованных операционных систем. 10. 3. Изоляция драйверов с использованием виртуальных машин на основе экзоядра. Классические виртуальные машины представляют собой мощное средство для одновременного выполнения нескольких операционных систем. Экзоядра похожи на виртуальные машины, но в них ресурсы разделяются, а не реплицируются, что приводит к большей эффективности. 10. 4. Драйверы, выполняющиеся в пользовательском режиме в монолитном ядре. Ранним проектом, в котором применялись драйверы, выполняющиеся в пользовательском режиме, был Mach 3. 0. Система состояла из микроядра Mach, поверх которого запускалась ОС Berkeley UNIX в виде пользовательского процесса, и драйверы устройств также выполнялись в пользовательских процессах. К сожалению, в случае фатального сбоя драйвера Berkeley UNIX приходилось перезапускать, так что от изоляции драйверов было мало пользы. 10. 5. Разработки минимальных ядер. Хотя извлечение драйверов из ядра является большим шагом вперед, еще лучше извлечь из ядра операционную систему. Именно здесь начинают применяться минимальные ядра с чрезвычайным сокращением числа реализуемых в них абстракций. Вероятно, первым минимальным ядром была система RC 4000 Бринка Хансена, датируемая началом 1970 -х гг. С середины 1980 -х гг. был написан ряд минимальных ядер, включая Amoeba , Chorus, Mach и V. Однако ни в одном из них не применялось безопасное программное обеспечение: у всех имелись не изолированные драйверы внутри ядра. QNX является коммерческой UNIX-подобной системой реального времени с закрытыми кодами. Хотя у нее имеется минимальное ядро, называемое Neutrino, по поводу системы опубликовано мало статей, и точные детали мало известны. 54
10. 6. Односерверные операционные системы. Одним из способов использования минимальных ядер является обеспечение платформы, поверх которой, как единственный сервер, запускается вся операционная система, возможно, в режиме пользователя. Для получения системных сервисов пользовательские программы запрашивают их у процесса операционной системы. Ошибка в драйвере по-прежнему может сломать всю операционную систему, а в результате и прикладные программы. Единственным реальным преимуществом является то, что перезагрузка после фатального сбоя сервера операционной системы, выполняемого в режиме пользователя, и всех приложений происходит быстрее, чем перезагрузка компьютера. 10. 7. Мультисерверные операционные системы. Более сложный подход состоит в расщеплении операционной системы на части и выполнении каждой части в собственной области защиты. Одним из таких проектов был Saw. Mill Linux. В 2001 г. проект был неожиданно остановлен после того, как многие из его основных участников ушли из IBM. Другим мультисерверным проектом является DROPS, в котором ОС также строится поверх минимального ядра L 4/Fiasco. Этот проект ориентирована на мультимедийные приложения. Однако большинство драйверов устройств выполняется в составе большого серверного процесса L 4 -Linux, и только мультимедийные подсистемы выполняются отдельно. После некоторой настройки проигрыш в производительности снизился до 2 -4%. 55


