SWD Software Ltd. - официальный дистрибьютор QNX на территории России и стран бывшего СССР Операционная система реального времени QNX
Инструменты для создания надёжных встраиваемых систем и
интеллектуальных устройств любой сложности
QNX Software Systems - разработчик встраиваемой операционной системы QNX
  Стандартная версия

Пространство имен ввода/вывода

Эта глава охватывает следующие темы:

Введение

Пространство имен ввода/вывода

Ресурсы ввода/вывода (files/images/literature/qnx4/O, от английского Input/Output) в QNX не встроены внутрь Микроядра. Процессы обслуживающие ввод/вывод запускаются динамически во время работы системы. Так как файловая система QNX является необязательным элементом, то пространство путей (полных имен файлов и каталогов) не встроено внутрь файловой системы, в отличие от большинства монолитных систем.

Префиксы и области полномочий

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

Разборка имен путей

Префиксы менеджера ввода/вывода

Когда процесс открывает файл, то полное имя файла сопоставляется с деревом префиксов с тем, чтобы отправить запрос open() к соответствующему менеджеру ресурсов ввода/вывода. Например, Менеджер устройств (Dev) обычно регистрирует префикс /dev. Если процесс вызывает open() с аргументом /dev/xxx, то совпадет префикс /dev и open() будет направлен к Dev (его владельцу).

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

/ дисковая файловая система (Fsys)
/dev система символьных устройств (Dev)
/dev/hd0 дисковый том без разделов (Fsys)

Менеджер файловой системы зарегистрировал два префикса, один для смонтированной файловой системы QNX (т.е. /) и один для блок-ориентированного файла, который представляет весь физический жесткий диск (т.е. /dev/hd0). Менеджер устройств зарегистрировал единственный префикс. Следующая таблица иллюстрирует правило наиболее длинного совпадения при разборе имен путей.

Имя пути: Совпадает:Относится к:
/dev/con1 /dev Dev
/dev/hd0 /dev/hd0 Fsys
/usr/dtdodge/test / Fsys

Дерево префиксов хранится как список префиксов, разделенных двоеточиями, следующим образом:

prefix=pid,unit:prefix=pid,unit:prefix=pid,unit  

где pid - это ID процесса менеджера ресурсов files/images/literature/qnx4/O, а unit - это один символ, используемый менеджером для нумерации префиксов, которыми он владеет. В приведенном выше примере, если Fsys был бы процессом 3 и Dev был бы процессом 5, то дерево префиксов выглядело бы как:

/dev/hd0=3,a:/dev=5,a:/=3,e  
Если вы хотите: Используйте:
Показать дерево префиксов Утилиту prefix
Получить дерево префиксов внутри программы на языке Си Функцию qnx_prefix_query()

Сетевой корень

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

/dev/ser1 локальный последовательный порт
//10/dev/ser1 последовательный порт в узле 10
//0/dev/ser1 локальный последовательный порт
//20/usr/dtdodge/test файл на узле 20

Заметьте, что //0 всегда указывает на локальный узел

Сетевой корень по умолчанию

Когда программа выполняется на удаленном узле, вы обычно хотите, чтобы она рассматривала имена путей в контексте пространства имен своего узла. Например, эта команда:

//5 ls / 

которая запускает утилиту ls на узле 5, возвратит то же самое, что и:

ls / 

запускаемая на своем узле. В обоих случаях "/" будет соотноситься с деревом префиксов на вашем узле, а не на узле 5. В противном случае, можно представить себе беспорядок, который мог бы возникнуть, если бы префикс "/" рассматривался своим как для узла 5, так и для своего узла: выбирались бы одновременно файлы из разных файловых систем!

Чтобы правильно интерпретировать имена путей, каждый процесс имеет сетевой корень по умолчанию, который определяет, дерево префиксов какого узла будет использоваться для разбора любых имен путей, начинающихся с одиночной косой черты ("/"). Когда разбирается имя пути, начинающееся с одиночной косой черты, то оно предваряется сетевым корнем по умолчанию. Например, если процесс имеет сетевой корень по умолчанию //9, тогда:

/usr/home/luc

будет разбираться, как:

//9/usr/home/luc

что может быть интерпретировано, как "разобрать путь /usr/home/luc, используя дерево префиксов 9-го узла".

Сетевой корень по умолчанию наследуется процессами при их создании и соответствует локальному узлу, на котором запускается система. Например, допустим, что вы работаете на узле 9, находясь в командном интерпретаторе, для которого сетевой корень по умолчанию установлен как узел 9 (весьма типичный случай). Если вы выполните команду:

ls / 

команда унаследует сетевой корень по умолчанию //9, и в результате вы получите:

ls //9/ 

Подобно тому, если бы вы ввели команду:

//5 ls / 

Вы запустили команду ls на узле 5, но она по-прежнему унаследует сетевой корень по умолчанию //9, поэтому в результате вы опять получите ls //9/. В обоих случаях имя пути разбирается относительно одного и того же пространства имен.

Если вы хотите: Используйте:
Получить текущий сетевой корень по умолчанию Функцию qnx_prefix_getroot()
Установить сетевой корень по умолчанию Функцию qnx_prefix_setroot()
Запустить программу с новым сетевым корнем по умолчанию Утилиту on

