Mach (микроядро)

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 16:18, 9 июня 2017.
Mach
Создатели: Университет Карнеги — Меллон (CMU)
Выпущена: 1985; 36 years ago (1985)
Постоянный выпуск: 3.0 / 1994; 27 years ago (1994)
Состояние разработки: Discontinued
Написана на: C (язык программирования) и Ассемблер
Тип ПО: Микроядро
Веб-сайт The Mach Project

Mach[Источник 1] — микроядро операционной системы, разработанное в Carnegie Mellon University при проведении исследовательских работ в области операционных систем, главным образом, распределённых и параллельных вычислений. Это один из самых первых примеров микроядра, но до сих пор он является стандартом для других подобных проектов.

Проект существовал с 1985 по 1994 годы и был закончен выпуском Mach 3.0. Несколько исследовательских групп продолжили разработку Mach; например, Университет Юта некоторое время вёл проект Mach 4. Mach разрабатывался как замена ядру BSD UNIX, поэтому не было необходимости в разработке нового операционного окружения. Дальнейшие научно-исследовательские работы по проекту Mach, похоже, закончены; несмотря на это, Mach и его производные используются в ряде коммерческих операционных систем, например, NeXTSTEP, наиболее заметной из которых является Mac OS X, в которой используется ядро XNU, вобравшее в себя Mach 2.5. Система управления виртуальной памятью Mach была перенята разработчиками BSD в CSRG и используется в современных UNIX-системах, производных от BSD, например, FreeBSD. Ни Mac OS X, ни FreeBSD не сохранили микроядерную архитектуру, используемую в Mach, хотя Mac OS X предлагает для использования в приложениях микроядерную систему межпроцессного взаимодействия и примитивы управления.

Mach является логическим продолжением ядра Accent, также разработанного в Carnegie Mellon University. Ведущий разработчик проекта Ричард Рашид с 1991 года работает в Microsoft в подразделении Microsoft Research. Ещё один из основных разработчиков, Аветис Теванян, работал главой отдела разработки программ в NeXT, затем, до марта 2006, главой отдела перспективных программных технологий в Apple.

Концепция

Так как Mach был спроектирован как быстрая замена традиционному ядру Unix, мы рассмотрим в основном отличия Mach от Unix. Стало понятным, что Unix-концепция «всё — файл» больше не работает на современных системах, но такие системы, как Plan 9 от Bell Labs, всё же пытаются следовать по этому пути. Разработчики Mach заметили негибкость такого подхода, и предположили, что другой уровень виртуализации может заставить систему «работать» снова.

Одна из важнейших абстракций в Unix — это конвейеры (pipe). Что похоже на конвейеры и позволит на более общем уровне сделать доступным различные перемещения информации между программами? Такая система может существовать, используя межпроцессное взаимодействие (IPC) — похожий на конвейеры принцип организации взаимодействия процессов, позволяющий перемещать любую файлоподобную информацию между двумя программами. В то время как во многих системах, включая различные Unix, уже несколько лет существовали различные реализации IPC, но они были предназначены для специальных целей и не могли обеспечить то, что создатели Mach от них ждали.

В Carnegie Mellon University начали разработку ядра Accent, используя IPC, базирующийся на разделяемой памяти. Accent был экспериментальной системой со множеством возможностей, разрабатываемых по веянию моды за всё время разработки. Дополнительно, полезность Accent для исследования была ограничена, потому что он не был Unix-совместимым, а Unix был уже стандартом де-факто во всех исследовательских ОС. Кроме того, Accent был жёстко привязан к платформе, на которой разрабатывался. Но в начале 1980-х казалось, что вскоре появится множество новых платформ, многие из которых будут поддерживать массовый параллелизм.

В начале разработка Mach рассматривалась, в основном, как попытка создать концептуально «чистый», основанный на Unix, легко портируемый Accent. Были сформулированы следующие основные концепции:

  • «задача» — набор ресурсов, которые позволяют «потокам» исполняться
  • «поток» — единица, исполняющаяся на процессоре
  • «порт» определяет защищённый конвейер для IPC между задачами
  • «сообщение» перемещается между программами через порт
Абстрактная модель эмуляции UNIX на основе Mach

