/sys/doc/ Documentation archive

Сетевая организация в Plan 9

Дейв Пресотто
Фил Уинтерботтом

presotto,philw@plan9.bell-labs.com

АБСТРАКТНО

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

1. Введение

Plan 9 [1] представляет собой многопользовательскую портабильную распределенную систему общего назначения, функционирующую на разнообразных компьютерах и сетях. Plan 9 отличается от других систем своей организацией, перед которой ставились две цели: уменьшение администрирования и продвижение совместного использования ресурсов. Одним из факторов успеха Plan 9 как распределенной системы является организация и управление сетями.

Система Plan 9 состоит из файловых серверов, CPU серверов и терминалов. Файловые и CPU серверы обычно представляют собой централизованные многопроцессорные машины с большим количеством памяти и высокоскоростными взаимосвязями. Различные машины класса рабочих станций служат терминалами, соединенными с центральными серверами посредством нескольких сетей и протоколов. Архитектура системы нуждается в иерархии сетевых скоростей, соответствующих требованиям компонентов. Соединения между файловыми и CPU серверами — волоконные связи точка-точка, обладающие высокой пропускной способностью. Соединения от серверов развертываются к локальным терминалам с использованием сетей средней скорости, наподобие Ethernet [4] и Datakit [5]. Низкоскоростные соединения через Internet и базовую сеть AT&T обслуживают пользователей в Орегоне и Иллинойсе. Сеть типа ISDN и последовательные линии на 9600 baud обеспечивают медленные связи с пользователями на дому.

Поскольку CPU серверы и терминалы используют одно и то же ядро, у пользователей есть выбор: запускать программы локально на своих терминалах или удаленно на CPU серверах. Организация Plan 9 скрывает детали системной связности, позволяя и пользователям, и администраторам конфигурировать свою среду, чтобы получить распределения или централизацию, по их усмотрению. Простые команды поддерживают конструкцию локально представленного пространства имен, которое распределяется на много машин и сетей. На работе пользователи используют свои терминалы как рабочие станции, запуская интерактивные программы локально и резервируя CPU серверы для заданий интенсивной обработки данных, типа компиляции и вычисления шахматных эндшпилей. Дома или при соединении через медленную сеть, пользователи для снижения траффика на медленных связях выполняют большую часть работы на CPU сервере. Задача сетевой организации состоит в обеспечении одинаковой среды для клиентов (пользователей) независимо от используемых ресурсов.

2. Сетевая поддержка в ядре

Сети играют важнейшую роль в любой распределенной системе. В особенности, это применимо к Plan 9, где большинство ресурсов организованы во внешние по отношению к ядру серверы. Значение сетевого кода в пределах ядра отражено в его размере; 25 тыс. строк ядерного кода, из которых 12,5 тыс. приходится на сеть, протокол и связанные средства. Сети непрерывно добавляются и доля кода, отведенного коммуникациям, постоянно растет. Кроме того, сетевой код достаточно сложный. Реализации протокола почти полностью состоит из синхронизации и управления динамической памятью, это область, требовательная к стратегиям, которым присущее тонкое восстановление ошибок. На сегодня ядро имеет поддержку Datakit, волоконных связей типа точка-точка, Internet (IP) протокола и ISDN. Разнообразные сети и машины подняли вопросы, которые ранее не были адресованы другими системами, запущенными на коммерческом аппаратном обеспечении с поддержкой только Ethernet или FDDI.

2.1 Протокол файловой системы

Центральной идеей Plan 9 является представление ресурсов в виде иерархической файловой системы. Каждый процесс создает вид системы путем построения пространства имен [2], объединяющего используемые им ресурсы. Файловые системы не должны представлять дисковые файлы; фактически, большинство файловых систем Plan 9 не имеют постоянной памяти. Типичная файловая система динамически представляет некоторые ресурсы подобно набору сетевых соединений или таблице процессов. Связь между ядром, драйверами устройств и локальными или удаленными файловыми системами обеспечивает протокол под названием 9P. Протокол состоит из 17 сообщений, описывающих операции над файлами. Ядро-резидентное устройство и драйверы протокола используют процедурную версию протокола, в то время как внешние файловые серверы используют форму RPC. Почти весть траффик между системами Plan 9 составляют сообщения протокола файловой системы. 9P основан на различных свойствах лежащего в его основе транспортного протокола. Он отвечает за надежную и последовательную доставку сообщений с сохранением разделителей.

