Чтение онлайн

ЖАНРЫ

Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform

Кёртен Роб

Шрифт:

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

FILE* stream
).

Это естественно приводит нас к выводу о том, что администраторы ресурсов будут работать почти исключительно с вызовами дескриптор-ориентированных функций. Поскольку QNX/Neutrino — операционная система, организованная на основе обмена сообщениями, из этого следует, что вызовы POSIX-функций транслируются в сообщения, которые затем пересылаются администраторам ресурсов.

Именно эта трансляция вызовов POSIX в сообщения позволяет нам отвязать клиентуру от администраторов ресурсов. Все, что должен уметь делать администратор ресурса, — это обрабатывать

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

Поскольку взаимодействие между клиентурой и администраторами ресурсов основано на обмене сообщениями, имеет смысл делать этот «передаточный уровень» как можно «тоньше». Например, когда клиент выполняет функцию open и получает в ответ дескриптор файла, этот дескриптор фактически является идентификатором соединения! Данный идентификатор соединения (он же дескриптор файла) используется затем функциями клиентской Си-библиотеки (например, функцией read) при создании и отправке сообщения для администратора ресурсов.

Взгляд со стороны клиента

Мы уже намекнули, что ожидает клиент. Он ожидает интерфейс на основе файловых дескрипторов с применением стандартных функций POSIX.

В действительности «под колпаком» происходит еще кое-что.

Например, как в действительности клиент соединяется с соответствующим администратором ресурса? Что происходит в случае объединённых файловых систем (когда несколько файловых систем ответственны за то же самое пространство имен)? Как обрабатываются каталоги?

Поиск сервера

Первое, что делает клиент, — это вызывает open, чтобы получить дескриптор файла. (Заметьте, что если клиент вместо этого вызывает функцию более высокого уровня — например, fopen — утверждение остается справедливым, поскольку fopen в конечном счете вызывает open).

Реализация функции open в Си-библиотеке создает сообщение, которое затем пересылается администратору процессов (

procnto
).

Администратор процессов отвечает за поддержание информации о пространстве имен путей. Данная информация представляет собой древовидную структуру имен путей, с которой связаны дескрипторы узлов (node descriptors), идентификаторы процессов (process IDs), идентификаторы каналов (channel IDs) и обработчики (handles):

Пространство имен путей в QNX/Neutrino.

Отметьте, что на представленном выше рисунке и в последующих описаниях для обозначения администратора ресурса, который реализует файловую систему QNX4, я использовал имя
fsys-qnx4
. В действительности все немного сложнее, потому что драйверы файловой системы представляют собой группы связанных между собой динамических библиотек (DLL), так что никакой программы с именем
fsys-qnx4
на самом деле не существует; мы просто используем это имя в качестве «заполнителя» для компонента файловой системы.

Давайте предположим, что клиент вызывает функцию open:

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

Реализация функции open в клиентской Си-библиотеке создает сообщение и пересылает его администратору процессов. Это сообщение гласит: «Хочу открыть

/dev/ser1
. К кому мне обратиться по этому вопросу?»

Первая стадия разрешения имени.

Администратор процессов принимает запрос и просматривает дерево имен на предмет соответствия (давайте предположим здесь, что нам необходимо точное соответствие). Имя пути «

/dev/ser1
» вполне подойдет, и администратор процессов может ответить клиенту: «Нашел
/dev/ser1
.
За обработку отвечает канал 1 процесса 44 на узле 0, спроси его!»

Не забывайте: мы все еще в клиентском коде open!

Функция open создает другое сообщение и соединяется с указанным процессом (PID 44) на указанном узле (NID 0 означает локальный узел) по заданному каналу (CHID 1), помещая обработчик (handle) непосредственно в сообщение. Это воистину «сообщение установки соединения» — то самое сообщение которое клиентская функция open использует для установления связи с администратором ресурса (3 стадия на рисунке ниже) Когда администратор ресурса получает сообщение установки соединения, он анализирует его и проверяет на корректность. Например, вы могли бы попытаться применить операцию записи к администратору ресурса, который реализует файловую систему с доступом только для чтения — в этом случае вы бы получили обратно признак ошибки (в данном случае — EROFS). В нашем примере, однако, администратор последовательного порта смотрит на запрос (мы указали там O_WRONLY, что для последовательного порта абсолютно кошерно) и отвечает признаком EOK (4 стадия на рисунке ниже).

Сообщение _IO_CONNECT.

Затем, наконец, клиентская функция open возвращает клиенту корректный дескриптор файла.

На самом деле этот дескриптор файла представляет собой идентификатор соединения, который мы только что использовали для отправки сообщения администратору ресурса! Если бы администратор ресурса не ответил признаком EOK, мы бы сообщили клиенту, что произошла ошибка (open возвратила бы -1 и установила код ошибки в errno).

Поиск администратора процессов

Теперь, когда мы знаем основные этапы поиска конкретного администратора ресурса, осталось раскрыть тайну поиска администратора процесса, с которого все начинается. На самом деле все очень просто. По определению, администратору процессов соответствует дескриптор узла 0 (то есть текущий узел), идентификатором процесса 1 и идентификатор канала 1. Так что администратор процессов всегда идентифицируется триплетом ND/PID/CHID, равным 0/1/1.

Обработка каталогов

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

Предположим, у меня есть код, который делает следующее:

fp = fopen("/etc/passwd", "r");

Напомним, что функция fopen в конечном счете вызывает функцию open, так что реально мы имеем функцию open, запрашивающую имя пути

/etc/passwd
. Но такого имени на рисунке нет:

Пространство имен путей в QNX/Neutrino.

Однако, из рисунка видно, что модуль

fs-qnx4
зарегистрировал свою тройку ND/PID/CHID для имени пути «
/
». Хоть это и не показано на рисунке, файловая система
fs-qnx4
зарегистрировалась как «администратор каталога», сказав администратору процессов, что будет отвечать за «
/
» и все то, что расположено «ниже». «Администраторы устройств» (например, администратор последовательного порта) так не делают. Установив флаг каталога,
fs-qnx4
получает возможность обработать запрос для имени пути «
/etc/passwd
», потому что это имя начинается с «
/
», а значит, есть совпадение!

Поделиться с друзьями: