Архитектура QNX


Отклики, которые я получил на свои предыдущие статьи, убедили меня в том, что необходимо кардиально изменить стиль моих публикаций. Мои статьи были слишком поверхностны, вследствие чего те, кто до этого ничего не знал о QNX, так ничего и не поняли, а для тех, кто знал, они бесполезны. Поэтому вместо сборника азбучных истин я решил подготовить цикл статей, в каждой из которых будет более-менее обстоятельно рассматриваться один из вопросов, связанных с ОСРВ QNX. Большинство материалов одинаково верны (либо не верны :)) как для 4.х, так и для 6.х.

Но перед этим несколько замечаний. Как мне удалось выяснить, QNX когда-то называлась QUNIX. По настоянию фирмы AT&T, владеющей торговой маркой UNIX, название было изменено. Сначала я решил, что буква Q, возможно, означает Quantum (прежнее название QSS), но подтверждения своей версии не нашел - везде написано, что Q = Quick (быстрый). А ведь могло получиться очень забавно: сначала ОС назвали по имени фирмы (QNX = Quantum UniX), а затем фирму - по имени ОС (QSS = Qnx Software Systems). Может, маркетологи QSS (которые недавно полностью были замещены новыми кадрами) решатся на очередное переименование? Например, QSUX = QSs UniX :). Шучу, конечно, QNX - очень громкий брэнд в мире встраиваемых систем, а у QSS - очень грамотный отдел маркетинга, управляет которым, между прочим, бывший работник Микрософт (!). Уж что-что, а маркетинг у М$ всегда был очень эффективный. Фактически, это единственный работоспособный отдел Корпорации. Впрочем, это уже оффтопик.

Итак, архитектура системы определяется архитектурой ядра. Это тем более верно для встраиваемых систем, ведь в ПЗУ промышленного робота, бортовой компьютер ракеты-носителя ядерной боеголовки, микропрограмму микроволновки (извините за тавтологию), и все такое прочее, кроме ядра и собственно программы управления данным объектом ничего засовывать не будут. Удаленное администрирование через TCP/IP и/или ГУИ возможно, но используется редко. Один человек совершенно справедливо назвал QNX мечтой кришнаита: маленькая, никому не мешает, тихо жужжит себе под нос, и делает благое дело. Такое мнение он высказал, когда совершенно случайно узнал, что один из использующихся на его заводе станков с ЧПУ работает под QNX2. В течение многих лет никто не удосужился это выяснить просто потому, что не было повода - перенастройки/апдейта/ребута/переустановки не требовалось. Последние три действия вообще не имеют смысла применительно к встроенным системам. Во всяком случае, так было раньше, до того, как на рынок встроенных систем вышла всеми любимая Microsoft с ее Windows XP Embedded Edition (и WinCE 3.0). Так что в ближайшее время тебе понадобиться перезагружать холодильник по десять раз на дню (Произошло переполнение морозильника по адресу ПолкаFFFF0001, если после разморозки проблема возникнет снова, обратитесь в ближайший супермаркет :)), а тостер при попытке засунуть в него кусок батона будет бить током, кричать про недопустимую операцию и требовать исключительно французских круассанов.

По поводу использования QNX в качестве десктоп-системы (сервер, рабочая станция, firewall/proxy, файловая мусорка, домашняя/игровая машина) могу заявить сразу: возможно, но не всегда, и почти никогда не оправдывает материальных/интеллектуальных вложений. Микроядро на десктопе - нонсенс. Обычно хост-система, то есть тот комп, на котором идет разработка встраиваемой системы, работает под обычной OS (Windows, Solaris, Linux, HP-UX, etc). Встраиваемые ОСРВ работают в специализированных контроллерах, но некоторые из них допускают использование на обычном компе (в смысле - никуда не встраиваемом, но это не всегда обычный IBM PC) в качестве среды разработки. Использовать QNX RtP для разработки embedded-приложений очень удобно, что не удивительно, так как она разрабатывалась для этого, более того - ТОЛЬКО для этого (ну, разве что еще для саморекламы :)). Ставить ее на комп "просто так" - глупость. Впрочем, лично меня это соображение не остановило, чему я очень рад :).

Опять меня занесло куда-то совсем далеко от темы статьи... Итак, дубль 2. Основой архитектуры QNX является микроядро, жесткое реальное время и обмен сообщениями.

Используемое в QNX микроядро чрезвычайно мало (около 8Кб в QNX4, от 20 до 32Кб в различных реализациях QNX6), и полностью написано на ассемблере, что обеспечивает высочайшую скорость. Кроме того, в такой небольшой программе легче находить ошибки, что делает его чрезвычайно надежным. Конечно, 32 килобайта для программы на чистом ассемблере не так уж и мало, но все же учитывая соотношение между количеством потраченных на разработку человеко-дней и размером полученного бинарного кода, QNX смело можно назвать одной из самых проработанных ОС. Не удивительно, что ей доверяют контроль над ядерными реакторами и медицинским оборудованием. И то, и другое напрямую связано с жизнью и здоровьем людей.

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

1. Планировщик процессов

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

Все функции, выполняемые операционной системой QNX, за исключением функций ядра, реализуются стандартными процессами. В типичной конфигурации системы QNX имеются следующие системные процессы: администратор процессов (Proc), администратор файловой системы (Fsys), администратор устройств (Dev), сетевой администратор (Net). Вот как примерно выглядят некоторые команды в файле sysinit (инициализация системы):

# запуск менеджера устройств
Dev &
# драйвер консоли с восемью виртуальными консолями
Dev.con -n 8 &
# открыть ввод-вывод на консоль
reopen //0/dev/con1
# последовательные порты (COM1/COM2)
Dev.ser &
# параллельный порт (LPT1)
Dev.par &
# диспетчер файловых систем
Fsys &
# драйвер флоповода
Fsys.floppy &
# администратор сети
Net &
# драйвер сетевой карты
Net.ether8003 &
# инициализация всех консолей и запуск login на первой
tinit -T /dev/con* -t /dev/con1 &

Применение термина "системный процесс" весьма условно - никакого отличия от процессов пользователя нет. Ты можешь называть их модулями, демонами, драйверами, приложениями - суть не меняется. Абсолютно любой процесс в QNX можно запустить или остановить в любой момент. Никакого сходства с Linux или BSD: тебе не нужно компилировать ненужный в настоящий момент кусок кода в виде модуля в надежде, что он понадобиться в будущем. Во-первых, где уверенность, что понадобиться? Во-вторых, даже неподключенный модуль занимает память и тормозит ядро. Да, я знаю, это глупо. Как может программа занимать память, если она не запущена? Почему работа компа замедляется, хотя модуль не выполняется? Я бы тебе объяснил, но статья посвящена Кьюниксу, а не Линуксу :). Скажу только, что никогда и ничего не компилирую в виде модулей - Лучше лишний раз пересобрать ядро. Впрочем, я опять отвлекся, так что напомню: в QNX таких проблем нет, можно развернуть систему от минимальной инсталляции до полнофункциональной, а потом обратно... без перезагрузки (!), без перекомпиляции (!!), и даже без непосредственного контакта с конфигурируемым компьютером (!!!), то бишь через сеть.

В начале статьи уже упоминалось, что бОльшая часть изложенной здесь информации справедлива и для QNX4, и для QNX6/Neutrino. Фундаментальные принципы организации ядра практически не изменились. Но все же есть по крайней мере одно очень серьезное нововведение в Neutrino. Еще раз подчеркиваю - речь не о улучшениях ядра, их гораздо больше одного, что заметно хотя бы по количеству функций (в ядре QNX4 - 14 системных вызовов, в Нейтрино - около пятидесяти), а об изменениях базовых, "ядерных" принципов.

Отличие микроядра Neutrino от наноядра QNX - возможность создания "системного процесса", причем в данном случае имеется в виду процесс, взаимодействующий с системой непосредственно, в обход менеджера процессов. В QNX4 ядро и менеджер процессов запускались в едином адресном пространстве, что делало их фактически единым целым. Такой подход снижает надежность, но увеличивает производительность. Во многих конкурирующих ОСРВ такой подход применяется ко всем приложениям, а в QNX4 это - единичный случай. Этот факт, конечно, радует. Но, как всегда, появился побочный эффект (хорошо, что только один, и еще лучше, что от него удалось избавиться, но я забегаю вперед). В случае использования под QNX только одного приложения (не такая уж и редкостная для встроенных систем ситуация) приходилось использовать менеджер процессов, хотя это и бессмысленно - ведь приложение единственное, и делить ресурсы ему не с кем. На основе ядра Neutrino можно создать так называемый "системный процесс" (просто у системных программистов не хватило фантазии на что-нибудь вроде quazi-kernel или OS-Level-App), то есть пользовательское приложение, функционирующее на уровне ОС. Просто до гениальности: сплавляем микроядро Neutrino со своей программой управления Терминатором, и получаем быструю (потому что это голое ядро), компактную (потому что выкинули менеджер процессов), автономную микропрограмму, не зависящую от ОС (потому что она и есть ОС) и даже от BIOS (можно организовать начальную загрузку "своими средствами", через модули IPL).

Кстати, интересный вопрос - является ли такой "системный процесс" однозадачной ОС? И может ли вообще однозадачная ОС работать в реальном времени? Однозначно - нет (на оба вопроса). Во-первых, "системный процесс" может распараллелить все что угодно с помощью нитей (threads), которые QNX, конечно, поддерживает. Во-вторых, однозадачная система может быть мягкой системой реального времени (или системой мягкого реального времени, что одно и то же), но ОС жесткого реального времени обязана быть многозадачной. События могут наступить одновременно, и однозадачная система обязательно "потеряет" одно из них. Время отклика зависит в том числе от задержки выполнения прерывания, а контролировать это, например, в DOS - невозможно (это не единственная причина, но самая наглядная). Более того, не все многозадачные ОС это умеют (Windows 95/98/ME не умеет, да и вообще не является истинно многозадачной). Кстати, Windows NT/2000/XP/CE3, формально являющиеся мягкими ОСРВ, реализуют реальное время, но очень уж [мелко-]мягко :). Сколько времени пройдет между ISR (первичный обработчик прерывания) и DPC (вызов отложенной процедуры) - Гейтс его знает. Видимо, поэтому многие системщики вообще не признают мягкие ОСРВ, ведь если система гарантирует обработку сигнала, но не гарантирует, что обработает его "сейчас" (то есть в течение некоторого заранее определенного кванта времени), то она бесполезна в системах реального времени. Действительно, существуют системы критичные ко времени обработки сигнала, в которых промедление смерти подобно (иногда - в буквальном смысле), но мягкие системы реального времени тоже находят свою рыночную нишу.

2. Обмен сообщениями

Любая многозадачная ОС должна содержать средства связи между процессами (IPC = interprocess communication), тогда мы сможем создавать приложения, представляющих собой набор взаимосвязанных процессов, в котором каждый процесс выполняетет одну строго определенную функцию. Это важно не только для ОСРВ, но все же именно в ОСРВ производительность напрямую зависит от реализации IPC.

Ядро QNX реализует передачу всех сообщений между всеми процессами во всей системе. Сообщение - это просто пакет данных длиной до 65536 байт, который передается от одной задачи в другую. Ядро никоим образом не изменяет сообщение, просто копирует данные из адресного пространства одного процесса в адресное пространство другого процесса и приостанавливает первый процесс, пока второй не вернет ответное сообщение. Гениальность такого подхода - в его изяществе, функциональности, эффективности и простоте, а эти качества обычно встречаются только по одиночке :). Многие другие ОСРВ тоже реализуют механизм передачи сообщений, но только QNX делает это на уровне ядра.

Попутно замечу, что QNX поддерживает и другие виды IPC (в том числе сигналы, семафоры, исключения, и т.д.), но именно сообщения являются в QNX основным способом передачи информации, потому и поддерживаются непосредственно ядром. Можно, конечно, организовать передачу данных через совместно используемую память, порты, или даже временные файлы, но все эти способы либо медленны, либо ненадежны, либо не работают вне локальной машины. Именно поэтому процессы используют сообщения, даже если вызывают другие функции, ведь системные библиотеки - это надстройка над ядром, а значит, и над системой передачи сообщений. Например, процесс, передающий данные другому процессу по "трубопроводу" (pipe), косвенно передает их в виде сообщений.

Оперирование сообщениями осуществляется вызовом соответствующих функций языка Си: send() - послать, receive() - принять, reply() - ответить, а также нескольких других, предназначенных для обработки коллизий (в смысле - внештатных ситуаций). Процесс-получатель идентифицируется PID'ом, но существуют способы привязки имени процесса к определенному PID'у. Можно получить сообщение постепенно, кусками требуемого размера, что очень удобно в случае, когда сообщений очень много, и необходимо приостановить прием одного для обработки другого. Это позволяет динамически распределять память под принимаемые данные, что избавляет процесс от излишнего количества буферов.