Структура данных ядра, канал (channel), обрабатывается файловым сервером. Операции над каналом генерируют следующие сообщения 9P. Сообщения session и attach аутентифицируют соединение, установленное внешними по отношению к 9P средствами, и подтверждают пользователя. Результатом является аутентифицированный канал, указывающий на корневой каталог сервера. Сообщение clone создает новый канал, идентичный существующему (это сообщение во многом схоже с системным вызовом dup). Канал может быть перемещен в файл на сервере с помощью сообщения walk. Сообщения stat и wstat читают и записывают атрибуты в файл, указанный каналом. Сообщение open подготавливает канал для последующих сообщений read и write, отвечающих за доступ к содержимому файла. Create и remove выполняют соответствующие названиям действия над файлом, указанный каналом. Сообщение clunk отвергает канал без влияния на файл.

Драйвер монтирования — это ядро-резидентный файловый сервер, который преобразует процедурную версию 9P в версию RPC. Системный вызов mount представляет файловый дескриптор, который может быть конвейер пользовательского процесса или сетевым соединением с удаленной машиной, для ассоциирования с точкой монтирования. После монтирования, операции над файловым деревом ниже точки монтирования отправляются на файловый сервер в виде сообщений. Драйвер монтирования управляет буферами, выполняет упаковку и распаковку параметров из сообщений, и демультиплексируется среди процессов с использованием файлового сервера.

2.2 Ядерная организация

Сетевой код ядра разделен на три слоя: аппаратный интерфейс, обработка протокола и программный интерфейс. Драйвер устройства обычно использует потоки для соединения двух интерфейсных слоев. Для обработки протоколов, модули дополнительного потока могут выталкиваться в устройстве. Каждый драйвер устройства является ядро-резидентной файловой системой. Простые драйверы устройств подаются в виде одноуровневого каталога, содержащего всего несколько файлов; например, мы представляем каждое UART устройство файлами данных и управления.

Ctl файл используется для управления устройством; запись строки b1200 в файл /dev/eia1ctl устанавливает скорость передачи данных 1200 baud.

Мультиплексные устройства представлены структурой с более сложным интерфейсом. К примеру, Ethernet драйвер LANCE подается в виде двухуровневого файлового дерева (Рис. 1), обеспечивая:

Верхний каталог содержит файл clone и по каталогу для каждого соединения, пронумерованные от 1 до n. Каждый каталог соединения соответствует типу пакета Ethernet. За открытием файла clone следует поиск неиспользуемого каталога соединения и открытие его ctl файла. Чтение управляющего файла возвращает ASCII номер соединения; пользовательский процесс может использовать это значение для создания соответствующего каталога соединения. В каждом каталоге соединения файлы ctl, data, stats и type обеспечивают доступ к соединению. Запись строки connect 2048 в ctl файл устанавливает тип пакета 2048 и ориентирует соединение для приема всех IP пакетов, отправленных машине. Последующие чтения файла type дадут строку 2048. Файл data имеет отношение к устройству, его чтение возвращает следующий пакет выбранного типа. Запись файла ставит в очередь передачи пакет, чей заголовок, содержащий исходный адрес и тип, был добавлен. Файл stats возвращает ASCII текст, который содержит следующие данные: адрес интерфейса, количество ввода-вывода пакета, статистику ошибок и общую информацию о состоянии интерфейса.

Если несколько соединений в интерфейсе сконфигурированы под конкретный тип пакета, тогда каждое будет получать копию входящих пакетов. Специальный тип пакета -1 выбирает все пакеты. Для приема всех пакетов из Ethernet необходимо в ctl файл записать строки promiscuous и connect -1.

