Менеджеры ресурсов

Фреймворк организации сервисов и стандартизованных интерфейсов драйверов устройств

Введение
Что такое менеджер ресурсов (администратор ресурсов)?
Назначение менеджера ресурсов
Типы менеджеров ресурсов
Менеджеры устройств
Менеджеры файловых систем
Взаимодействие через стандартные IPC
Архитектура менеджеров ресурсов
Типы сообщений
Разделяемая библиотека менеджера ресурсов
Автоматический вызов стандартных обработчиков сообщений
Функции open(), dup() и close()
Многопоточная обработка
Функции диспетчера
Составные сообщения
Второй уровень стандартной обработки сообщений

Введение

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

Менеджеры ресурсов, как правило, предназначены для обеспечения интерфейса к различным типам устройств: как к реальным физическим (например, последовательный порт, сетевая карта и жесткий диск), так и к виртуальным логическим (например, /dev/null, сетевая файловая система или псевдотерминал).

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

Что такое менеджер ресурсов (администратор ресурсов)?

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

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

Связь между менеджером ресурсов и клиентскими процессами, которые используют соответствующий ресурс, осуществляется посредством отображения в пространство имен менеджера процессов. В результате этой операции устанавливается соответствие символьным именем и серверным процессом. Менеджер ресурсов регистрирует свое имя в пространстве имен с помощью передачи менеджеру процессов сообщения о том, что он отвечает за обработку запросов к некоторой точке монтирования (или ниже нее, если речь идет о файловой системе). Это позволяет выполнить связывание сервиса с пространством имен, что перекликается с UNIX-концепцией всё есть файл.

Так, например, менеджер ресурсов devc-ser8250 может одновременно управлять последовательным портом и обслуживать клиентские запросы через префикс (символьное имя) /dev/ser1 пространства имён. Таким образом, запрос на передачу данных в последовательный порт выполняется с помощью открытия ресурса с именем /dev/ser1.

Назначение менеджера ресурсов

Выбор менеджера ресурсов в качестве интерфейса реализации системного сервиса имеет смысл по нескольким причинам:

Клиентский интерфейс (API) менеджера ресурсов является POSIX-совместимым
Всем Cи-программистам знакомы функции open(), read() и write(). Именно они используются для взаимодействия с сервером. Для управляющих воздействий, которые не входят в модель "чтение/запись", предусмотрена функция devctl(), хотя она и не является POSIX-совместимой.
Возможность сократить количество типов интерфейсов
Если имеется множество сервисов, разработка каждого из них в виде менеджера ресурсов позволяет сократить до минимума количество различных клиентских интерфейсов.
С менеджерами ресурсов можно взаимодействовать с помощью утилит командной строки
Стандартные утилиты командной строки используют те же интерфейсы, что и любая программа, написанная с использованием системной библиотеки. По этой причине они могут применяться для взаимодействия с менеджерами ресурсов.

Допустим, менеджер ресурсов регистрирует имя /dev/stats и при чтении из него возвращает текст с описанием статистики своего использования. Утилита cat по имени файла позволяет вывести эти сведения в stdout:
cat /dev/stats

Типы менеджеров ресурсов

Менеджеры ресурсов можно условно разделить на два типа:

Менеджеры устройств

Менеджеры ресурсов данного типа создают только одиночные записи в пространстве имен, каждая из которых представляет некоторое отдельное устройство. В менеджерах ресурсов этого типа, как правило, основная часть работы по предоставлению POSIX-интерфейса к устройству выполняется за счет библиотеки менеджеров ресурсов (интегрирована в системную библиотеку).

Например, драйвер последовательного порта регистрирует такие имена, как /dev/ser1 и /dev/ser2. Когда пользователь применяет команду ls -l /dev, библиотека неявно выполняет обработку ответов на возникающие сообщения _IO_STAT (их посылает утилита ls). По этой причине разработчик последовательного порта может сосредоточиться на деталях управления оборудованием и меньше отвлекаться на интерфейсные вопросы.

Менеджеры файловых систем

Менеджеры ресурсов данного типа регистрируют в пространстве имен точку монтирования. Она представляет собой часть пути, ниже которой располагаются ресурсы, о которых известно лишь менеджеру ресурсов. Например, если менеджер ресурсов обслуживает точку монтирования /mount, то при проверке пути /mount/home/user1:

/mount/
Определяет саму точку монтирования, которую контролирует менеджер процессов.
home/user1
Определяет ресурс, которым управляет менеджер ресурсов.