Mach разработан на концепциях IPC Accent’а, но сделан системой, больше похожей на Unix, которая позволяет запускать Unix-программы с минимальными изменениями или вовсе без них. Для достижения этой цели в Mach появилась концепция «порта», представляющего конец в двухстороннем IPC. Порты были защищены и имели права, подобные правам доступа на файлы в Unix, а также использовали очень похожую на Unix модель защиты. Дополнительно Mach разрешал любой программе владеть привилегиями, которыми обычно владеет только ядро, позволяя непривилегированному уровню (user-space) обращаться к аппаратному обеспечению. В Mach, как в Unix, ОС опять стала главным образом набором утилит. Как и Unix, в Mach есть концепция «драйвера» как посредника между собственно ядром и аппаратным обеспечением. Однако все драйверы для существующего аппаратного обеспечения должны быть включены в микроядро. Другие архитектуры, базирующиеся на слоях абстракции от оборудования или экзоядрах, могут отделять драйверы от микроядра.

Главным отличием от Unix было то, что утилиты должны были работать не с файлами, а с задачами. Больше кода было перемещено из ядра в непривилегированный режим. Ядро за счёт этого стало существенно меньше, отсюда термин микроядро для обозначения ядра Mach. В отличие от традиционных систем, под Mach процесс (или «задача») может состоять из набора потоков. Mach был первой ОС, которая определила понятие "поток" в этом смысле. Задачи ядра были сведены к работе с оборудованием и поддержке утилит. Существование портов и использование IPC определяет большинство различий между Mach и традиционными ядрами. В Unix для обращения к ядру используются «системные вызовы» или «сигнал». Программа использует библиотеку, чтобы разместить данные в известном месте в памяти, и затем вызывает специальное программное прерывание. Когда система впервые запускает ядро, она устанавливает обработчик этого прерывания, поэтому программа, порождающая прерывание, вызывает ядро, которое исследует пришедшую информацию и совершает действия.

В Mach для этой цели используется IPC. Программа запрашивает у ядра доступ к порту, а затем использует механизм IPC для посылки сообщений в порт. В других ядрах эти сообщения обрабатываются системными вызовами; в Mach практически все запросы обрабатываются другой программой. Использование IPC для передачи сообщений поддерживает потоки и конкуренцию. Так как задачи состоят из множества потоков, и их код в потоках использует механизм IPC, Mach может заморозить или разморозить поток, пока сообщение обрабатывается. Это позволяет системе быть распределённой на множество процессоров, используя разделяемую память прямо в большинстве сообщений Mach, или путём добавления кода для копирования сообщения в другой процессор, если требуется. В традиционном ядре это трудно реализовать, потому что система должна быть уверена, что различные программы не пытаются писать в одну и ту же память на различных процессорах. В Mach это хорошо определено и легко реализуется; процесс, совершающий доступ к памяти, портам, делается "гражданином" системы. Система IPC имеет проблемы с производительностью, для преодоления которых было разработано несколько стратегий. В частности, Mach использует единый механизм разделения памяти для физической передачи сообщений от одной программы к другой. Физическое копирование сообщения будет медленным, поэтому Mach обращается к блоку управления памятью (MMU) для быстрого соотнесения данных в одной программе и в другой. Только если данные записываются, они будут физически скопированы, процесс, называющийся «копирование при записи» (copy-on-write; cow).

Сообщения также проверяются на целостность ядром, чтобы избежать плохих данных, которые выведут из строя одну из программ, составляющих систему. Порты были разработаны на основе файловой системы Unix. Это позволило портам использовать существующие концепции навигации по файловой системе, а также права доступа. По сравнению с более традиционными ОС разработка такой системы становится проще. Большая часть системы может быть запущена, отлажена и создана с помощью тех же утилит, что и программы для традиционной системы. С монолитным ядром ошибка в коде требует выключения целой машины и перезагрузки, в то время как в Mach требуется только перезапуск программы. Дополнительно пользователь может указывать системе, включить или выключить возможности по своему желанию. Так как ОС — это коллекция программ, разработчики могут добавлять или удалять её части, просто запуская или останавливая их, как и любую другую программу.

Разработка

Mach изначально располагался, как дополнительный код, написанный к существующему 4.2BSD-ядру, который позволял команде работать на системе задолго до того, как она была завершена. Работа началась с уже готовой Accent IPC/порт системы, и переместилась на другие ключевые части OS, задачи, потоки и виртуальную память. Эти части были переписаны на вызов функций в Mach; параллельно с этим велась работа над 4.3BSD.

