Введение
Vanguard — экспериментальное микроядро, разработанное в Apple Computer в исследовательской группе Apple Advanced Technology Group (ATG) в начале 1990-х годов. Основанный на системе V, Vanguard внедрил стандартизированные идентификаторы объектов и уникальную систему последовательной передачи сообщений для повышения производительности. Vanguard не был использован ни в одном из коммерческих продуктов Apple. Разработка была прекращена в 1993 году, когда Росс Финлейсон, ведущий исследователь проекта, покинул Apple.
Основные понятия
Vanguard был в целом очень похож на V System, но добавил поддержку полноценного объектно-ориентированного программирования операционной системы. Это означало, что интерфейсы ядра и серверов экспортировались в виде объектов, которые можно было наследовать и расширять в новом коде. Это изменение не оказывало видимого влияния на систему, это в основном изменение в исходном коде, упрощающее программирование. Например, в Vanguard был класс ввода/вывода (I/O), который поддерживался несколькими различными серверами, такими как сетевые и файловые серверы, с которыми новые приложения могли взаимодействовать, импортируя интерфейс I/O и вызывая методы. Это также значительно упростило разработку новых серверов, поскольку у них был единый стандарт для программирования и появилась возможность легче обмениваться кодом.
Семантика V-сообщений
Ключевая концепция, лежащая в основе почти всех микроядер, заключается в разделении одного большого ядра на набор взаимодействующих серверов. Вместо того чтобы одна большая программа контролировала все аппаратное обеспечение компьютерной системы, различные функции распределяются между небольшими программами, которым предоставляются права на управление различными частями машины. Например, один сервер может получить контроль над сетевым оборудованием, а другой – над управлением жесткими дисками. Еще один сервер обрабатывает файловую систему, вызывая эти два сервера нижнего уровня. Пользовательские приложения запрашивают сервисы, отправляя сообщения этим серверам, используя ту или иную форму межпроцессного взаимодействия (IPC), в отличие от запроса к ядру выполнить эту работу посредством системного вызова (syscall) или прерывания. В системе V система IPC, по-видимому, концептуально моделируется на основе удаленных вызовов процедур (RPC) с точки зрения клиентского приложения. Клиент импортирует файл определения интерфейса, содержащий информацию о вызовах, поддерживаемых ядром или другими приложениями, а затем использует это определение для формирования запросов. При вызове ядро немедленно берет управление на себя, анализирует результаты и передает информацию соответствующему обработчику, возможно, внутри ядра. Любые результаты затем возвращаются через ядро клиенту. Работа системы, как она представляется клиентскому приложению, очень похожа на работу с обычным монолитным ядром. Хотя возвращаемые результаты могут поступать от обработчика третьей стороны, это по сути невидимо для клиента. Серверы, обрабатывающие эти запросы, работают аналогично клиентам, устанавливая соединения с ядром для передачи данных. Однако серверы обычно порождают новые потоки по мере необходимости для обработки длительных запросов. После обработки этих запросов и отправки ответов поток может быть освобожден, а серверы могут перейти в режим приема, ожидая дальнейших запросов. В отличие от этого, большинство систем на микроядрах основаны на модели асинхронной связи, а не на синхронных вызовах процедур. Каноническая система микроядер, Mach, моделирует сообщения как операции ввода-вывода, что имеет несколько важных побочных эффектов. Главный из них заключается в том, что обычные планировщики задач в Unix-подобных системах обычно блокируют клиент, ожидающий запрос ввода-вывода, таким образом, механизмы приостановки и возобновления работы приложений, ожидающих сообщений, уже встроены в базовую систему. Недостатком этого подхода является то, что планировщик достаточно ресурсоемкий, и обращение к нему является серьезным узким местом в производительности, что привело к значительным усилиям по ее улучшению. В модели системы V накладные расходы на передачу сообщений снижаются, поскольку не требуется обращаться к планировщику процессов, нет вопросов о том, что следует запускать дальше – вызываемый сервер. Недостатком подхода V является то, что он требует больше работы от сервера, если ответ может занять некоторое время на обработку.
Цепление
Одним из ключевых нововведений в системе IPC под управлением Vanguard, в отличие от V, стала концепция цепочек сообщений, позволяющая отправлять одно сообщение между несколькими взаимодействующими серверами за один обмен данными. Теоретически, использование цепочек могло повысить производительность часто выполняемых многоэтапных операций. Рассмотрим пример, когда клиентскому приложению необходимо прочитать файл. Обычно для этого требуется одно сообщение ядру для поиска файлового сервера, а затем еще три сообщения файловому серверу: одно для преобразования имени файла в идентификатор объекта, другое для открытия этого идентификатора и, наконец, третье для чтения файла. С помощью цепочек Vanguard клиент мог сформировать одно сообщение, содержащее все эти запросы. Это сообщение отправлялось бы ядру, а затем передавалось файловому серверу, который обрабатывал бы все три запроса и, наконец, возвращал данные. Значительная часть проблем с производительностью, обычно возникающих в микроядерных системах, связана с переключениями контекста при передаче сообщений между приложениями. В приведенном выше примере, работающем на системе V, потребовалось бы в общей сложности восемь переключений контекста – по два для каждого запроса, когда клиент переключался в ядро и обратно. В Vanguard использование цепочки сократило бы это число до трех переключений: одно от клиента в ядро, другое от ядра к файловому серверу и, наконец, от сервера обратно к клиенту. В некоторых случаях накладные расходы на переключение контекста превышают время, необходимое для фактического выполнения запроса, поэтому механизм цепочек Vanguard мог привести к реальному повышению производительности.
Название объекта
V также представила простую распределенную службу имен. Эта служба хранила известные имена, представляющие различные объекты в распределенной системе V, например, лазерный принтер на втором этаже. Приложения могли запрашивать у сервера имен объекты по имени и получать идентификатор, позволяющий взаимодействовать с этим объектом. Служба имен не была отдельным сервером и управлялась кодом в ядре. Это отличалось от полноценного сервера имен в операционной системе Spring, который знал не только об объектах внутри системы, но и использовался другими серверами для преобразования их внутренних имен, таких как имена файлов и IP-адреса. В системе V объекты на серверах идентифицировались с помощью некоего произвольного приватного ключа, например, 32-битного целого числа. Клиенты передавали эти ключи серверам для поддержания взаимодействия по конкретной задаче. Например, приложение могло запросить у ядра файловую систему и получить 32-битный ключ, представляющий идентификатор программы, а затем использовать этот ключ для отправки сообщения файловой системе с просьбой открыть файл my addresses, в результате чего возвращался 64-битный ключ. Ключи в этом примере были специфичны для каждого сервера, единого формата ключей в системе не существовало. Поскольку такое разрешение имен было очень распространено в V, авторы решили сделать эти ключи полноценными объектами в Vanguard. Вместо использования любых идентификаторов объектов, которые случайно использовали серверы, в Vanguard от всех серверов требовалось понимать и возвращать глобально уникальный 128-битный ключ, первые 64 бита которого содержали идентификатор сервера, а следующие 64 бита – идентификатор объекта на этом сервере. Идентификатор сервера хранился в ядре, что позволяло перенаправлять сообщение по сети, если целевой сервер находился на удаленной машине. Для клиента это было прозрачно. Неизвестно, генерировались ли эти идентификаторы случайным образом, чтобы предотвратить их успешное угадывание злонамеренным программным обеспечением.