Несколько примеров менеджеров ресурсов файловых систем:

Взаимодействие через стандартные IPC

Как только менеджер ресурсов зарегистрирует свой префикс, он будет получать сообщения при каждой попытке клиента выполнить для этого путевого имени вызов open(), read(), write() и т.п. Например, после того как devc-ser8250 создаст префикс /dev/ser1, и клиентская программа выполнит следующий код:

fd = open( "/dev/ser1", O_RDONLY );

системная библиотека на стороне клиента сгенерирует сообщение с типом _IO_CONNECT и передаст его менеджеру ресурсов. Через некоторое время клиентская программа может выполнить команду:

read( fd, buf, BUFSIZ );

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


Note: Все драйверы устройств и файловые системы в ЗОСРВ «Нейтрино» реализуются в виде менеджеров ресурсов. Это делает их в общем случае прозрачно доступными по сети. Кроме того, это означает, что пользовательский менеджер ресурсов потенциально обладает такой же функциональностью, что и собственного драйвера и сервисы операционной системы.

Архитектура менеджеров ресурсов

Представить архитектуру менеджеров ресурсов можно следующим псевдокодом:

1. Инициализация структуры dispatch
2. Регистрация префикса в пространстве имен менеджера процессов procnto
WHILE True
3. Получение сообщения
SWITCH 16-битный_тип_сообщения
CASE _IO_CONNECT:
Вызов обработчика io_open()
CASE _IO_READ:
Вызов обработчика io_read()
CASE _IO_WRITE:
Вызов обработчика io_write()
CASE *:
Обработка других типов сообщений и выполнение соответствующих операций
ENDSWITCH
ENDWHILE

Таким образом, архитектура включает три ключевых компонента:

  1. Канал, создание которого позволяет клиентским процессам соединиться с менеджером ресурсов и отправлять ему сообщения.

  2. Регистрация одного или нескольких префиксов в пространстве имен для того, чтобы менеджер процессов мог переадресовывать поступающие запросы на разрешение имен к соответствующему серверу (менеджеру процессов).

  3. Получение и обработка сообщений.

Конструкция вида switch ... case требуется для каждого менеджера ресурсов. Однако, системная библиотека поставляется набор библиотечных функций, которые автоматизируют создание и обслуживание таких конструкций. Включая обработчики сообщений разных типов со стандартизованной логикой (обычно это возвращение корректного кода ошибки).

Типы сообщений

Существует две категории сообщений, предназначенных для менеджера ресурсов:

Сообщения установления соединения генерируются клиентом при вызове библиотечных функций, использующих символьное имя префикса (например, сообщение с типом _IO_CONNECT генерируется функцией open() системной библиотеки). Обработка таких сообщений может включать проверку разрешений (имеет ли клиент право на доступ к данному устройству) и настройку контекста для данного запроса. Данный контекст называется блок управления открытым контекстом (англ. OCB, Open Control Block).

Сообщения ввода/вывода используют OCB, создаваемый между клиентом и менеджером ресурсов, для выполнения последующих операций ввода/вывода (например, обработку сообщений с типом _IO_READ, генерируемых клиентским вызовом read()).

Такое разделение имеет веское обоснование. Было бы крайне неэффективно передавать полное символьное имя префикса с каждым сообщением, следующим после установления соединения с префиксом (или ресурсом, если речь идет о менеджере файловой системы). Обработчик io_open() также должен выполнять некоторые операции (например, проверку разрешений) лишь однократно, а не с каждым поступающим сообщением. Допустим, что при обработке запроса от read() клиенту переданы 4096 байт данных из 20 Мбайт общего объема данных в файле, ассоциированном с префиксом. В этом случае менеджер ресурсов при последовательных вызовах функции read() со стороны клиента должен иметь доступ к некоторой контекстной информации, ассоциированной с сессией клиента. Например, для хранения сведений о позиции чтения/записи в файле. Эту задачу позволяет решить OCB.

Разделяемая библиотека менеджера ресурсов

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

Рассмотрим основные подходы к автоматизации, заложенные в библиотеку resmgr.

Автоматический вызов стандартных обработчиков сообщений

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

Предусмотрено два уровня действий по умолчанию:

Функции open(), dup() и close()

Другой удобной возможностью разделяемой библиотеки resmgr является автоматическая обработка сообщений dup().

Допустим, что клиентская программа выполнила следующий код:

fd = open( "/dev/device", O RDONLY );
...
fd2 = dup( fd );
...
fd3 = dup( fd );
...
close( fd3 );
...
close( fd2 );
...
close( fd );

В этом случае будет сгенерировано сообщение _IO_CONNECT для вызова open() и два сообщения _IO_DUP для двух вызовов dup(). После этого, когда клиент выполнит вызовы close(), он сгенерирует три сообщения _IO_CLOSE.

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

Библиотека предоставляет стандартные обработчики, которые отслеживают вызовы open(), dup() и close() и выполняют работу только по последней операции закрытия.

Многопоточная обработка

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

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

Функции диспетчера

Предусмотрен набор функций вида dispatch_*(), которые:

Составные сообщения

Для сохранения пропускной способности сети и поддержки атомарных операций микроядро поддерживает составные сообщения. Составное (комбинированное) сообщение состоит из нескольких буферов, объединенных и передаваемых как единое целое.

Например, функция readblock() позволяет потоку атомарно выполнять операции lseek() и read(). В клиентской библиотеке это достигается путем объединения сообщений _IO_LSEEK и _IO_READ в одно. При получении такого сообщения библиотека обработает оба поступивших сообщения, превращая обработку запросов от клиентской функции readblock() в атомарную на уровне IPC.

Составные сообщения также полезны для функции stat(). Вызов stat() может быть реализован как комбинированная отправка сообщения в составе функций open(), fstat() и close(). Вместо создания нескольких отдельных сообщений для каждой из этих функций, библиотека объединяет их в одно составное сообщение, что повышает общую производительность коммуникаций (особенно при их сетевом характере). Также это упрощает сам менеджер ресурсов, поскольку в этом случае для обработки stat() не требуется отдельного сообщения установления соединения.

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

Второй уровень стандартной обработки сообщений

Поскольку большое число сообщений, принимаемых менеджером ресурсов, связано с общим набором атрибутов, в ОС предусмотрен еще один уровень стандартной обработки сообщений. Этот уровень связан с библиотекой функций iofunc_*() и позволяет менеджеру ресурсов автоматически обрабатывать такие функции, как stat(), chmod(), chown(), lseek() и т.п. Еще одним преимуществом является то, что обработчики iofunc_*() реализуют POSIX-семантику при обработке сообщений, что так же уменьшает работу программиста.

Необходимо рассмотреть три основных структуры, которыми управляет менеджер ресурсов:

Их взаимосвязь представлена на схеме:

5_1.png
Рисунок 1. Структуры, которыми управляет менеджер ресурсов

Первая структура уже рассматривалась ранее в параграфе Типы сообщений. Она содержит данные, ассоциированные с конкретной сессией пользователя, и создается при каждом новом открытии префикса.

Поскольку менеджер ресурсов может обслуживать более чем один префикс (например, devc-ser8250 может отвечать за /dev/ser1, /dev/ser2, /dev/ser3 и т.д.), атрибутивная запись является уникальным идентификатором префикса. Она содержит данные, ассоциированные с каждым конкретным устройством. В частности, она включает идентификатор владельца и его группы, время последнего изменения и т.п.

Третья структура используется в основном лишь менеджерами файловых систем — она содержит общие для всего монтируемого устройства данные.

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

5_2.png
Рисунок 2. Открытие нескольких устройств множеством клиентов

По умолчанию функции iofunc_*() работают на основе допущения, что в структуры OCB и атрибутивной записи не инкапсулированы в пользовательские типы данных. Это предположение является безопасным по следующим причинам:

Как уже было отмечено, стандартные структуры должны быть заданы первым полем в типах данных, в которые их необходимо инкапсулировать. Это позволяет применять к ним функций iofunc_*(), передавая указатели на пользовательские типы данных. рассмотрим это на примере атрибутивной записи (понимая, что аналогичную процедуру можно произвести и для OCB):

5_3.png
Рисунок 3. Инкапсуляция стандартных структур

Расширение атрибутивной записи позволяет иметь во всех обработчиках сообщений доступ к данным, уникальным для конкретного префикса (файла устройства). В расширенном OCB можно хранить данные, ассоциированные с пользовательской сессией. Доступ к обоим видам данных из обработчиков производится без осуществления поиска, что существенно экономит вычислительные ресурсы.

Системная библиотека содержит стандартные обработчики iofunc_*() для следующих клиентских вызовов:

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




Предыдущий раздел: перейти