В 1986 система была завершена и могла запускаться на DEC VAX. Несмотря на малое практическое значение, цель создать микроядро была воплощена в жизнь. Вскоре последовал выпуск версий для IBM PC/RT и рабочих станций Sun Microsystems 68030, предоставляя портируемость системы. К 1987 в список включены Encore Multimax и Sequent Balance. Release 1 вышел в этот год, а Release 2 - в следующий.

Все это время обещанное «настоящее» микроядро не было создано. Эти ранние версии Mach включали большую часть 4.3BSD ядра, системы, известные как POE, в результате это ядро было фактически больше, чем Unix, на котором оно базировалось. Однако, цель переместить Unix-слой из ядра, где оно более просто разрабатывалось и заменялось, была достигнута. Производительность оставляла желать лучшего, и был осуществлен ряд архитектурных изменений, чтобы решить эту проблему.

В результате Mach 3 вышел в 1990 и вызвал большой интерес. Маленькая команда, которая сделала Mach, портировала его на множество платформ, включая сложные мультипроцессорные системы, которые создавали серьёзные проблемы для старомодных ядер. Также был активирован интерес в коммерческом сегменте рынка, где нашлись компании, которые хотели бы иметь возможность менять платформы, и, если бы они портировали свои ОС на Mach, то могли бы безболезненно менять платформы.

Mach получил видимое ускорение, когда Open Source Foundation анонсировало, что они будут создавать будущую версию OSF/1 на Mach 2.5, и были бы рады использовать Mach 3. Mach 2.5 также был выбран для NeXTSTEP-систем и некоторым количеством коммерческих мультипроцессорных производителей. При помощи Mach 3 было совершено некоторое число попыток портировать другие ОС на это ядро, включая IBM Workplace OS и несколько попыток от Apple Computer создать кросс-платформенную версию Mac OS. Некоторое время казалось, что каждая ОС, созданная в конце 1990-х, будет базироваться на Mach.

Процессы

Процесс Mach

Процесс в Mach - это, в первую очередь, адресное пространство и набор нитей, которые выполняются в этом адресном пространстве. Таким образом, выполнение связано с нитями, а процессы являются пассивными объектами и служат для сбора всех ресурсов, использующихся группой взаимосвязанных нитей, в удобные контейнеры.

Рисунок иллюстрирует процесс в Mach. Кроме адресного пространства и нитей, процесс характеризуется используемыми им портами и некоторыми параметрами. Все порты, показанные на рисунке, имеют специальное назначение. Порт процесса используется для взаимодействия с ядром. Многие из функций ядра процесс может вызывать путем отправки сообщения на порт процесса, а не с помощью системного вызова. Этот механизм используется в Mach всюду для уменьшения количества системных вызовов до возможного минимума. Некоторые из системных вызовов будут далее обсуждены. Вообще говоря, программист может даже не знать, выполняется ли сервис с помощью системного вызова или нет. Всем сервисам, вызываемым как с помощью системных вызовов, так и с помощью передачи сообщений, соответствуют эрзац-процедуры (заглушки, или стабы) в библиотеке. Это именно те процедуры, которые описаны в руководствах и которые вызываются прикладными программами. Эти процедуры генерируются на основании описания сервиса с помощью компилятора MIG (Mach Interface Generator).

Порт загрузки используется при инициализации, когда система стартует. Самый первый процесс читает из порта загрузки, чтобы узнать имена портов ядра, которые обеспечивают наиболее важные сервисы. Процессы UNIX'а также используют этот порт для взаимодействия с UNIX-эмулятором.

Порт особых ситуаций используется системой для передачи сообщений об ошибках процессу. Примерами особых ситуаций является деление на ноль, неправильный код команды. Этот порт также используют отладчики.

Зарегистрированные порты обычно используются для обеспечения возможностей взаимодействия между процессами и стандартными системными серверами.

Процессы также обладают и другими свойствами. Процесс может быть выполняемым или заблокированным, независимо от состояния его нитей. Если процесс является выполняемым, то те его нити, которые также готовы к выполнению, могут планироваться и выполняться. Если процесс заблокирован, то все его нити не могут выполняться независимо от их состояния.

Параметры планирования, помимо нитей, определяются также для процессов. Эти параметры включают возможность определения того, на каких процессорах могут выполняться нити данного процесса. Это свойство наиболее полезно для многопроцессорных систем. Например, процесс может использовать это свойство для того, чтобы заставить выполняться нить на разных процессорах, или заставить все нити работать на одном процессоре, или реализовать какой-нибудь промежуточный вариант. Кроме того, каждый процесс имеет приоритет, который можно устанавливать. При создании нити ей присваивается этот приоритет. Также имеется возможность изменять приоритет всех существующих нитей.