Передача имен путей между процессами

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

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

Псевдонимы префиксов

Мы рассмотрели префиксы, которые регистрируются менеджерами ресурсов files/images/literature/qnx4/O. Вторая форма префикса известна как псевдоним префикса, это простая подстановка строки вместо искомого префикса. Псевдоним префикса имеет форму:

prefix=строка-заменитель  

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

/=//10/ 

Это приведет к тому, что ведущая косая черта (/) будет заменяться на префикс //10/. Например, путь /usr/dtdodge/test будет заменен следующим:

//10/usr/dtdodge/test

Это новое имя пути будет снова сравниваться с деревом префиксов; на этот раз будет использоваться уже дерево префиксов 10 узла, т.к. имя пути начинается с //10. Это приведет к Менеджеру файловой системы на узле 10, которому и будет направлен запрос, например, open(). С помощью всего лишь нескольких символов этот псевдоним позволил нам обращаться к удаленной файловой системе как к локальной.

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

/dev=5,a:/=//10/ 

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

Создание специальных имен устройств

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

/dev/printer=//20/dev/spool 

Любой запрос на открытие локального принтера /dev/printer будет направляться по сети к реальному спулеру. Аналогичным образом, если не существует локального дисковода для гибких дисков, псевдоним для удаленного дисковода на узле 20 может иметь вид:

/dev/fd0=//20/dev/fd0 

В обоих рассмотренных выше случаях можно не использовать перенаправление с помощью псевдонима, а обращаться непосредственно к удаленному ресурсу следующим образом:

//20/dev/spool ИЛИ //20/dev/fd0

Относительные пути

Путь не обязательно должен начинаться с одной или двух косых черт. В таких случаях путь считается относительным по отношению к текущему рабочему каталогу. QNX хранит текущий рабочий каталог в виде строки символов. Относительные пути преобразуются в полные сетевые имена пути добавлением текущего рабочего каталога в начало относительного пути.

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

Текущий рабочий каталог

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

Например, эта команда:

cd //18/ 

является примером первой (определенной) формы и привязывает последующие разборы относительных путей к узлу 18, независимо от того, какое значение сетевого корня по умолчанию. Выполнив затем команду cd dev, вы попадете в //18/dev.

С другой стороны, такая команда:

cd / 

является примером второй формы, когда сетевой корень по умолчанию влияет на разбор относительного пути. Например, если сетевой корень по умолчанию //9, тогда, выполнив команду cd dev, вы попадете в //9/dev. Так как текущий рабочий каталог не начинается с указания узла, то сетевой корень по умолчанию добавляется для получения полного сетевого пути.

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

Замечание о cd

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

В QNX, однако, cd не изменяет путь - за исключением сокращенных ссылок "..". Например:

cd /usr/home/luc/test/../doc 

установит текущий рабочий каталог /usr/home/luc/doc, даже если некоторые элементы этого пути являются символическими связями.

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


Чтобы получить полное сетевое имя пути, используйте утилиту fullpath.

Пространство дескрипторов файлов

При открытии ресурса files/images/literature/qnx4/O, open() возвращает целое число, называемое дескриптор файла (FD), которое используется затем, чтобы направлять все последующие запросы files/images/literature/qnx4/O к данному менеджеру. (Внутри библиотечных процедур для направления опроса используется вызов ядра Sendfd().)

Пространство дескрипторов файлов, в отличие от пространства имен пути, полностью локально для каждого процесса. Менеджер использует сочетание PID и FD, чтобы определить управляющую структуру, созданную предыдущим вызовом open(). Эта структура называется блок управления файлом (OCB от английского open control block) и хранится внутри менеджера files/images/literature/qnx4/O.

Следующая диаграмма показывает, как менеджер files/images/literature/qnx4/O находит соответствующий OCB для пары PID и FD.



PID и FD определяют OCB для Менеджера ввода/вывода.

Блоки управления файлами

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

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

Следующая диаграмма показывает два процесса, один из которых открывает файл дважды, а другой открывает этот же файл один раз. При этом нет разделяемых FD.



Процесс A открывает файл /tmp/file дважды. Процесс B открывает тот же файл один раз.

Несколько дескрипторов файла в одном или нескольких процессах могут указывать на один и тот же OCB. Это может быть достигнуто двумя способами:

  • процесс может использовать функции Си dup(), dup2() или fcntl() для создания дубликата дескриптора файла, который указывает на тот же самый OCB;
  • при создании нового процесса посредством fork(), spawn() или exec(), все дескрипторы открытых файлов по умолчанию наследуются новым процессом; эти наследуемые дескрипторы указывают на те же самые OCB, что и соответствующие дескрипторы файла в родительском процессе.

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

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

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



Процесс открывает файл дважды, а затем получает третий FD посредством dup(). Его дочерний процесс унаследует все эти три дескриптора файла.

Вы можете предотвратить наследование дескриптора файла при использовании spawn() или exec(), предварительно вызвав функцию fcntl() и установив флаг FD_CLOEXEC.