Хотя интерфейс драйверов может показаться несколько сложным, представление устройства как набора текстовых файлов и использование ASCII строк для коммуникаций имеет несколько преимуществ. Любой механизм, поддерживающий удаленный доступ к файлам, позволяет удаленной машине использовать наши интерфейсы как шлюзы. При использовании ASCII строк для управления интерфейсом избегаются проблемы с порядком байт, обеспечивается единое представление устройств на одной машине, а также удаленный доступ к устройствам. Представление разнородных устройств в виде одинакового набора файлов позволяет общим средствам работать в различных сетях и интерфейсах. Программы вроде stty заменены на echo и переадресацию оболочки.

2.3 Устройства протокола

Сетевые соединения представлены как псевдо-устройства, которые называются устройствами протокола. Драйверы устройств присутствуют для протокола Datakit URP и каждого из Internet IP протоков TCP, UDP, и IL. IL, описанный ниже, является новым протоколом связи и используется в Plan 9 для передачи удаленных процедурных вызовов файловой системы. Все драйверы протокола выглядят одинаково, так что пользовательские программы не содержат специфического для сети кода.

Каждый драйвер устройства протокола подан в виде структуры каталогов аналогичной таковой в драйвере LANCE. Верхний каталог содержит файл clone и по каталогу для каждого соединения, пронумерованные от 0 до n. Каждый каталог содержит файлы управления, передачи и приема информации для одного соединения. Каталог соединения TCP выглядит таким образом:

Файлы local, remote и status содержат информацию о состоянии соединения. Файлы data и ctl обеспечивают доступ к процессовому концу потока, реализующего протокол. Файл listen используется для приема входящих вызовов из сети.

Следующие шаги устанавливают соединение.

  1. Клон устройства соответствующего каталога протокола открыт для резервирования неиспользуемого соединения.
  2. Файловый дескриптор возвращается открытыми указателями в ctl файл нового соединения. Чтение файлового дескриптора возвращает номер соединения.
  3. Специфичный для протокола/сети. ASCII строка с адресом записывается в ctl файл.
  4. Путь к файлу data создан посредством номера соединения. Соединение устанавливается, когда открывается файл data.

Процесс может читать и записывать этот файловый дескриптор в передаваемые и принимаемые сообщения из сети. Если процесс открывает файл listen, то происходит блокировка, пока не будет получен входящий вызов. Адрес строки записывается в ctl файл, перед тем как listen выберет порты или сервисы, которые процесс готов принимать. За приемом входящего вызова следует завершение открытия и возвращение файлового дескриптора, указывающий на ctl файл нового соединения. Чтение ctl файла дает номер соединения, который используется для создания пути к файлу data. Соединение остается установленным, пока любой из файлов в каталоге соединения является ссылочным или пока из сети не пришло сообщение close.

2.4 Потоки

Поток [3, 7] — это двунаправленный канал, соединяющий физическое или псевдо-устройство с пользовательскими процессами. Пользовательские процессы вставляют и удаляют данные из одного конца потока. Ядро обрабатывает действие на стороне устройства, вставляя данные в другой конец. С использованием потоков реализованы асинхронные каналы связи типа каналов, обмены информацией TCP, Datakit, и линии RS232.

Поток включает линейный список обрабатывающих модулей. Каждый модуль имеет две подпрограммы вставки (называемые также put-процедурами): поток-вверх (по отношению к процессу) и поток-вниз (по отношению к устройству). Вызов подпрограммы вставки модуля на любом конце вставляет данные в поток. Каждый модуль вызывает последующую отправку данных вверх или вниз в поток.

Пример обрабатывающего модуля представлен парой очередей, по одной для каждого направления. Очереди указывают на подпрограммы вставки и могут использоваться для поочередной передачи информации в потоке. Некоторые подпрограммы вставки выполняют локальное чередование данных, затем отправляя их в поток, это происходит или из-за последующего вызова, или асинхронного события, наподобие повторной передачи, или прерывания устройства. Обрабатывающие модули создают helper процессы ядра помощники для обеспечения контекста для управляющих асинхронных событий. К примеру, helper процесс ядра приводит к периодическому выполнению любых необходимых повторных передач TCP. Использование процессов ядра взамен серийным запуск-до-завершения процедурным службам отличается от реализации Unix потоков. Процедурные службы Unix не могут использовать любые блокирующие ресурсы ядра и в них отсутствует локальное долговечное состояние. Helper процессы ядра решают эти проблемы и существенно упрощают код потоков.

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

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