Наконец, каждый процесс имеет статистику, например, о количестве используемой памяти, временах выполнения нитей и т.д. Любой другой процесс, который интересуется этой статистикой, может получить ее, послав сообщение на порт данного процесса.

Также полезно упомянуть, чем не обладает процесс Mach по сравнению с процессами UNIX'а. Процесс не содержит: идентификатор пользователя, групповой идентификатор, маску сигналов, корневой каталог, рабочий каталог, массив дескрипторов файлов. Вся эта информация содержится в параметрах процесса на уровне сервера пользовательского режима.

Примитивы управления процессами [Источник 2]

Mach предусматривает небольшое количество примитивов управления процессами. Большинство из них выполняется путем посылки сообщения ядру через порт процесса. Наиболее важные из этих вызовов приведены в таблице.

Первые два вызова предназначены для создания и уничтожения процессов. Вызов Create определяет прототип процесса, которым не обязательно является вызывающий процесс. Потомок является копией прототипа, за исключением случая наследования родительского адресного пространства, который определяется заданием параметра. Если адресное пространство не наследуется, то объекты (например, текст, инициализированные данные и стек) могут быть отображены в него позже. Изначально потомок не имеет нитей. Однако он автоматически наследует порты загрузки, процесса и особых ситуаций. Другие порты автоматически не наследуются.

Вызов Описание
Create Создает новый процесс, наследующий некоторые свойства
Terminate Завершает определенный процесс
Suspend Наращивает счетчик приостановок
Resume Уменьшает счетчик приостановок; если он равен 0, то разблокирует процесс
Priority Устанавливает приоритет для существующих или будущих нитей
Assign Говорит, на каком процессоре должны выполняться новые нити
Info Возвращает информацию о времени выполнения, используемой памяти и т.д.
Threads Возвращает список нитей процесса

Процессы могут быть приостановлены и возобновлены с помощью программного управления. Каждый процесс имеет счетчик, наращиваемый вызовом Suspend и уменьшаемый вызовом Resume, которые могут блокировать и разблокировать его. Когда счетчик равен 0, то процесс может выполняться. Наличие счетчика позволяет избежать гонок.

Вызовы Priority и Assign позволяют программисту управлять тем, как и где нити выполняются в многопроцессорной системе. Планирование CPU выполняется на основе приоритетов, так что программист может определять, какие нити более важные, а какие - менее важные.

Нити

Активными объектами в Mach являются нити. Все нити процесса разделяют одно адресное пространство и имеют общие в пределах процесса ресурсы, показанные на рисунке 6.2. Кроме того, каждая нить имеет и свои особые ресурсы. Одним из таких ресурсов является порт нити, который является аналогом порта процесса и который используется нитью для того, чтобы вызывать специальные, ориентированные на нити, сервисы ядра, например, функцию завершения нити. Так как порты являются общими ресурсами для всех нитей одного процесса, каждая нить имеет доступ к портам своих нитей-братьев, таким образом каждая нить может управлять другой нитью, если это необходимо. Нити Mach управляются ядром, то есть они являются тем, что иногда называют "тяжеловесными" нитями, в отличие от "легковесных" нитей (нитей, полностью выполняющихся в пользовательском пространстве). Создание и уничтожение нитей осуществляется ядром и включает обновление структур данных ядра. Нити ядра предоставляют базовые механизмы для управления множеством "активностей" в общем адресном пространстве. Что делать с этими механизмами - это дело пользователя.

На однопроцессорной системе нити выполняются в режиме мультипрограммирования. На многопроцессорной системе нити выполняются параллельно. Этот параллелизм делает более важными вопросы синхронизации, взаимного исключения и планирования. Поскольку Mach ориентирована для выполнения на многопроцессорных системах, этим вопросам уделяется особое внимание. Подобно процессу, нить может быть активной или заблокированной. Механизм блокировки очень простой: просто у каждой нити есть счетчик, который наращивается или уменьшается. Когда он равен нулю - нить готова к выполнению. Когда он положителен, нить должна ждать, пока другая нить не уменьшит его до нуля. Такой механизм позволяет нитям управлять поведением друг друга. Имеется некоторое множество примитивов, связанных с нитями. Базовый интерфейс ядра обеспечивает примерно 2 десятка примитивов для нитей. Многие из них связаны с планированием. На базе этих примитивов можно строить различные пакеты нитей.

