Pull to refresh

ОСРВ QNX: Немного о микроядре, потоках и процессах

Reading time 6 min
Views 27K
Поскольку мой первый небольшой обзор операционной системы реального времени QNX показал, что среди жителей Хабра есть к ней интерес, то я решил продолжить цикл заметок. Мне кажется, что стоит немного рассказать о системной архитектуре QNX6. Думаю, что нелишне дать определение тому, что такое микроядро, и какие задачи оно решает. По ходу повествования будут также развенчаны два мифа, связанные с QNX. Но сначала…

Немного о POSIX


С выпуском каждой новой версии QNX (а надо отметить, что первая версия ОСРВ QNX появилась теперь уже в далёком 1981 году), разработчики использовали накопленный ранее опыт и делали систему лучше, в том числе и удобнее для разработчиков. Именно поэтому QNX Neutrino поддерживает стандарты POSIX 1003.1, например, управление потоками (threads), расширения реального времени (Realtime Extensions), дополнительные расширения реального времени (Additional Realtime Extensions) и профили прикладных окружений (Application Environment Profiles, AEP).

Зачем я об этом говорю? Очень просто. Хотелось бы отметить две вещи, которые характерны для QNX и связаны с POSIX. Есть мнение, что любая POSIX операционная система скрывает в себе UNIX. А раз так, то такая система слишком громоздкая и не может применяться во встраиваемых решениях. Однако, в случае QNX это неверно. Стандарт POSIX описывает интерфейс, а не реализацию. А значит, под слоем POSIX может скрываться что угодно, в том числе и микроядро.

Второй момент, на котором я бы хотел остановится, наверное, очевиден. Это, конечно, те преимущества, которые даёт POSIX в QNX:
  • Повторное использование кода. Однажды разработанный, отлаженный и протестированный код для одной POSIX операционной системы, может быть повторно использован в другой POSIX системе, в том числе и в QNX.
  • «Переносимость» программистов. Разработчик или группа разработчиков, знакомая c POSIX и UNIX могут легко приступить к разработке встраиваемой системы реального времени (я, конечно, имею в виду QNX). Ведь большая часть ОС им будет уже знакома.
Итак, можно сделать вывод, что POSIX в QNX обеспечивает ряд преимуществ.

Истинное ядро


ОСРВ QNX основана на микроядерной архитектуре. Я думаю, что стоит остановится, и прежде, чем идти дальше, определить, что такое микроядро (по крайней мере в терминологии QNX). Термин микроядро с некоторого времени стал очень популярен и многие системы, содержащие ядро небольшого размера, называются микроядерными. А если ядро ещё меньше, то его называют наноядром. Это не совсем верно для QNX. Маленький размер ядра это не главная цель. Идея в том, что многие функции операционной системы (в том числе и такие привычные, как, например, поддержка файловой системы) вынесены из ядра в область пользовательских приложений. А само микроядро выполняет лишь небольшой набор функций, большая часть которых обеспечивает межзадачное взаимодействие:
  • обмен сообщениями (это основное, чем занимается микроядро);
  • управление потоками;
  • планирование (потоков);
  • синхронизация (потоков);
  • управление сигналами;
  • управление таймерами.
Обратите внимание, что даже управление процессами выполняется не микроядром, а администратором процессов (который, правда, скомпонован с микроядром в один модуль). Это ли не есть истинное ядро системы?

То, что я перечислил выше, это всё, что делает микроядро QNX. Такие вещи как драйверы сети или поддержка дисков вынесены в отдельные модули, которые запускаются и работают как обычные пользовательские процессы. Со всеми вытекающими отсюда преимуществами (перед системами на основе монолитного ядра). А какие это преимущества? Очень просто:
  • Исходный код микроядра гораздо меньше, чем у монолитного ядра, а значит ядро легче отладить и протестировать.
  • Микроядро увеличивает модульность. Конечная целевая система может быть легко сконфигурирована в соответствии с требованиями. Достаточно только запустить те менеджеры, которые требуются.
  • Микроядро повышает надёжность системы. Если в драйвере произошла ошибка, то это не приведёт к краху системы (и микроядра), а сам драйвер можно перезапустить в любой момент, без перезапуска всей системы.

