8. Администраторы ресурсов
Введение
ОС QNX Neutrino позволяет создавать пользовательские
процессы, работающие в качестве администраторов ресурсов (resource managers), которые можно динамически
запускать и останавливать. Это дает системе чрезвычайную
гибкость, позволяет минимизировать объем памяти, занимаемый
конечной системой, и взаимодействовать с широким набором
устройств, применяемых во встраиваемой системе.
Администраторы
ресурсов, как правило, предназначены для обеспечения
интерфейса с различными типами устройств, в том числе
физическими устройствами (например, последовательными и
параллельными портами, сетевыми платами, жесткими дисками) и
виртуальными устройствами (например, /dev/null, сетевой файловой системой,
псевдотерминалом).
В других
операционных системах эти функции обычно обеспечиваются драйверами устройств. Однако в отличие от драйверов
устройств, администраторы ресурсов не требуют специальных
механизмов для взаимодействия с ядром, а работают как любая
другая пользовательская программа.
Что такое
администратор ресурсов?
Поскольку ОС QNX Neutrino является распределенной,
микроядерной операционной системой, в которой почти все
внеядерные функции обеспечиваются той или иной конфигурацией
инсталлируемых программ, для управления взаимодействием между
клиентской программой и администратором ресурсов требуется
простой и эффективный интерфейс. В ОС QNX Neutrino все функции
администратора ресурсов документированы, и между ядром и
администратором ресурсов не существует какого-либо "тайного"
или особенного интерфейса.
На самом деле,
администратор ресурсов представляет собой серверную программу
пользовательского уровня, которая принимает сообщения от
других программ и при необходимости может взаимодействовать с
оборудованием. При этом производительность и гибкость служб
межзадачного взаимодействия в ОС QNX Neutrino позволяют
реализовать администратор ресурсов вне ОС.
Связь между
администратором ресурсов и клиентскими программами, которые
используют соответствующий ресурс, осуществляется посредством
гибкого механизма, называемого отображением пространства имен путей
(pathname space mapping).
В результате
отображения пространства имен путей устанавливается
соответствие между путевым именем и администратором ресурсов.
Администратор ресурсов производит данное отображение
пространства имен путей с помощью передачи администратору
процессов сообщения о том, что он отвечает за обработку
запросов к некоторой точке монтирования (или ниже нее, если речь идет о файловой
системе). Это позволяет администратору процессов выполнить
связывание между службами (например, функциями,
обеспечиваемыми администраторами ресурсов) и именами путей.
Например,
администратор ресурсов devc-ser* может быть назначен для управления последовательным
портом, однако в пространстве имен путей физический ресурс
может быть назван как /dev/ser1.
Запрос служб последовательного порта, как правило, выполняется
с помощью его открытия, в данном случае обращения к
физическому ресурсу с именем /dev/ser1.
Зачем писать
администратор ресурсов?
Написание администратора ресурсов имеет смысл по
нескольким причинам.
- Клиентский
программный интерфейс (API) является POSIX-совместимым.
Программный
интерфейс, предназначенный для взаимодействия с
администратором ресурсов, по большей части является
POSIX-совместимым. Все Cи-программисты знакомы с такими
функциями, как open(), read() и write(). В результате затраты на дополнительное обучение,
так же как и на документирование интерфейса для сервера,
становятся минимальными.
- Возможность
сократить количество типов интерфейсов.
Если
имеется множество серверных процессов, написание каждого
сервера в виде администратора ресурсов позволяет сократить
до минимума количество различных интерфейсов, необходимых
для клиентских процессов.
Например,
есть команда программистов, которые разрабатывают
некоторое приложение, и каждый из этих программистов
занимается написанием одного или нескольких серверов для
этого приложения. Программисты могут быть сотрудниками
одной компании, или же относиться к другим компаниям, с
которыми осуществляется сотрудничество и которые
привлечены к разработке дополнительного оборудования
модульной платформы.
Если
серверы являются администраторами ресурсов, тогда в
качестве интерфейса ко всем этим серверам служат
POSIX-функции – open(), read(), write() и т. д. Для управляющих
сообщений, которые не входят в модель "чтение/запись",
предусмотрена функция devctl() (хотя эта функция не является POSIX-совместимой).
- С
администраторами ресурсов могут взаимодействовать утилиты
командной строки.
Поскольку в
качестве программного интерфейса для взаимодействия с
администратором ресурсов используется набор
POSIX-совместимых функций, и поскольку этот программный
интерфейс используется стандартными POSIX-утилитами,
утилиты командной строки могут применяться для
взаимодействия с администратором ресурсов.
Например,
встраиваемый модуль поддержки протокола TCP/IP содержит
программный код, реализующий администратор ресурсов и
регистрирующий имя /proc/ipstats. Если открыть это имя и
прочитать данные из него, администратор ресурсов возвратит
текст с описанием статистики по IP-протоколу.
Утилита cat берет имя файла, открывает файл, читает из него
данные и выдает их в стандартный поток вывода (как правило,
это дисплей). Таким образом, можно вызвать следующую команду:
cat /proc/my_stats
и ресурс
менеджер выдаст соответствующую статистику.
Также можно
использовать утилиты командной строки для драйвера
робота-манипулятора. Драйвер может зарегистрировать следующее
имя: /dev/robot/arm/angle, и в результате любая
информация, передаваемая на это устройство, будет
интерпретироваться как значение угла, на который нужно
установить манипулятор. Для того чтобы протестировать этот
драйвер из командной строки, можно набрать следующую команду:
echo 87 >/dev/robot/arm/angle
Утилита echo открывает путь /dev/robot/arm/angle и записывает туда символьную цепочку ("87").
Драйвер обрабатывает запись и устанавливает манипулятор под
углом 87 градусов. Следует отметить, что данное
тестирование было выполнено без помощи специальной тестирующей
программы.
Другой пример:
имена типа /dev/robot/registers/r1, r2....
При чтении с этих имен выдается содержание соответствующих
регистров. При записи на эти имена устанавливаются заданные
значения в соответствующие регистры.
Даже если все
остальное межзадачное взаимодействие осуществляется
посредством какого-либо не POSIX-совместимого программного
интерфейса, один поток все же имеет смысл реализовать в виде
администратора ресурсов для того, чтобы обрабатывать описанные
ранее запросы на чтение и запись.
Типы
администраторов ресурсов
В зависимости от объема реализации POSIX-совместимой
файловой системы, который выполняется для своего клиента,
администраторы ресурсов можно разделить на два типа:
- файловые
администраторы ресурсов (device resource managers);
- каталоговые
администраторы ресурсов (filesystem resource managers).
Файловые
администраторы ресурсов
Администраторы
ресурсов файлового типа создают только однофайловые записи в
файловой системе, каждая из которых регистрируется
администратором процессов. Каждое имя обычно представляет
только одно устройство. В администраторах ресурсов этого типа,
как правило, основная часть работы по предоставлению
POSIX-интерфейса к устройству выполняется за счет библиотеки
администратора ресурсов.
Например,
драйвер последовательного порта регистрирует такие имена
регистров, как /dev/ser1 и /dev/ser2. Когда
пользователь применяет команду ls -l /dev, библиотека сама
выполняет необходимую обработку для передачи соответствующих
ответов на возникающие сообщения _IO_STAT, поэтому человек,
который пишет драйвер последовательного порта, может
сосредоточиться на деталях управления оборудованием
последовательного порта.
Каталоговые
администраторы ресурсов
Администраторы
ресурсов каталогового типа регистрируют точку монтирования посредством администратора
процессов. Точка монтирования представляет собой часть пути,
зарегистрированного администратором процессов. Управление
остальными частями пути выполняется администратором ресурсов
каталогового типа. Например, если администратор ресурсов
каталогового типа присоединяет точку монтирования к /mount, то при проверке пути /mount/home/thomasf:
- /mount/ — определяет точку
монтирования, которой управляет администратор процессов;
- home/thomasf — определяет остальную
часть пути, которая должна управляться администратором
ресурсов каталогового типа.
Ниже приведено
несколько примеров использования администраторов ресурсов
каталогового типа:
-
процесс для управления "почтовыми ящиками",
который регистрирует имя /mailboxes и управляет каждым "почтовым ящиком", существующим
в виде каталога, и файлами, которые фактически содержат
сообщения.
Обмен информацией
посредством механизма межзадачного взаимодействия ОС QNX
Neutrino
Как только администратор ресурсов установил свой
префикс путевого имени, он начинает получать сообщения при
каждой попытке клиентской программы сделать по этому путевому
имени вызов open(), read(), write() и т. д. Например,
после того как администратор ресурсов devc-ser* получит путевое имя /dev/ser1, и
клиентская программа выполнит следующую команду:
fd = open ("/dev/ser1", O_RDONLY);
клиентская
Си-библиотека сгенерирует сообщение io_open и затем передаст его
администратору ресурсов посредством механизма межзадачного
взаимодействия.
Через некоторое
время, после того как клиентская программа выполнит команду:
read (fd, buf, BUFSIZ);
клиентская
библиотека Си сгенерирует сообщение io_read и затем передаст его
администратору ресурсов.
Главным здесь
является то, что все связи между клиентской
программой и администратором ресурсов осуществляются с помощью
механизма
межзадачного взаимодействия ОС QNX Neutrino, что дает уникальные
преимущества.
- Четко
определенный интерфейс для приложений. В среде разработки
это позволяет ясно отделить реализацию клиентской стороны
от реализации администратора ресурсов.
- Простой
интерфейс для администратора ресурсов. Поскольку все связи
с администратором ресурсов происходят только посредством
механизма межзадачного взаимодействия, без каких-либо
специальных обработчиков или интерфейсов ОС, разработчик
администратора ресурсов может сосредоточиться на текущей
задаче и не беспокоиться о каких-либо особых требованиях,
которые встречаются в других операционных системах.
- Сетевая
прозрачность. Так как основополагающий механизм
межзадачного взаимодействия ОС QNX Neutrino по своей сути
является распределенным и не требует от клиента или
сервера (администратора ресурсов) дополнительных средств
обеспечения сетевого распределения, программы могут
получать прозрачный доступ к ресурсам на других узлах,
даже не зная о том, что при этом выполняется запрос по
сети.
Замечание
Все драйверы
устройств и файловые системы в ОС QNX Neutrino реализуются как
администраторы ресурсов. Это означает, что любой администратор
ресурсов, написанный пользователем, может иметь те же
функциональные возможности, которые есть у "собственного"
драйвера устройств или файловой системы ОС QNX Neutrino.
Например,
файловые системы FTP. Здесь администратор ресурсов получает
часть пространства путевых имен (например, /ftp) и разрешает пользователям применять команду cd для доступа к FTP-сайтам. Например, при вводе
команды cd
/ftp/rtfm.mit.edu/pub произойдет соединение с сайтом rtfm.mit.edu и переход в каталог /pub. После
перехода в этот каталог пользователь получает возможность
открывать, редактировать или копировать файлы.
Другим примером
пользовательского администратора ресурсов являются
специализированные файловые системы (application-specific
filesystems). Для приложения, основанного на использовании
хранящихся на жестком диске файлов, можно разработать
специализированную файловую систему, которая позволит повысить
производительность работы такого приложения.
Возможности
разработки таких специализированных администраторов ресурсов
ограничиваются только воображением программиста.
Архитектура администратора
ресурсов
Схематичная
структура администратора ресурсов:
initialize the dispatch interface
register the
pathname with the process manager
DO forever
receive a message
SWITCH on the type
of message
CASE io open:
perform io open
processing
ENDCASE
CASE io read:
perform io read
processing
ENDCASE
CASE io write:
perform io write
processing
ENDCASE
. // и т.д. - обработка
всех других сообщений
. // и выполнение
соответствующих операций
ENDSWITCH
ENDDO
Таким образом, архитектура администраторов ресурсов
состоит из трех частей:
1. Создание
канала, с помощью которого клиентская программа может
соединиться с администратором ресурсов для передачи
сообщения.
2. Регистрация
администратором процессов одного или нескольких путевых имен
для того, чтобы администратор ресурсов мог обрабатывать
запросы на открытие этих имен и передавать их администратору
процессов.
3. Получение
и обработка сообщений.
Данная
конструкция по обработке сообщений требуется для каждого
администратора ресурсов без исключения. В ОС QNX Neutrino
поставляется набор удобных библиотечных функций для реализации
этой структуры (а также других базовых структур).
Типы сообщений
С точки зрения архитектуры, существует две категории
сообщений, предназначенных для администратора ресурсов:
- сообщения
установления соединения (connect messages);
- сообщения
ввода/вывода (I/O messages).
Сообщения
установления соединения генерируются клиентом для выполнения
операции с использованием путевого имени (например, сообщение
io_open). Генерация сообщений этой
категории может включать выполнение таких операций, как
проверка разрешений (имеет ли клиент разрешение на открытие
данного устройства) и установку контекста для данного запроса.
Сообщения
ввода/вывода используют контекст, создаваемый между клиентом и
администратором ресурсов, для выполнения соответствующих
операций ввода/вывода (например, io_read).
Такой подход
можно считать довольно разумным, например, потому, что было бы
неэффективно передавать полное путевое имя на каждый вызов read(). Обработчик io_open также может выполнять
какие-нибудь задачи (например, проверку разрешений)
однократно, а не с каждым сообщением ввода/вывода. Кроме того,
когда функция read() уже прочитала 4096 байт
данных из дискового файла, там могут остаться еще
20 Мбайт непрочитанных данных, поэтому для функции read() нужна некоторая контекстная
информация, сообщающая позицию чтения.
Разделяемая
библиотека администратора ресурсов
При разработке специализированной встраиваемой
системы часть сил может быть потрачена на написание
администратора ресурсов, если у вас нет готового драйвера для
нужного аппаратного компонента системы.
Разделяемая
библиотека администратора ресурсов в ОС QNX Neutrino упрощает
эту задачу.
Автоматическая
обработка сообщений по умолчанию
Если какие-либо
функции не нужны для администратора ресурсов по тем или иным
причинам (например, в случае, если аналогово-цифровой
преобразователь не поддерживает функцию lseek() или не требует ее), разделяемая
библиотека может выполнять действия по умолчанию.
Предусмотрено
два уровня действий по умолчанию.
- Первый
уровень просто возвращает ENOSYS клиентскому приложению и
сообщает о том, что данная функция не поддерживается.
- Второй
уровень (т. е. разделяемая библиотека iofunc_*()) позволяет администратору
ресурсов автоматически обрабатывать различные функции.
Более подробно
о действиях по умолчанию см. в разделе "Второй уровень обработки сообщений
по умолчанию" далее в этой главе.
Функции open(), dup() и close()
Другой удобной
службой, предоставляемой разделяемой библиотекой
администратора ресурсов, является автоматическая обработка
сообщений dup().
Например,
клиентская программа выполнила программный код, который
заканчивается следующим образом:
fd = open
("/dev/device", O RDONLY);
...
fd2 = dup (fd);
...
fd3 = dup (fd);
...
close (fd3);
...
close (fd2);
...
close (fd);
Клиент сгенерирует сообщение io_open для первого вызова open() и затем два сообщения io_dup для двух вызовов dup(). После этого, когда клиент
выполнит вызовы close(), он сгенерирует три сообщения io_close.
Так как функции
dup() генерируют дубликаты файловых
дескрипторов, новая контекстная информация не будет выделена
для каждого из них. Так как выделение контекстной информации
для каждого вызова dup() не происходит, освобождение памяти каждым сообщением io_close также не требуется. (В противном случае,
первая же операция закрытия стерла бы контекст.)
Разделяемая
библиотека администратора ресурсов предоставляет обработчики
по умолчанию, которые регистрируют сообщения open(), dup() и close() и выполняют работу только по
последней операции закрытия (т. е. третье сообщение io_close в приведенном примере).
Многопоточная
обработка
Одной из
отличительных черт ОС QNX Neutrino является возможность
применять потоки
(threads).
Посредством использования множества потоков работу
администратора ресурсов можно организовать таким образом,
чтобы несколько потоков одновременно ожидали сообщения и затем
одновременно обрабатывали их.
Такое
управление потоками является еще одной удобной функцией,
которую предоставляет разделяемая библиотека администратора
ресурсов. Эта библиотека не только следит за количеством
созданных и ожидающих потоков, но также и за тем, чтобы это
количество было оптимальным.
Функции
диспетчеризации
В ОС QNX
Neutrino предусмотрен набор функций вида dispatch_*(), которые:
- позволяют
создать общую точку блокировки для администраторов и
клиентов, которые должны поддерживать множество типов
сообщений (например, можно сделать так, чтобы
администратор ресурсов обрабатывал сообщения только
заданного типа);
- обеспечивают
гибкий интерфейс для тех типов сообщений, которые не
связаны с администратором ресурсов (для быстрой обработки
локальных сообщений и импульсов);
- отделяют
код блокировки и код обработчика от потоков, благодаря
чему можно реализовать цикл обработки событий в основном
программном коде администратора ресурсов. Подобное
разделение также позволяет упростить отладку, так как
можно помещать точки останова (breakpoint) между функцией
блокировки и функцией-обработчиком.
Более подробную
информацию можно найти в главе, посвященной написанию
администратора ресурсов, в "Руководстве программиста"
(Programmer's Guide).
Составные сообщения
Для сохранения
пропускной способности сети и поддержки атомарных операций в
ОС QNX Neutrino поддерживаются составные сообщения (combine messages). Составное сообщение
генерируется Си-библиотекой клиента и состоит из нескольких
сообщений ввода/вывода и/или соединения, объединенных в одно.
Например,
функция readblock() позволяет потоку автоматически
выполнять операции lseek() и read(). В клиентской библиотеке это осуществляется с помощью
объединения сообщений io_lseek и io_read. При получении этого составного
сообщения разделяемая библиотека администратора ресурсов
обработает оба сообщения, входящие в него (io_lseek и io_read), и таким образом работа функции readblock() будет сделана атомарной.
Составные
сообщения также полезны для функции stat(). Вызов stat()
может быть реализован в клиентской библиотеке посредством
функций open(), fstat() и close(). Вместо генерирования трех
сообщений для каждой из этих функций, библиотека объединяет их
в одно составное сообщение, что повышает производительность
(особенно при использовании сети), а также упрощает
администратор ресурсов, поскольку в этом случае для него не
требуется функция установления соединения для обработки вызова
stat().
Разделяемая
библиотека администратора ресурсов самостоятельно выполняет
разделение составного сообщения на составные компоненты и
передачу этих компонентов различным функциям для обработки.
Это, опять же, позволяет минимизировать усилия, связанные с
написанием администратора ресурсов.
Второй уровень
обработки сообщений по умолчанию
Поскольку
большое число сообщений, принимаемых администратором ресурсов,
касается некоторого общего набора атрибутов, в ОС QNX Neutrino
предусмотрен еще один уровень обработки сообщений по
умолчанию. Этот второй уровень, называемый разделяемой
библиотекой iofunc_*(), позволяет администратору
ресурсов автоматически обрабатывать такие функции, как
stat(), chmod(), chown(), lseek() и так далее без необходимости писать
дополнительный код. Еще одним преимуществом является то, что
обработчики iofunc_*() реализуют POSIX-семантику для
сообщений, а это опять же уменьшает работу программиста.
Необходимо
рассмотреть три основных структуры (рис. 8.1):
Рис. 8.1. Администратор ресурсов управляет тремя
структурами данных
Первая структура данных (контекст) уже была рассмотрена
в разделе "Типы
сообщений".
Она содержит данные, которые используются при каждом
открытии, — например, текущая позиция в файле (смещение lseek()).
Администратор
ресурсов может управлять более чем одним устройством
(например, devc-ser* может отвечать за /dev/ser1, /dev/ser2, /dev/ser3 и т. д.), поэтому атрибутная запись содержит данные по каждому
отдельному устройству. В частности, она включает такие
элементы, как пользовательский и групповой идентификатор
владельца устройства, время последнего изменения
и т. д.
Для
администраторов файловых систем (блочных устройств
ввода/вывода) используется еще одна структура — это запись точки монтирования, которая содержит элементы
данных, имеющих глобальных характер по отношению к
монтируемому устройству.
После того как
несколько клиентских программ откроют различные устройства,
расположенные на некотором ресурсе, описанные ранее структуры
данных могут выглядеть следующим образом — рис. 8.2.

Рис. 8.2. Открытие различных устройств множеством
клиентов
Используемые по умолчанию функции iofunc_*() работают на основе того
определения, которое программист установил по умолчанию для
контекста и атрибутной записи. Такой порядок является
оптимальным по следующим причинам:
- контексты
и атрибутные записи содержат достаточное количество
информации для большинства приложений;
- если
структуры, принятые по умолчанию, не содержат необходимой
информации, их можно инкапсулировать в определенные вами
структуры.
По определению
структуры, принятые по умолчанию, должны быть первыми
элементами соответствующих суперструктур, что обеспечивает для
функций iofunc_*(), принятых по умолчанию, простой
и удобный доступ к необходимым базовым элементам
(рис. 8.3).
Рис. 8.3. Инкапсуляция
Библиотека по
умолчанию содержит следующие обработчики вызовов iofunc_*():
chmod()
chown()
close()
devctl()
fpathconf()
fseek()
fstat()
lock()
lseek()
mmap()
open()
pathconf()
stat()
utime()
Резюме
Благодаря поддержке отображения в пространство имен
путей, наличию четкого интерфейса администраторов ресурсов, а
также набора библиотек для реализации основных функций
администраторов ресурсов, ОС QNX Neutrino обеспечивает
беспрецедентную гибкость и простоту в разработке "драйверов"
для нового оборудования, что имеет решающее значение для
многих встраиваемых систем.
Для получения
более подробной информации о разработке администраторов
ресурсов см. главу «Администраторы ресурсов» учебного пособия
Р. Кртена «Введение в QNX Neutrino. Руководство для разработчиков приложений
реального времени».
5
Их посылает утилита ls. — Прим. научн. ред.
6
Имеется в виду блок управления открытым контекстом (184ОСВ). — Прим. научн. ред.