Мы уже рассматривали один пакет нитей в главе 4, а именно, пакет нитей DCE. Гораздо более скромные возможности предоставляет так называемый C-пакет нитей, поддерживаемый Mach (который был положен в основу пакета OSF). Этот пакет предназначен для того, чтобы дать возможность пользователям использовать нитевые примитивы ядра в простой и доступной форме. Он не предоставляет всю мощность, которую предлагает интерфейс ядра, однако он достаточен для реализации задач среднего уровня сложности. Этот пакет обладает свойством переносимости на широкий круг операционных систем и архитектур.

С-пакет содержит 6 вызовов, предназначенных для непосредственного манипулирования нитями. Эти вызовы приведены в таблице

Вызов Описание
Fork Создает новую нить, выполняющую тот же код, что и родительская нить
Exit Завершает нить
Join Приостанавливает вызывающую нить до тех пор, пока существует некоторая указанная нить
Detach Объявляет, что нить никогда не будет присоединена (ее не нужно ждать)
Yield Отдает управление процессором по собственной инициативе
Self Возвращает нити ее идентификатор

Первый вызов, Fork, создает новую нить в том же адресном пространстве, в котором работает вызывающая нить. Новая нить выполняет процедуру, указанную в качестве параметра вызова Fork, а не процедуру родительской нити. После выполнения вызова родительская нить продолжает выполняться параллельно с порожденной нитью. Нить запускается с приоритетом и назначается на процессор в соответствии с параметрами процесса, к которому она относится.

Когда нить завершает свою работу, она выполняет вызов Exit. Если родительская нить заинтересована фактом завершения данной нити и выполнила вызов Join, то родительская нить блокирует себя до тех пор, пока данная нить, порожденная ею, не завершится. Если эта нить уже завершилась, родительская нить немедленно продолжает свое выполнение. Эти три вызова является грубыми аналогами системных вызовов UNIX'а FORK, EXIT и WAITPID.

Четвертый вызов, Detach, в UNIX'е отсутствует. Он обеспечивает способ, с помощью которого какая-либо нить может объявить, что ее не следует ждать. Обычно очистка стека и другой информации о состоянии родительской нити происходит только после того, как эта нить, присоединившая нить-потомок с помощью вызова Join, будет уведомлена о завершении нити-потомка. Для нити, выполнившей вызов Detach, такая очистка происходит немедленно после ее завершения (с помощью вызова Exit). В сервере это может быть полезно, когда для обслуживания каждого поступающего запроса инициализируется новая нить, а не используется уже существующая.

Вызов Yield говорит планировщику о том, что у нити нет полезной работы в данный момент времени, и она отдает управление процессором по собственной инициативе. В Mach, где обычно используется вытесняющая многозадачность, вызов Yield используется только для оптимизации. В системах с невытесняющей многозадачностью этот вызов - основной для планирования работы нитей.

Синхронизация осуществляется с помощью мьютексов и переменных состояния. Примитивами мьютексов являются вызовы Lock, Trylock и Unlock. Также есть примитивы для распределения и освобождения мьютексов. Примитивы мьютексов в С-пакете те же, что и в пакете DCE.

Реализация С-нитей в Mach

В Mach существует несколько реализаций С-нитей. Оригинальная реализация выполняет все нити в пользовательском пространстве внутри одного процесса. Этот подход основан на разделении во времени всеми С-нитями одной нити ядра, как это показано на рисунке , а. Этот подход может также использоваться в UNIX'е или любой другой системе, в которой нет поддержки нитей ядром. Такие нити работают как разные подпрограммы одной программы, что означает, что они планируются невытесняющим образом. Например, в случае типовой задачи "производитель-покупатель" производитель должен заполнить буфер, а затем заблокироваться, чтобы дать шанс покупателю на выполнение.

Варианты реализаций С-нитей (а) - Все C-нити используют одну нить ядра; (б) - Каждая C-нить имеет свою собственную нить ядра; (в) - Каждая C-нить имеет свой собственный однонитевый процесс

Данная реализация имеет недостаток, присущий всем нитевым пакетам, работающим в пользовательском пространстве без поддержки ядра. Если одна нить выполняет блокирующий системный вызов, например, чтение с терминала, то блокируется весь процесс. Чтобы избежать этой ситуации, программист должен избегать использовать блокирующие системные вызовы. В BSD UNIX имеется системный вызов SELECT, который может быть использован для того, чтобы узнать, имеется ли символ в буфере терминала. Но в целом операция остается запутанной.