2.4.1 Пользовательский интерфейс

На пользовательском уровне поток представляют два файла: ctl и data. Фактические имена могут быть изменены драйвером устройства с использованием потока, как мы ранее продемонстрировали в примере с драйвером UART. Первый процесс, открывающий любой из файлов, автоматически создает поток. Последнее закрытие уничтожает его. Запись файла data приводит к копированию данных в блоки ядра и их передачи подпрограммой вставки поток-вниз в первый обрабатывающий модуль. Запись менее 32 KB гарантировано поместится в один блок. Параллельные записи в один поток не синхронизируются, хотя размер блока в 32 KB гарантирует элементарные записи большинства протоколов. Последний записанный блок имеет разделитель для предупреждения о границах записи модулей потока-вниз. В большинстве случаев первая подпрограмма вставки вызывает вторую, вторая вызывает третью и т.д., пока данные не закончатся. В результате, большинство данных выводятся без контекстного переключения.

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

Подобно Unix потокам [7], потоки Plan 9 могут динамически конфигурироваться. Потоковая система перехватывает и интерпретирует следующие управляющие блоки:

push имя

добавляет пример имени обрабатывающего модуля к верху потока

pop удаляет верхний модуль потока
hangup отправляет сообщение о зависании в поток из конца устройства

Другие управляющие блоки являются модуле-специфичными и интерпретируются каждым обрабатывающим модулем после передачи.

Свернутые синтаксис и семантика системного вызова Unix ioctl убедили нас оставить его вне Plan 9. Вместо него используется файл ctl. Запись информации в ctl файла идентична записи в файл data за исключением блоков, которые имеют тип управление. Обрабатывающий модуль выполняет грамматический разбор каждого управляющего блока, представленному ему. Командами в управляющих блоках служат ASCII строки, таким образом, когда одна система управляет потоками в пространстве имен, реализованном на другом процессоре, порядок байт не имеет значения. Поскольку управляющие операции редкие, то время грамматического разбора управляющих блоков роли не играет.

2.4.2 Интерфейс устройства

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

2.4.3 Мультиплексирование

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

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

2.4.4 Отражения

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

Потоки остаются в нашем ядре, поскольку мы не в состоянии разработать лучшую альтернативу. X-ядро Ларри Питерсона (Larry Peterson) [6] является ближайшим кандидатом, но преимуществ, которые мы можем получить при переключении на него, недостаточно. Если бы код потоков поддавался обновлению, то для уменьшения сложности мы бы статически распределили ресурсы для большого фиксированного числа обменов информацией и сжигания памяти.

3. Протокол IL

Ни один из стандартных IP протоколов не пригоден для передачи сообщений 9P через сети Ethernet или Internet. У TCP слишком большие потери данных и он не сохраняет разделители сообщений. UDP, при низкой стоимости и сохранении разделителей сообщений, отличается ненадежной доставкой дейтаграмм. Во время реализации IP, TCP и UDP в нашей системе мы пробовали выбрать протокол, пригодный для переноса сообщений 9P. Необходимыми были такие свойства:

Так как протокол, удовлетворяющий таким требованиям, не был найден, то мы разработали новый. IL — это легкий, изолированный от IP, протокол. Он основан на соединениях и обеспечивает надежную передачу упорядоченных сообщений. Поскольку протокол предназначен для передачи сообщений RPC между клиентом и сервером, то отпадает необходимость в средствах управления потоками. Нас вполне устраивает структура с присущими потоковыми ограничениями. Уменьшенное окно для нераспределенных сообщений предотвращает от буферизации слишком большого количества входящих сообщений; сообщения за пределами окна отвергаются и должны быть переданы повторно. Установка соединения использует дуплексное квитирование связи для генерации начальных последовательных номеров в каждом конце соединения; чтобы получатель мог изменять порядок сообщений, последующие сообщения данных увеличивают последовательные номера. В отличие от других протоколов, IL избегает слепые повторные передачи, таким образом не происходит продублирование сообщений. Это свойство повышает производительность протокола в перегруженных сетях, где слепые повторные передачи могут вызывать дальнейшие перегрузки. Подобно TCP, IL имеет адаптивные тайм-ауты, в результате чего протокол работает одинаково хорошо как в Internet, так и в Ethernet.