Миф: Микроядро QNX написано на ассемблере


Среди некоторых разработчиков бытует мнение, что высокая производительность и компактность микроядра QNX Neutrino является результатом того, что оно написано на ассемблере. Но это не так. Микроядро практически полностью написано на C. Производительность и компактность это следствия применения хорошо отлаженных алгоритмов и структур.

Процессы и потоки в QNX


Согласно спецификации POSIX, ОСРВ QNX поддерживает потоки. И даже больше, микроядро QNX управляет только потоками, и именно потоки можно считать минимальной «единицей выполнения». Каждый процесс в QNX может содержать один или несколько потоков. В таком ключе, процесс может рассматриваться как контейнер потоков.

В простейшем случае, процесс может (и должен) содержать один поток. Но иногда требуется параллельное выполнение нескольких алгоритмов в одном процессе. Приведу небольшой пример из QNX. Менеджер файловой системы может принимать и обрабатывать параллельно запросы от нескольких клиентов (других процессов). Если бы менеджер работал в один поток, то уже второй обратившийся к нему клиент вынужден был бы ожидать завершения ранее запрошенной операции. К счастью, менеджер файловой системы в QNX работает в несколько потоков, что позволяет обслуживать несколько приложений одновременно и параллельно.

Все процессы в QNX изолированны друг от друга и выполняются в своём собственном виртуальном адресном пространстве. За реализацию такой защиты отвечает блок управление памятью (Memory Management Unit, MMU). Наличие в процессоре устройства MMU является обязательным требованием для запуска QNX. В тоже время, потоки одного процесса работают в одном адресном пространстве.

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

Миф: Микроядро QNX медленно переключает контексты между процессами


Изначально потоки появились в UNIX-системах в качестве решения проблемы слишком медленного переключения контекстов между процессами. Скорее всего, с тех пор принято считать, что переключение контекстов между процессами происходит очень неспешно. А поскольку при использовании микроядра и передачи сообщений, выполняется больше переключений контекстов, чем при использовании монолитного ядра, то делается простой вывод — QNX работает медленно. Однако, архитектура QNX Neutrino решает проблему производительности переключения контекстов. И в QNX практически нет разницы в скорости переключения контекстов между процессами и потоками.

Как живёт поток


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

Пока поток запущен он может находиться в двух состояниях: готов (ready) или блокирован (blocked). Но на самом деле существуют различные причины, по которым поток может быть заблокирован и при выводе информации о процессах и потоках при помощи команды pidin (QNX-специфичный вариант утилиты ps) мы можем наблюдать разные состояния, например, SEND, RECEIVE, SIGWAITINFO, NANOSLEEP и другие. Например, в таком состоянии у меня сейчас потоки менеджера USB:

# pidin -P io-usb
     pid tid name               prio STATE       Blocked
    4101   1 proc/boot/io-usb    10o SIGWAITINFO
    4101   2 proc/boot/io-usb    21r RECEIVE     1
    4101   3 proc/boot/io-usb    10o RECEIVE     4
    4101   4 proc/boot/io-usb    10r NANOSLEEP
    4101   5 proc/boot/io-usb    10o RECEIVE     4

Здесь pid — идентификатор процесса (process ID) в системе, tid — идентификатор потока (thread ID) в процессе, prio — номер приоритета потока и дисциплина планирования, STATE — состояние потока, Blocked — значение зависит от причины блокировки. В приведённом примере потоки 2, 3 и 5 находятся в Receive-блокированном состоянии (т.е. готовы принимать сообщения от других потоков).

Эпилог


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

И ещё. Если кого-то заинтересовал QNX, то всё, что я описал и даже гораздо больше можно прочитать в Системной архитектуре (System Architecture) QNX Neutrino. Эта документация доступна в электронном виде на сайте www.qnx.com (на английском языке), а также есть русский перевод в печатном виде:
  1. Операционная система реального времени QNX Neutrino 6.3. Системная архитектура. ISBN 5-94157-827-X
  2. Операционная система реального времени QNX Neutrino 6.3. Руководство пользователя. ISBN 978-5-9775-0370-9
Tags:
Hubs:
+84
Comments 64
Comments Comments 64

Articles