Вторая реализация использует одну Mach-нить для одной С-нити, как показано на рисунке. Эти нити планируются с вытеснением. В многопроцессорных системах нити могут действительно работать параллельно на различных процессорах. Фактически возможно мультиплексирование m пользовательских нитей с n нитями ядра, хотя наиболее общим случаям является n=m.

В третьей реализации для каждого процесса выделяется одна нить ядра, как показано на рисунке. Процессы реализуются так, что их адресные пространства отображаются в одну и ту же физическую память, позволяя разделять ее тем же способом, что и в предыдущих реализациях. Данный способ поддержки нитей используется только тогда, когда требуется специализированное использование виртуальной памяти. Недостатком этого метода является то, что порты, UNIX-файлы и другие ресурсы процесса не могут разделяться.

Основное практическое значение первого подхода состоит в том, что в условиях отсутствия истинного параллелизма последовательное выполнение нити позволяет воспроизводить промежуточные результаты, облегчая процесс отладки.

Управление памятью в Mach

Ядро Mach имеет мощную, тщательно разработанную и в высшей степени гибкую систему управления памятью, основанную на страничном механизме и обладающую многими редкими свойствами. В частности, в нем машинно-зависимая часть кода отделена от машинно-независимой части чрезвычайно ясным и необычным способом. Это разделение делает управление памятью более мобильным, чем в других системах. Кроме того, система управления памятью тесно взаимодействует с коммуникационной системой.

Одной из основных особенностей системы управления памятью Mach является то, что ее код разбит на три части. Первая часть называется pmap, который работает в ядре и занимается работой с устройством отображения виртуальных адресов в физические (Memory Management Unit, MMU). Эта часть устанавливает значения регистров MMU и аппаратных страничных таблиц, а также перехватывает все страничные прерывания. Эта часть кода зависит от архитектуры MMU и должна быть переписана для каждой новой машины всякий раз, когда Mach на нее переносится. Вторая часть - это машинно-независимый код ядра, и он связан с обработкой страничных сбоев, управляет отображением областей памяти и заменой страниц.

Третья часть кода работает в пользовательском пространстве в качестве процесса, называемого "менеджер памяти" (memory manager) или иногда "внешний менеджер страниц" (external pager). Эта часть имеет дело с логическими аспектами (в отличие от физических) системы управления памятью, в основном, с управлением хранения образов памяти на диске. Например, менеджер памяти отслеживает информацию о том, какие виртуальные страницы используются, какие находятся в оперативной памяти и где они хранятся на диске, когда не находятся в оперативной памяти. Ядро и менеджер памяти взаимодействуют с помощью хорошо определенного протокола, что дает возможность для пользователей ядра Mach писать свои собственные менеджеры памяти. Такое разделение обязанностей позволяет реализовывать страничные системы специльного назначения. При этом ядро становится потенциально меньше и проще, так как значительная часть кода работает в пользовательском пространстве. С другой стороны, это и источник усложнения ядра, так как при этом оно должно защищать себя от ошибок или некорректной работы менеджера памяти. Кроме того, при наличии двух активных частей системы управления памятью возможно возникновение гонок между ними.

Коммуникации в ядре Mach

Основной целью, которую ставили перед собой разработчики средств коммуникации ядра Mach, являлась поддержка различных стилей коммуникаций в сочетании с надежностью и гибкостью. Коммуникационные средства ядра Mach могут поддерживать асинхронную передачу сообщений, RPC, потоки байт (streams), а также другие способы. Механизм взаимодействия процессов Mach базируется на соответствующих механизмах своих предшественников - RIG и Accent. Из-за своего эволюционного развития этот механизм приспособлен больше для локального использования, а не для распределенных систем.

Сначала рассмотрим случай одного узла, а затем расширим его для сети. Следует отметить, что мультипроцессор - это тоже один узел, поэтому взаимодействие процессов, работающих на разных процессорах многопроцессорной машины, также относится к локальному случаю.

Проблемы с производительностью

