Надеюсь, что долгожданное продолжение цикла заметок об операционной системе реального времени QNX. В этот раз я бы хотел рассказать о Qnet — собственном сетевом протоколе QNX. Сразу уточню, что помимо родной сети Qnet, в QNX поддерживается стек протоколов TCP/IP, работа с которым в общем-то должна быть знакома администраторам Unix-подобных систем. Поэтому в заметке сначала немного расскажу о сетевом администраторе io-pkt
, а потом более подробно о протоколе Qnet. По ходу повествования нас также ждут четыре лирических и одно техническое отступления.
Что такое Qnet?
Сеть QNX представляет собой группу соединённых между собой целевых систем, каждая из которых работает под управлением ОСРВ QNX Neutrino. В такой сети любая программа имеет доступ к любому ресурсу на любом узле (node, именно так называются отдельные компьютеры в сети). В качестве ресурса может выступать файл, устройство или процесс (в том числе обеспечивается и запуск процессов на другом узле). При этом целевые системы (те самые узлы) могут представлять собой компьютеры различных архитектур — x86, ARM, MIPS и PowerPC (текущая реализация Qnet работает в том числе и в cross-endian среде). Но словно этого мало, любое POSIX-приложение, портированное в QNX (для переноса зачастую требуется только пересборка) без всякой доработки будет обладать перечисленными выше способностями работы в сети Qnet. Заинтригованы, как это получается?
Протокол Qnet распространяет механизм обмена сообщениями на сеть микроядер QNX Neutrino. Вот и раскрыт самый большой секрет Qnet, дальше в заметке мы рассмотрим, как это всё работает.
Сетевая подсистема io-pkt
Сетевая подсистема является типичным представителем одной из подсистем QNX, т.е. состоит из администратора io-pkt*
(построенного по технологии менеджера ресурсов QNX), модулей устройств (драйверов), например, devnp-e1000.so
, модулей протоколов, например, lsm-qnet.so
, а также утилит, например, ifconfig
и nicinfo
. К слову, в QNX три менеджера сети: io-pkt-v4
, io-pkt-v4-hc
и io-pkt-v6-hc
. Суффикс v4
говорит о том, что данный менеджер поддерживает только IPv4, а вариант с v6
поддерживает IPv4 и IPv6. Суффикс hc
(high capacity) означает расширенный вариант с поддержкой шифрования и Wi-Fi. Поэтому иногда в литературе можно встретить название io-pkt*
, но мы будем называть менеджер io-pkt
(без звёздочки), т.к. в нашем случае не важно, о какой версии TCP/IP идёт речь, ведь эта заметка о Qnet.
io-net
, и модуль протокола TCP/IP не был слинкован с io-net
, а был самостоятельным модулем наподобие lsm-qnet.so
. Хотя постойте, ведь и модули тогда имели другой префикс, так TCP/IP модули назывались npm-tcpip-v4.so
и npm-tcpip-v6.so
, а Qnet — npm-qnet.so
. Хотя последнее тоже не совсем верно, в стародавние времена (в эпоху QNX 6.3.2) было два модуля Qnet — npm-qnet-compat.so
(для совместимости с более старыми версиями QNX6) и npm-qnet-l4_lite.so
(новый протокол, который поддерживается модулем lsm-qnet.so
из состава QNX 6.5.0). Кстати, npm означает Network Protocol Module, а lsm — Loadable Shared Module.
Во времена io-net
модули сетевых драйверов носили гордый префикс devn. Драйверы для io-pkt
имеют другой префикс — devnp. Старые драйверы можно подключать и к io-pkt
, при этом автоматически используется модуль прослойка devnp-shim.so
.
Рис. 1. Детализированное представление архитектуры io-pkt
.
Архитектура io-pkt
представлена на рис.1. На нижнем уровне расположены драйверы проводной и беспроводной сетей. Это подгружаемые модули (DLL, разделяемые библиотеки). Стоит отметить, что помимо Ethernet поддерживаются и другие среды передачи. Например, драйвер devn-fd.so
позволяет организовать передачу и приём данных используя файловый дескриптор (fd это как раз file descriptor), таким образом можно организовать работу сети, например, по последовательному порту. Скорости, конечно, будут соответствующие, но иногда это очень спасает. Драйверы устройств подключаются к многопоточному компоненту второго уровня (стеку). Стек, во-первых, обеспечивает возможность мостового соединения и ретрансляции. Во-вторых, стек предоставляет унифицированный интерфейс управления пакетами и обработку протоколов IP, TCP и UDP. На верхнем уровне расположен менеджер ресурсов, который реализует передачу сообщений между стеком и пользовательскими приложениями, т.е. обеспечивает работу функций open()
, read()
, write()
и ioctl()
. Библиотека libsocket.so
преобразует интерфейс io-pkt
в BSD socket layer API, который является стандартом для большинства современного сетевого кода.
Стек также предоставляет интерфейсы для работы с данными на Ethernet и IP уровнях, что позволяет протоколам (например, Qnet) выполненным в виде DLL, соединяться с необходимым уровнем (IP или Ethernet). В случае если требуется взаимодействие с внешними приложениями протокол (например, Qnet) может включать собственный менеджер ресурсов. Помимо драйверов и протоколов стек включает средства пакетной фильтрации: BPF (Berkeley Packet Filter) и PF (Packet Filter).
Пример запуска io-pkt
с поддержкой протокола Qnet:
io-pkt-v4-hc -d e1000 -p qnet
Опция -d
указывает драйвер сетевого контроллера devnp-e1000.so
, опция -p
загружает модуль Qnet. На самом деле, при установке QNX с инсталляционного диска сеть стартует автоматически из стартовых сценариев, ручной запуск обычно требуется в случае встраивания QNX и для уменьшения скорости загрузки системы.
Помимо работы с кадрами Ethernet протокол Qnet может также инкапсулироваться в IP пакеты. Это может быть удобно, например, при работе в распределённых сетях. Следует учитывать, что накладные расходы в этом случае увеличиваются. Пример запуска для работы Qnet поверх IP:
io-pkt-v4-hv -d e1000 -p qnet bind=ip,resolve=dns
Если по каким-то причинам io-pkt
был запущен без поддержки Qnet, то модуль lsm-qnet.so
можно подгрузить позже, например:
mount -Tio-pkt lsm-qnet.so
Более полную информацию об io-pkt
можно получить в справочной системе QNX, а мы перейдём к основной теме нашей заметки.
Как работает Qnet?
С точки зрения пользователя Qnet работает полностью прозрачно… Стоп. А не пора ли пояснить значение слова прозрачно в данном контексте? Пожалуй, пора. Термин прозрачность, в нашем случае, означает, что для доступа к удалённому ресурсу не требуется предпринимать каких-то дополнительных действий по сравнению с доступом к локальному ресурсу. Попробую пояснить на примерах чуть ниже.
После запуска Qnet создаёт каталог /net, в котором отображается файловая система каждого узла сети в отдельном каталоге с именем узла. Если имя узла (hostname) не установлено (т.е. используется имя по умолчанию localhost), то в каталоге /net он будет обзываться именем составленным из MAC-адреса. Например, следующая команда выведет на экран содержимое корневого каталога узла zvezda:
ls -l /net/zvezda
Следующая команда позволяет отредактировать один из стартовых сценариев на узле zvezda:
vi /net/zvezda/etc/rc.d/rc.local
С доступом к файлам разобрались, ничего необычного тут нет. Доступ к устройствам работает аналогично. Вот так, например, можно работать с последовательным портом на другом узле:
qtalk -m /net/zvezda/dev/ser1
А вот пример поинтересней:
phcalc -s /net/zvezda/dev/photon
Обычно приложения Photon (это штатная графическая оконная подсистема QNX) позволяют указать сервер к которому будут подключаться (опция -s
). В данном случае приложение запускается на локальном узле, а графическое окно будет отображаться на узле zvezda. На локальном узле не требуется даже запуск графической подсистемы. Это может быть удобно в ряде случаев, например, когда узел не имеет графического контроллера, но требуется графическое отображение собранных данных с датчиков или настройка системы. Также такой подход может позволить разгрузить центральный процессор графического сервера.
Tcpip
только на одном узле в сети FLEET, и все запросы библиотеки socket направлять на этот узел. Не пугайтесь, в QNX6 обычно так не делают. Да и в случае с io-pkt
в этом нет смысла, т.к. TCP/IP стек неотделим от самого io-pkt
.
С файлами и устройствами, надеюсь, разобрались. А как же быть с процессами? С этим тоже всё просто. Ряд системных утилит предоставляет опцию -n
, которая говорит о том, что требуется либо работать на удалённом узле, либо собирать информацию с удалённого узла. Например, получить список запущенных процессов с аргументами командной строки можно так:
pidin -n zvezda arg
Если какая-то утилита или программа не поддерживает опцию -n
, то на помощь приходит штатная утилита on
, которая как раз и предназначена для запуска приложений на другом узле, например:
on -f zvezda ls
В этом случае утилита ls
запускается на узле zvezda, а вывод утилиты отображается в текущем терминале.
on
предоставляет богатые возможности по управлению параметрами запускаемых процессов. Можно не только запускать процессы на удалённом узле, но и изменять уровень приоритета, дисциплину диспетчеризации, запускать процессы от другого пользователя и даже привязывать процесс к определённом процессорному ядру. Подробнее можно почитать в справке на утилиту.
Остановить процесс также просто, т.к. утилита slay
поддерживает опцию -n
. Например:
slay -n zvezda io-usb
Точно также при помощи slay
можно послать и любой другой сигнал, не только SIGTERM.
Как работает Qnet с точки зрения программиста?
В предыдущем разделе мы рассмотрели работу Qnet с точки зрения пользователя ОСРВ. В большинстве случаев не требуется даже доработки ПО для поддержки Qnet. Неужели это правда? Т.е. для создания сетевого приложения не требуется работы с какими-то специальными фреймворками и библиотеками? Да, не требуется. Если вы работаете с POSIX, то вы даже не заметите разницу. Но чтобы не быть голословным, приведу код небольшой программы, которая в качестве примера сохраняет строку в файл (в том числе и на удалённом узле):
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd;
char str[] = "This is a string.n";
if ( argc < 2 )
{
printf("Please specify file name.n");
exit(1);
}
if ( (fd = open(argv[1], O_RDWR|O_CREAT, 0644)) < 0 )
{
perror("open()");
exit(1);
}
write(fd, str, sizeof(str)-1);
close(fd);
return 0;
}
Этот код совместим с Qnet. Ведь действительно нет разницы, в какой файл сохраняется строка, в /tmp/1.txt
или /net/zvezda/tmp/1.txt
. Тоже самое и с устройствами, два фрагмента кода для программиста идентичны (отличие только в имени файла):
fd = open("/dev/ser1", O_RDWR);
и
fd = open("/net/zvezda/dev/ser1", O_RDWR);
За счёт чего же достигается подобная простота работы с сетью? POSIX-вызовы, например, open()
, порождают ряд низкоуровневых вызовов микроядра, управляющих обменом сообщениями: ConnectAttach()
, MsgSend()
и т.д. Программный код необходимый для сетевого взаимодействия идентичен, тому который используется локально. Отличие будет только в путевом имени, в случае работы по сети путевое имя будет содержать имя узла. Префикс с именем узла преобразуется в дескриптор узла, который в далнейшем используется в низкоуровневом вызове ConnectAttach()
. Каждый узел в сети имеет свой дескриптор. Для поиска дескриптора используется пространство имён путей файловой системы. В случае одной машины результатом поиска будет дескриптор узла, идентификатор процесса и идентификатор канала. В случае сети Qnet результат будет тот же самый, только дескриптор узла будет ненулевой (т.е. не равен ND_LOCAL_NODE, а это и говорит об удалённом соединении). Однако, все эти вызовы скрыты и нет необходимости о них заботиться, если вы просто используете open()
.
Что скрыто за простой функцией open()?
Рассмотрим ситуацию, когда приложению на узле node1
требуется использовать последовательный порт /dev/ser1
на узле node2
. На рис. 2 показаны, какие операции выполняются, когда приложение вызывает функцию open()
с именем /net/node2/dev/ser1
.
Рис. 2. Обмен сообщениями по сети Qnet.
- Приложение отправляет сообщение локальному менеджеру процессов с запросом о разрешении путевого имени
/net/node2/dev/ser1
. Т.к. за пространство имён/net
отвечаетlsm-qnet.so
, то менеджер процессов возвращает сообщение переадресации, в котором указывает, что приложение должно обратиться к локальному администратору сетиio-pkt
. - Приложение отправляет сообщение локальному администратору сети
io-pkt
с тем же запросом о разрешении путевого имени. Локальный администратор сети возвращает сообщение переадресации с указанием дескриптора узла, идентификатора процесса и идентификатора канала менеджера процессов узлаnode2
. - Приложение создаёт соединение с менеджером процессов узла
node2
и в очередной раз отправляет запрос на разрешение путевого имени. Менеджер процессов на узлеnode2
в свою очередь возвращает ещё одно сообщение переадресации с указанием дескриптора узла, идентификатора процесса и идентификатора канала драйвера последовательного порта на собственном узле (node2
). - Приложение создаёт соединение с драйвером последовательного порта на узле
node2
и получает идентификатор соединения, который может использоваться для дальнейшего обмена сообщениями (например, при вызовахread()
,write()
и других POSIX-функциях).
После всего этого квеста обмен сообщениями по полученному идентификатору соединения происходит точно так же, как и в случае локального обмена. Важно, что все последующие операции обмена сообщениями выполняются напрямую между приложением на узле node1
и драйвером последовательного порта (тоже, кстати обычным приложением в QNX) на узле node2
.
Ещё раз подчеркну, что для программиста использующего POSIX функцию open()
все эти низкоуровневые вызовы скрыты. В результате вызова open()
будет получен либо файловый дескриптор, либо код ошибки.
node2/dev/ser1
. На шаге 3 имя содержит только dev/ser1
. На шаге 4 — только ser1
.
Политики качества обслуживания
Протокол Qnet поддерживает передачу по нескольким каналам. Для выбора режима работы при использовании нескольких каналов применяются политики качества обслуживания (QoS, Quality of Service). В сетях Qnet служба QoS по сути выбирает среду передачи. Но если в системе только один сетевой интерфейс, то QoS не работает. Поддерживаются следующие политики качества обслуживания:
- loadbalance — протокол Qnet будет использовать любые доступные каналы связи и сможет распределять передачу данных между ними. Эта политика используется по умолчанию. В этом режиме пропускная способность между узлами будет равна сумме пропускных способностей по всем доступным каналам. Если один из каналов обрывается, то Qnet автоматически переключится на использование других доступных каналов. На неисправный канал будут автоматически отправляться служебные пакеты, и когда связь восстановится, то Qnet снова будет использовать этот канал.
- preffered — используется только один указанный канал связи, а остальные игнорируются до тех пор, пока основной не выйдет из строя. После выхода из строя основного канала связи Qnet переходит в режим loadbalance с использованием всех доступных каналов. При восстановлении основного канала Qnet переходит в режим preffered.
- exclusive — используется только один указанный канал связи, а остальные каналы НЕ будут использоваться даже в том случае, если основной выйдет из строя.
Для задания политики качества обслуживания используется модификатор путевого имени, начинающийся с символа тильды (~). Например, для использования политики QoS exclusive по интерфейсу en0
для доступа к последовательному порту на узле zvezda имя будет следующим:
/net/zvezda~exclusive:en0/dev/ser1
Когда использовать Qnet?
Выбор того или иного протокола зависит от ряда факторов. Протокол Qnet предназначен для сети доверенных компьютеров, работающих под управлением ОСРВ QNX Neutrino и имеющих одинаковый порядок следования байт. Протокол Qnet не выполняет аутентификацию запросов. Защита прав доступа выполняется при помощи обычных прав доступа пользователей и групп файлов. Также протокол Qnet это протокол с установлением соединения, сообщения о сетевых ошибках передаются клиентскому процессу.
Если в сети работают компьютеры с различными операционными системами или степень доверия к сети невысока, то стоит рассмотреть использование других протоколов, с которыми QNX также работает, например, TCP/IP, в частности, FTP, NFS, Telnet, SSH и других.
Вместо эпилога
В заметке, конечно, рассмотрены не все возможности и особенности Qnet, но дано общее описание возможностей протокола. Если вас заинтересовала технология Qnet или QNX в целом, то более подробно с материалом по этой и другим темам можно ознакомиться в Системной архитектуре (System Architecture) QNX Neutrino. Документация доступна в электронном виде на сайте www.qnx.com (на английском языке), частично перевод доступен на сайте www.kpda.ru. Также есть русский перевод в печатном виде:
Операционная система реального времени QNX Neutrino 6.5.0 Системная архитектура. ISBN 978-5-9775-3350-8
Операционная система реального времени QNX Neutrino 6.5.0 Руководство пользователя. ISBN 978-5-9775-3351-5
Автор: ob1