Для сохранения минимализма конструкции остальной части ядра, IL имеет небольшой размер. Код всего протокола составляет 847 строк, по сравнению с 2200 строками для TCP.

4. Сетевая адресация

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

Было создано и отвергнуто несколько возможных решений поставленной задачи. Мы могли бы использовать файловый сервер пользовательского уровня для представления сетевого пространства имен в виде файлового дерева Plan 9. Глобальная схема именования была реализована в других распределенных системах. Файловая иерархия обеспечивает пути к каталогам, которыми служат сетевые домены. Каждый каталог содержит файлы, которые являются именами машин в этом домене; примером может служить путь /net/name/usa/edu/mit/ai. Каждый файл машины содержит информацию типа IP адреса машины. Эта идея была отвергнута нами по нескольким причинам. Первая, трудно разрабатывать иерархию, охватывающую все представления различных сетевых схем адресации единым образом. Адресные строки Datakit и Ethernet не имеют ничего общего. Вторая причина, адрес машины часто составляет самую малую часть информации, требуемую для подключения к сервису на машине. К примеру, IP протоколы требуют символические имена сервисов для отображения в числовые номера порта, некоторые из которых привилегированные и следовательно, специальные. Информацию такого рода трудно представить в терминах операций над файлами. И, наконец, размер и количество представляемых сетей обременяют пользователя неприемлемо большим объемом информации об организации сетей и их связности. В таком случае представление ресурса как файл не уместно.

Если средства будут независимыми от сетей, сторонний сервер должен разрешать сетевые имена. Сервер на каждой машине, с локальным знанием, может выбрать лучшую сеть для любого конкретного расположения машины или сервиса. Поскольку сетевые устройства представляют общий интерфейс, то единственной операцией, которая отличается между сетями, является разрешение имен. Символическое имя должно переводиться в путь аналогичного файла устройства протокола и адресную ASCII строку для записи в ctl файл. Сервер соединений (connection server, CS) обеспечивает такой сервис.

4.1 Сетевая база данных

В большинстве систем сетевую информацию хранят несколько файлов наподобие /etc/hosts, /etc/networks, /etc/services, /etc/hosts.equiv, /etc/bootptab и /etc/named.d. Большую часть времени и усилий затрачивается на администрирование этих файлов и поддержку их взаимной последовательности. Программные средства пытаются автоматически создать один или более файлов из информации других файлов, но эксплуатация продолжает усложняться и иметь склонность к ошибкам.

Так как мы писали полностью новую системы, то могли свободно пробовать более простой метод. Одна база данных на распределенном сервере содержит всю информацию, требуемую для сетевого администрирования. Два текстовых файла включают основную базу данных: /lib/ndb/local содержит локально управляемую информацию и /lib/ndb/global содержит информацию, импортируемую из других мест. Файлы содержат комплекты пар атрибут/значение формы атр=значение, где атр и значение — текстовые строки. Системы описаны многострочными записями; строка-заголовок в левом поле начинает каждую запись предваренными нулем или более отступов парами атрибут/значение, которые определяют имена, адреса, свойства и т.п. Например, запись для нашего CPU сервера определена доменным именем, IP адресом, Ethernet адресом, Datakit адресом, загрузочным файлом и поддерживаемыми протоколами.

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

Записи базы данных также описывают отображение сервисных имен в номера портов для TCP, UDP и IL.