Некоторые возможности Mach базировались на тех же механизмах IPC. К примеру, Mach с лёгкостью может поддерживать многопроцессорные компьютеры. В традиционном ядре экстенсивная работа проделывается, чтобы обеспечить реентерабельность или прерываемость программ, запущенных на разных процессорах и способных вызывать функции ядра в одно и то же время. В Mach кусочки операционной системы изолированы в серверах, которые могут запускаться, так же, как и другие программы — на любом процессоре. Потому теоретически ядро Mach также должно быть реентерабельным, но на практике это не проблема, так как все, что нужно Mach — это направить запрос какой-нибудь непривилегированной программе. Mach также включал сервер, который может перенаправлять сообщения не только между программами, но и по сети. Работа в этой области была проделана в конце 1980-х и начале 1990-х. Использование IPC для большинства задач снижает производительность. Сравнения, проведенные в 1997 году, показали, что Unix, построенный на Mach 3.0, на 50 % медленнее, чем традиционный Unix.

Исследования показали, что производительность падает из-за IPC, и достичь ускорения за счет раздробления ОС на маленькие сервера невозможно. Было сделано множество попыток улучшить производительность Mach, но в середине 1990 интерес пропал. Концепция системы, основанной на IPC, похоже, умерла.

Фактически, исследование природы проблем производительности выявило несколько интересных фактов: один — IPC сам по себе не является проблемой, проблема в необходимости отображения памяти для его поддержки, что добавляет небольшие временные затраты. Большинство времени (около 80 %) тратится на дополнительные задачи в ядре — на обработку сообщения, в первую очередь - проверку прав порта и целостность сообщения. В тестах на Intel 80486DX-50 стандартный Unix-вызов занимает около 21 микросекунды, соответствующий вызов в Mach занимает 114 микросекунд, из них 18 микросекунд относятся к аппаратному обеспечению, остальное относится к различным функциям ядра Mach.

Когда Mach впервые был использован в серьёзных разработках (2.x версия), производительность была ниже, чем в традиционных ядрах, примерно на 25 %. Эта цена не вызывала беспокойства, потому что система хорошо портировалась и работала на множестве процессоров. Фактически, система скрыла серьёзные проблемы с производительностью до выхода Mach 3, когда множество разработчиков попыталось создать системы, запускаемые в непривилегированном режиме.

Когда Mach 3 попытался переместить ОС в непривилегированный режим, потеря производительности стала заметной. Рассмотрим простой пример: задача узнаёт текущее время. Посылается сообщение ядру Mach, это вызывает переключение контекста, отображение памяти, затем ядро проверяет сообщение и права, если все хорошо, то вызывается переключение контекста на сервер, затем сервер выполняет действия и посылает сообщение назад, ядро выделяет ещё памяти и переключает контекст дважды.

Но здесь есть проблема. Она заключается в системе подкачки виртуальной памяти. Традиционные монолитные ядра знают, где ядро и его модули, а где память, которую можно выгружать, в то время как Mach не имеет ни малейшего представления о том, из чего состоит система. Вместо этого он использует единое решение, добавляющее проблем с производительностью. Mach 3 дает простой менеджер памяти, который обращается к другим менеджерам, выполняющимся в непривилегированном режиме, что заставляет систему делать дорогие IPC-вызовы. Многие из этих проблем существуют в любой системе, которой необходимо работать на многопроцессорной машине, и в середине 1980-х казалось, что будущий рынок будет наполнен ими. Фактически эволюция не работает, как ожидается. Мультипроцессорные машины использовались в серверных приложениях в начале 1990-х, но затем исчезли. Тем временем производительность CPU возрастала на 60 % в год, умножая неэффективность кода. Плохо, что скорость доступа к памяти растет только на 7 % в год, это значит, что цена обращения к памяти не уменьшилась, и вызовы IPC Mach’а, которые не сохраняются в кэше, работают очень медленно.

Несмотря на возможности Mach, такие потери производительности в реальном мире неприемлемы, поэтому большая часть сообщества разработчиков ОС посчитала, что использование IPC в качестве основы ОС изначально провально.

Возможные решения

Как мы установили выше, большинство производительности Mach 3 теряется на IPC-вызовах. Однако концепция «многосерверной системы» все ещё многообещающая, поэтому она требует исследований. Разработчикам необходимо аккуратно изолировать код в модули, которые не делают вызовов от сервера к серверу. К примеру, большая часть кода для работы с сетью должна быть помещена в отдельный сервер. Под Unix это не так-то просто, потому что система базируется на использовании файловой системы для всего, от сети до безопасности.

Большинство разработчиков застряло на оригинальной концепции единого большого сервера, который предоставляет функциональность ОС. Также для простоты разработки они разрешили операционной системе работать в привилегированном и непривилегированном режимах. Это позволяет им разрабатывать в непривилегированном режиме и иметь все возможности идеи Mach, и затем перемещать отлаженный сервер в привилегированный режим, чтобы иметь лучшую производительность. Несколько операционных систем разработано подобным методом, известным как «ко-локейшн» (co-location), это используется в Lites (порт 4.4BSD Lite), MkLinux, OSF/1 и NeXTSTEP/OpenStep/Mac OS X. В ChorusOS эту возможность сделали частью базовой системы, разрешив серверам переходить в привилегированный режим с помощью встроенных механизмов.

В Mach 4 пытались решить эту проблему с помощью радикального набора улучшений. В частности, он находил программный код, который обычно не записываем, и поэтому копирование при записи случается редко. Это делало возможным не сопоставлять память между процессами (map memory) для IPC, а вместо этого использовать локальные области памяти программ. Это создало концепцию «шаттлов» и увеличило производительность, но разработчикам досталась сложность с управлением состояниями. В Mach 4 также включили встроенные механизмы колокейшна.

Во всех тестах IPC производительность была названа источником проблемы, ей причисляется 73 % потерянных циклов.

В середине 90-х работа над микроядерными системами остановилась. Хотя рынок верил, что все новые системы будут микроядерными в 90-х, сегодня только одна широко используемая система Mac OS X использует колокейшн-сервер поверх сильно модифицированного ядра Mach 3.

Следующее поколение

Исследования показали, что проблема производительности IPC не так страшна, как считается. Напоминаем, что односторонний вызов на BSD занимает 20 микросекунд, в то время как на Mach — 114, 11 из которых — это переключение контекста, идентичного BSD. Дополнительно 18 используется менеджером памяти для отображения сообщения между непривилегированной средой исполнения и привилегированной (user-space и kernel-space). Это добавляет 31 микросекунду, что дольше традиционного вызова, но не намного. Оставшаяся часть проблемы — это проверки прав доступа к порту сообщений. В то время, как это выглядит очень важным, фактически, это требуется только на Unix системах. К примеру, однопользовательская система, запущенная на мобильном телефоне, может не нуждаться в таких возможностях, и это тот тип систем, в которых Mach может быть использован. Однако Mach создает проблемы: когда память перемещается в ОС, другие задачи могут не нуждаться в этом. DOS и ранние Mac OS имели единое адресное пространство, разделяемое всеми процессами, поэтому в таких системах отображение памяти — пустая трата времени.

Эти реализации положили начало второму поколению микроядер, которое уменьшает сложность системы, размещая большую часть функциональности в непривилегированном режиме исполнения. Например, ядро L4 включает только 7 функций и использует 12 килобайт памяти, тогда как Mach 3 включает около 140 функций и использует 330 килобайт памяти. IPC вызов на L4 на 486DX-50 занимает только 5 микросекунд — быстрее, чем Unix вызов на этой же системе, и в 20 раз быстрее, чем Mach. Конечно здесь не учитывается тот факт, что L4 не оперирует разрешениями и безопасностью, оставляя их на усмотрение непривилегированным программам.

«Потенциальные» ускорения L4 основаны на факте, что непривилегированные приложения часто предоставляют множество функций, которые формально поддерживаются ядром. Можно сравнить производительность MkLinux в колокейшн режиме и порт L4, запущенный в непривилегированном режиме. L4 добавляет только около 5 %-10 % накладных расходов, в то время как Mach добавляет 15 %, что весьма интересно, если учитывать двойные переключения контекста.

В результате новые микроядра изменили индустрию в целом, множество некогда мертвых проектов вроде GNU Hurd снова привлекло к себе внимание.

Операционные системы и ядра, основанные на Mach

  • GNU Hurd
  • Lites
  • MkLinux
  • mtXinu
  • MachTen
  • MacMach
  • Mac OS X (Darwin)
  • NeXTSTEP
  • OSF/1
  • Workplace OS
  • xMach
  • UNICOS MAX

Источники

  1. Mach // Wikipedia: сайт. URL: https://ru.wikipedia.org/wiki/Mach (дата обращения: 7.05.2017).
  2. Mach // ХПИ: сайт. URL: http://khpi-iip.mipk.kharkiv.edu/library/extent/os/sos/sos06.html (дата обращения: 8.05.2017).

Ссылки/Литература