Вот как происходит передача сообщения от процесса А к процессу В:

  1. Процесс А вызывает функцию ядра send(), после чего становится SEND-блокированным.
  2. Ядро передает сообщение процессу В.
  3. Процесс В выдает receive().
  4. Ядро информирует процесс А, что его сообщение процессом В получено. Процесс А изменяет свое состояние на REPLY-блокированное.
  5. Процесс В выдает reply().
  6. Ядро передает процессу А ответное сообщение процесса В. Процесс А разблокировывается.

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

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

Маленький пример. Допустим, происходит передача сообщения по вышеприведенной схеме. После того, как процесс А выдаст запрос send(), он приостанавливается, следовательно, ему нужно меньше ресурсов, чем процессу В, который занят обработкой данных, полученных от процесса А. Поэтому процесс В после выдачи запроса receive() может продолжать свою работу до тех пор, пока не будет готов ответ для процесса А (или пока не поступит сообщение от какого-нибудь другого процесса С).

Выше уже упоминалось, что процесс блокируется до тех пор, пока не получит ответ на сообщение, но это не всегда приемлемо. Например, процессу требуется обслуживать несколько высокоскоростных устройств и одновременно общаться с другими процессами. На этот случай существует функция creceive(), которая в случае отсутствия сообщения немедленно делает return(), а в остальном идентична receive(). Именно этот принцип обеспечивает высокую производительность таких задач, как Dev и Fsys.

Надеюсь, ты понимаешь, что это далеко не все. Я бы хотел рассказать больше (о передаче сообщений через proxy-процесс, например, или о виртуальных каналах), но пытаться втиснуть в статью материал, которого хватило бы на несколько толстых книжек - бессмысленно. Надеюсь, я пробудил у тебя желание изучать QNX самостоятельно. Значит, не зря... Продолжаем дальше.

3. QNX - это сеть

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

QNX-сеть похожа на универсальную вычислительную машину (в простонаречии - мэйнфрейм), только собранную из обычных компов. Например, программы, управляющие работой устройств ввода/вывода, работающие в реальном времени, могут потребовать больше ресурсов, чем другие, менее критичные ко времени, приложения (текстовый редактор, например). Qnet достаточно реактивна для того, чтобы поддерживать оба типа приложений одновременно: вычислительная мощность будет сконцентрирована там, где и когда это необходимо, без ущерба для параллельной обработки данных. Получается что-то вроде кластерного суперкомпьютера, причем "прямо из коробки", а если использовать в качестве транспорта не FLEET (собственный сетевой протокол QNX), а TCP/IP, то можно распараллелить задачу на глобальную сеть...

Каждому узлу сети QNX присвоен номер от 1 до 255, являющийся его идентификатором. Сервер обычно получает номер 1, так что если у тебя номер другой - ты в сети :), причем не в качестве администратора %). В любом случае, ты можешь рассматривать тот компьютер, за которым работаешь (localhost), как номер 0. Другими словами, //0 = 127.0.0.1, если так тебе понятнее.

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

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

Избыточная топология сети обеспечивает бОльшую надежность. В случае выхода из строя одной из сетевых карт сетевой администратор автоматически перенаправляет весь поток данных по другой сети. Это обеспечивает прозрачную сетевую отказоустойчивость. Можно даже объеденить все компьютеры сети каким-нибудь дешевым интерфейсом (например, последовательным или параллельным соединением), который послужит резервной сетью. В случае полного выхода из строя всей сети (кто-то подал на общую шину 10 тысяч вольт, и все сетевые карты сгорели :)) передача данных не прервется, хотя пропускная способность сети, конечно, снизится.

Во многих операционных системах работа с сетью, межпроцессные коммуникации (IPC) и даже передача сообщений, выполняются не ядром, а внешними надстройками над ним. В результате получается совершенно неэффективный двойной интерфейс, в котором отдельно организована связь между процессами и совершенно иначе - доступ к закрытому монолитному ядру. Так что QNX - это не только высокоэффективная встраиваемая система жесткого реального времени, но и полностью распределенная сетевая ОС. Одну из следующих статей я полностью посвящу Qnet и FLEET, обещаю.

Прочитать эту статью в журнале Xakep (Часть 1)

Прочитать эту статью в журнале Xakep (Часть 2)


Назад

На главную страницу

X