Все программы читают базу данных непосредственно, как результат, проблемы стабильности редки. Тем не менее, файлы базы данных могут увеличиваться в размерах. Наш глобальный файл, содержащий всю информацию об обеих Datakit и Internet системах в AT&T, имеет 43 тыс. строк. Для ускорения поиска, мы построили файлы хэш таблиц для каждого атрибута, поиск по которым производиться чаще других. Записи хэш файла указывают на записи в основных файлах. Каждый хэш файл содержит время модификации основного файла, так что мы можем избежать использования устаревших хэш таблиц. Поиски по атрибутам, которые не прохэшированы или чьи хэш таблицы устарели, все еще работают, они просто выполняются немного дольше.

4.2 Сервер соединений

В каждой системе процесс сервера соединений пользовательского уровня, CS, преобразует символические имена в адреса. Для преобразования имен, CS использует информацию о доступных сетях, сетевой базе данных и других серверах (вроде DNS). CS — это файловый сервер, обслуживающий всего-лишь один файл, /net/cs. Клиент записывает символическое имя в /net/cs, затем читает одну строку для каждого соответствующего места назначения, достижимого из этой системы. Строки представляют собой форму имя файла сообщение, где имя файла — это путь открываемого аналогичного файла для нового соединения и сообщение — это строка, запись который приводит к созданию соединения. Следующий пример иллюстрирует это. Программа ndb/csquery выводит приглашения для записи строк в /net/cs и выводит ответы.

CS обеспечивает преобразование мета-имен для выполнения сложных поисков. Специальное сетевое имя net выбирает любую сеть между источником и местом назначения, поддерживающую указанный сервис. Имя хоста в форме $атр — это имя атрибута в сетевой базе данных. Поиск в базе данных возвращает значение соответствующей пары атрибут/значение, наиболее близко связанной с хостом источником. Наиболее близко связанная пара описана в сетевой основе. К примеру, символическое имя tcp!$auth!rexauth заставляет CS выполнить поиск атрибута auth в записи базы данных для исходной системы, затем в ее подсети (если есть) и затем в ее сети.

Обычно CS получает информацию именования из файлов своей базы данных. Тем не менее для доменных имен CS сначала обращаться к другому процессу пользовательского уровня, серверу доменных имен (DNS). Если нет DNS, то CS полагается на свои собственные таблицы.

Подобно CS, сервер доменных имен является процессом пользовательского уровня, обслуживающий один файл, /net/dns. Клиент записывает запрос в форме доменное-имя тип, где тип — это ресурс сервиса доменного имени. DNS выполняет рекурсивный запрос через систему доменных имен Internet, представляя по одной строке на каждую найденную запись ресурса. Клиент читает /net/dns для извлечения записей. Также как и другие серверы доменных имен, DNS кеширует информацию, полученную из сети. DNS реализован как многопроцессовое приложение с совместным использованием памяти, отдельные процессы которого прослушивают сетевые и локальные запросы.

5. Библиотечные подпрограммы

Этот раздел посвящен устройствам протокола, здесь в деталях описывается создание и прием соединений через сеть. Эта работа довольно простая, но скучная. Библиотечные подпрограммы предназначены для облегчения работы программиста.

5.1 Подключение

Библиотечный вызов dial устанавливает соединение с удаленным местом назначения. Он возвращает дескриптор открытого файла для файла data в каталоге соединения.

dest Символическое имя/адрес места назначения
local Локальный адрес. Так как большинство сетей не поддерживают его, обычно установлен в нуль.
dir Указатель на буфер хранения путевого имени каталога протокола, представляющего соединение. Dial заполняет этот буфер, если указатель не нуль.
cfdp Указатель на файловый дескриптор для ctl файла соединения. Если указатель не нуль, dial открывает управляющий файл и выталкивает файловый дескриптор сюда.

Большинство программ вызывают dial с именем места назначения и всеми другими аргументами-нулями. Dial использует CS для преобразования символического имени во все возможные адреса назначений и пытается подключится к каждому из них. Указание специального имени net в сетевой части позволяет CS выбрать общую пару сеть/протокол для места назначения, на котором доступен запрашиваемый сервис. К примеру, система research.bell-labs.com имеет адрес nj/astro/research и IP адреса 135.104.117.5 и 129.11.4.1. Вызов

пытается подключиться к nj/astro/research!login в Datakit и обоих 135.104.117.5!513 и 129.11.4.1!513 через Internet.

Dial принимает адреса взамен символическим именам. Например, имена tcp!135.104.117.5!513 и tcp!research.bell-labs.com!login являются эквивалентными ссылками на одну машину.

5.2 Прослушивание

Для прослушивания входящих соединений используется четыре подпрограммы. Сначала программа объявляет (announce()) свое намерение приема соединений, затем выполняет прослушивание (listen()) вызовов и, наконец, принимает (accept()) или отвергает (reject()) их. Announce возвращает дескриптор открытого файла для ctl файла соединения и заполняет dir путем к каталогу протокола для объявления.

Addr — это объявленное символическое имя/адрес, если оно не содержит сервис, то объявление для всех сервисов выполняется неявно. Таким образом, любой может легко написать эквивалент программы inetd без необходимости в объявлении каждого отдельного сервиса. Объявление остается в силе, пока управляющий файл закрыт.

Listen возвращает дескриптор открытого файла для ctl файла и заполняет ldir информацией о пути к каталогу протокола для принятого соединения. Она приходит в dir из объявления.

Accept и reject вызываются с дескриптором управляющего файла и ldir, возвращенного вызовом listen. Некоторые сети вроде Datakit принимают также причину для отказа; IP сети игнорируют третий аргумент.

Приведенный ниже код реализует типичный TCP слушатель. Он объявляет себя, прослушивает соединения и порождает копию нового процесса для каждого из них. Новый процесс дублирует данные соединения, пока удаленный конец не закроет его. Символ «*» в имени означает, что объявление доступно для любого адреса, связанного с машиной, на которой запущена программа.

6. Пользовательский уровень

Связь между машинами Plan 9 реализуется почти исключительно посредством терминов сообщений 9P. При этом используется только два сервиса: cpu и exportfs. Сервис cpu аналогичен rlogin. Тем не менее, в отличие от эмуляции сеанса терминала через сеть, cpu создает процесс на удаленной машине, чье пространство имен эквивалентно пространству имен окна, в котором он был вызван. Exportfs — это сервер пользовательского уровня, который позволяет экспорт пространства имен из машины к машине через сеть. Он используется командой cpu для обслуживания файлов в пространстве имен терминала, когда они доступны из CPU сервера.

По соглашению, файловые системы протокола и драйверов устройств монтируются в каталог /net. Хотя пространство имен процесса позволяет пользователям конфигурировать произвольный вид системы, на практике их профайлы формируют стандартное пространство имен.

6.1 Exportfs

Exportfs запускается входящим сетевым вызовом. Слушатель (Plan 9 эквивалент inetd) перед запуском exportfs запускает профайл пользователя, запрашивающего сервис для создания пространства имен. После того как начальный протокол установил корень экспортируемого файлового дерева, удаленный процесс монтирует соединение, позволяя exportfs действовать в роли главного файлового сервера. Операции в импортируемом файловом дереве выполняются на удаленном сервере с возвращением результатов. Вследствие чего пространство имен удаленной машины экспортируется в локальное файловое дерево.

Команда import вызывает exportfs на удаленной машине, монтирует результат в локальное пространство имен и завершает работу. Для обслуживания монтирований не требуется локальный процесс; сообщения 9P генерируются драйвером монтирования ядра и отправляются непосредственно через сеть.

Exportfs должен быть многониточным, поскольку системные вызовы open, read и write могут вызывать блокировки. В Plan 9 отсутствует системный вызов select, но система позволяет совместное использование файловых дескрипторов, памяти и других ресурсов. Exportfs плюс конфигурируемое пространство имен реализуют совместное использование (другими словами, распределение) ресурсов между машинами. Это строительный блок для создания комплексных пространств имен, сформированных из многих машин.

Простота интерфейсов способствовала наивным пользователям эксплуатировать потенциал богато соединенной среды. При использовании этих средств шлюзование между сетями представляется довольно таки легкой задачей. К примеру, терминал с одним Datakit соединением может импортировать из сервера helix:

Команда import создает Datakit соединение с машиной helix, на которой запущен пример exportfs для представления /net. Import монтирует удаленный каталог /net после (опция -a команды import) существующего содержимого локального каталога /net. Каталог содержит союз локальных и удаленных содержимых /net. Локальные данные заменяются удаленными с тем же именем, так что сети в локальной машине выбраны с предпочтением удаленности. Тем не менее, уникальные данные в удаленном каталоге теперь доступны в локальном каталоге /net. Все сети, соединенные с helix, т.е. не только Datakit, теперь доступны с терминала. Эффект в пространстве имен показан в следующем примере:

6.2 Ftpfs

Мы решили реализовать наш интерфейс протокола FTP в виде файловой системы, а не традиционной команды. Наша команда ftpfs подключается к FTP порту удаленной системы, запрашивает логин и пароль, устанавливает образный режим и монтирует удаленную файловую систему в /n/ftp. Для уменьшения траффика, файлы и каталоги поддаются кешированию. Кеш обновляется всякий раз, когда создается файл. Ftpfs работает с TOPS-20, VMS и различными реализациями Unix как с удаленными системами.

7. Волоконные связи Cyclone

Файловые и CPU серверы соединены связями типа точка-точка высокой пропускной способности. Связь состоит из двух карт VME, соединенных парой оптических волокон. Картами VME используются процессоры Intel 960 33 MHz и волоконные трансмиттеры/ресиверы AMD TAXI для управления линиями на скорости 125 Mbit/сек. ПО в VME уменьшает время ожидания посредством копирования сообщений из системной памяти в волокно без промежуточной буферизации.

8. Эффективность

Мы проводили измерения времени ожидания и производительности чтения и записи байт между двумя процессорами на различных маршрутах. Тесты проводились на двух- и четырехпроцессорных CPU серверах SGI Power Series с процессорами MIPS 3000 25 MHz. За время ожидания принималось время задержки отправки байт от одного процесса к другому и обратно. Измерения производительности проводились с использовании 16 KB записей от одного процесса к другому.

Таблица 1 — эффективность
тест
производительность, MByte/сек
время ожидания, мили сек
pipes
8,15
0,255
IL/ether
1,02
1,42
URP/Datakit
0,22
1,75
Cyclone
3,2
0,375

9. Выводы

Представление всех ресурсов в виде файловых систем в связке с ASCII интерфейсом оказалось более мощным новацией, чем мы первоначально полагали. Ресурсы могут использоваться любым компьютером в наших сетях независимо от порядка байт или типа процессора. Сервер соединений обеспечивает изящные решения использования несвязанных средств в сетях. Пользователи успешно используют Plan 9 без знания топологии системы или сетей, используемых ими. Больше информации о 9P может быть найдено в разделе 5 руководства программиста Plan 9, первый том.

Литература

[1] R. Pike, D. Presotto, K. Thompson, H. Trickey, Plan 9 from Bell Labs, UKUUG Proc. of the Summer 1990 Conf., London, England, 1990.
[2] R. Needham, Names, in Distributed systems, S. Mullender, ed., Addison Wesley, 1989.
[3] D. Presotto, Multiprocessor Streams for Plan 9, UKUUG Proc. of the Summer 1990 Conf., London, England, 1990.
[4] R. Metcalfe, D. Boggs, C. Crane, E. Taf and J. Hupp, The Ethernet Local Network: Three reports, CSL-80-2, XEROX Palo Alto Research Center, February 1980.
[5] A. G. Fraser, Datakit - A Modular Network for Synchronous and Asynchronous Traffic, Proc. Int'l Conf. on Communication, Boston, June 1980.
[6] L. Peterson, RPC in the X-Kernel: Evaluating new Design Techniques, Proc. Twelfth Symp. on Op. Sys. Princ., Litchfield Park, AZ, December 1990.
[7] D. M. Ritchie, A Stream Input-Output System, AT&T Bell Laboratories Technical Journal, 68(8), October 1984.

Copyright © 2000 Lucent Technologies Inc. All rights reserved.
Copyright © 2003 Перевод Андрей С. Кухар.