56. QNX, архитектура на основе микроядра, принципы, совместимость с UNIX и POSIX Ядро операционной системы QNX, представленное на рисунке, выполняет следующие функции: связь между процессами (IPC). Ядро обеспечивает три формы IPC (сообщения, proxy (прокси) и сигналы); сетевое взаимодействие нижнего уровня. Ядро передает все сообщения, предназначенные процессам на другом узле; планирование процессов. Планировщик ядра определяет, какой процесс будет выполняться следующим; первичную обработку прерываний. Все прерывания и сбои аппаратного обеспечения сначала обрабатываются в ядре, а затем передаются соответствующему драйверу или системному администратору. Микроядро QNX Neutrino соответствует POSIX 1003.1-2001, включая многопоточность, расширения реального времени и ряд других опций. В результате, в QNX Neutrino очень просто переносить программы с открытым исходным текстом из, например, UNIX или Linux - в большинстве случаев перенос сводится к перекомпиляции и компоновке с библиотеками QNX Neutrino. Благодаря тому, что QNX является POSIX-совместимой ОС, большинство программ для UNIXподобных операционных систем достаточно легко переносятся в QNX. Правда, для этого вам скорее всего придется самолично компилировать исходники - полной совместимости с UNIX все-таки нет. За установку же уже готовых для QNX программ отвечает Package Mgr 57. Функции микроядра В системе QNX ядро является действительно ядром. Прежде всего, как и подобает ядру операционной системы реального времени, оно имеет небольшой размер – менее 8 Кбайт (в версии 4, в Neutrino – 32КБ). На ядро системы QNX возложено выполнение только двух основных функций: передача сообщений (ядро реализует передачу всех сообщений между всеми процессами во всей системе); планирование (планировщик является частью ядра и подключается каждый раз, когда процесс меняет свое состояние в результате появления сообщения или прерывания). Микроядро реализует базовые функции операционной системы, на которые опираются другие системные службы и приложения. Некоторые авторы предлагают оставить в микроядре набор, состоящий всего из 3 операций, которые способны поддерживать внешние системы страничной организации памяти и управления внешней памятью. В этот набор входят следующие операции: Предоставление (grant). Владелец адресного пространства может предоставлять некоторые страницы другому процессу. Ядро удаляет эти страницы из адресного пространства первого процесса и передает их второму процессу. Отображение (map). Процесс может отобрать любые свои страницы в адресное пространство другого процесса, после чего оба процесса будут иметь доступ к этим страницам. Ядро не меняет информацию о принадлежности первоначальному процессу-владельцу, но при этом выполняет отображение, предоставляющее другому процессу доступ к этим страницам. Восстановление (flush). Процесс может восстановить любые страницы, предоставленные другим процессам или отображенные в их адресное пространство. 58. Системные и пользовательские процессы, драйверы устройств Все услуги операционной системы, за исключением тех, которые выполняются ядром, в QNX предоставляются через стандартные процессы. Типичная конфигурация QNX имеет следующие системные процессы: Менеджер процессов (Proc); Менеджер файловой системы (Fsys); Менеджер устройств (Dev); Менеджер сети (Net). Системные процессы практически ничем не отличаются от любых написанных пользователем программ - они не имеют какого-либо скрытого или особого интерфейса, недоступного пользовательским процессам. Единственный критерий, по которому мы можем отличить прикладные процессы и системные сервисные процессы, состоит в том, что процесс операционной системы управляет каким-либо ресурсом в интересах прикладного процесса. Драйверы устройств - это процессы, которые являются посредниками между операционной системой и устройствами и избавляют операционную систему от необходимости иметь дело с особенностями конкретных устройств. Так как драйверы запускаются как обычные процессы, добавление нового драйвера в QNX не влияет на другие части операционной системы. Таким образом, добавление нового драйвера в QNX не требует ничего, кроме непосредственно запуска этого драйвера. После запуска и завершения процедуры инициализации, драйвер может выбрать один из двух вариантов поведения: стать расширением определенного системного процесса; продолжать выполнение как независимый процесс. 59. Состояния потоков и системные вызовы Системный вызов – это требование к ОС (ядру) произвести аппаратно/системно специфическую или привилегированную операцию. Neutrino поддерживает 48 системных вызовов (QNX - 14), обеспечивающих нити, передачу сообщений, сигналы, системные часы и таймеры, обработку прерываний и механизмы синхронизации нитей. На картинке ниже изображены состояния процессов, вместо которых в лекциях упоминались потоки. Для более точного соответствия лекциям следует добавить к Reply() Error(), а также перед названием каждой функции писать Msg1, например, MsgReply(). В версии QNX 4.х использовались функции без Msg, этот префикс появился с Neutrino, хотя механизм взаимодействия посредством сообщений остался прежним. 1 Состояния процесса в типичной транзакции Send-Receive-Reply 60. Механизмы межпроцессного взаимодействия в QNX Если по лекциям, то Для межпроцессного взаимодействия используются следующие службы: название уровень обмен сообщениями ядра сигналы ядра очереди сообщений POSIX внешнего процесса механизм разделения памяти адм. процесса именованные каналы внешнего процесса неименованные каналы внешнего процесса Если из инета, то Ядро QNX поддерживает три типа связи между процессами: сообщениями, proxy и сигналами. Основной формой IPC в системе QNX является обмен сообщениями. Сообщения обеспечивают синхронную связь между взаимодействующими процессами, при которой процесс, посылающий сообщение, требует подтверждения о его приеме и, возможно, ответа на сообщение. Связь посредством proxy представляет собой особый вид передачи сообщений. Этот вид связи специально предназначен для оповещения о событиях, при которых процесс-отправитель не нуждается во взаимодействии с процессом-получателем. Связь сигналами - это традиционная форма IPC. Сигналы используются для обеспечения асинхронной связи между процессами. 61. Службы синхронизации QNX Название Мютекс Условная переменная Ждущая блокировка2 Барьер Блокировка чтения/записи Семафор FIFO-планирование Сообщения Атомарные операции 2 Только внутри процессов 3 Только для именованных семафоров Межзадачная поддержка + + + + + + + Сетевая поддержка +3 + - Все примитивы синхронизации реализуются ядром, кроме барьеров, ждущих блокировок, блокировок чт./зап., которые основаны на мютексах и условных переменных, и атомарных операций, которые либо выполняются процессором, либо эмулируются ядром. Усл. переменная используется для проверки секции по условию pthread_cond_wait/signal/broadcast/timeout(). QNX предоставляет упрощенный вариант использования условной переменной, для блокирования (остановки) потока, предоставляя интерфейс т.н. ждущей блокировки (sleepon). Для использования этого механизма не нужно явно создавать никаких объектов синхронизации, это делает ОС. Внешне ждущие блокировки выглядят как набор функций ожидания и освобождения, при этом последовательность действий аналогична использованию мютексов и условных переменных. За этим интерфейсом на самом деле скрывается один мютекс и несколько дополнительных условных переменных. Использование функций ожидания должно проходить внутри участка кода, отмеченного вызовами блокирования и разблокирования ассоциированного с ждущей блокировкой мютекса. Одним из основных недостатков ждущей блокировки является то, что для всех потоков и всех ключей ожидания используется один общий мютекс. ОС не может отслеживать взаимные блокировки потоков при использовании ждущих блокировок. Барьер – механизм синхронизации, позволяющий координировать работу нескольких потоков. По умолчанию атрибуты барьера запрещают доступ к элементу синхронизации из других процессов. int pthread_barrierattr_init( pthread_barrierattr_t * attr ); int pthread_barrierattr_destroy( pthread_barrierattr_t * attr ); Функция инициализации и разрушения возвращает следующие значения: EOK – успешное выполнение; ENOMEM – недостаточно памяти для инициализации атрибутов барьера; EINVAL – передан неверный объект атрибутов барьера attr; EBUSY – попытка инициализации инициализированного барьера/в настоящее время есть потоки блокированные не барьере; EAGAIN – Системе не хватает ресурсов для инициализации барьера; EFAULT – Сбой произошел при обращении ядра к аргументам. FIFO-планирование применяется в системах без SMP для синхронизации потоков с одинаковым приоритетом. Нет необходимости явного вызова процедуры синхронизации. Атомарные операции хранятся в заголовочном файле <atomic.h> и служат для незапрещения прерываний. 62. Send и Reply-управляемая передача сообщений Способ передачи сообщений называется Send-управляемым, если процесс, требующий обслуживания, инициирует работу, посылая сообщение, а обслуживающий процесс завершает работу, выдавая ответ на принятое сообщение. Reply-управляемый способ передачи сообщений - способ, при котором работа инициируется функцией MsgReply(). В соответствии с этим способом "рабочий" процесс посылает сообщение обслуживающему процессу, указывая на то, что он готов к работе. Обслуживающий процесс фиксирует, что "рабочий" процесс послал ему сообщение, но не отвечает ему немедленно. Через некоторое время обслуживающий процесс может ответить "рабочему" процессу. "Рабочий" процесс выполняет свою работу, а затем, завершив ее, посылает обслуживающему процессу сообщение, содержащее результаты. 63. Передача данных и уведомления, импульсы Хз. Не нашел=) 64. Условный прием сообщений Обычно для приема сообщения используется функция Receive(). Этот способ приема сообщений в большинстве случаев является наиболее предпочтительным. Однако, иногда процессу требуется предварительно "знать", было ли ему послано сообщение, чтобы не ожидать поступления сообщения в RECEIVE-блокированном состоянии. Например, процессу требуется обслуживать несколько высокоскоростных устройств, не способных генерировать прерывания, и кроме того, процесс должен отвечать на сообщения, поступающие от других процессов. В этом случае используется функция Creceive(), которая считывает сообщение, если оно становится доступным, или немедленно возвращает управление процессу, если нет ни одного отправленного сообщения. По возможности следует избегать использования функции Creceive(), так как она позволяет процессу непрерывно загружать процессор на соответствующем приоритетном уровне. 65. Чтение сообщения по частям и приём составных сообщений Как правило, сообщения состоят из нескольких дискретных частей. Например, сообщение может иметь заголовок фиксированной длины, за которым следуют данные переменной длины. Для того, чтобы части сообщения эффективно передавались и принимались без копирования во временный рабочий буфер, составное сообщение может формироваться в нескольких раздельных буферах. Этот метод позволяет администраторам ввода/вывода системы QNX Dev и Fsys, обеспечивать высокую производительность. Для работы с составными сообщениями используются следующие функции: Creceivemx() Readmsgmx() Receivemx() Replymx() Sendmx() Writemsgmx() Cоставное сообщение может быть описано с помощью управляющей структуры mx. Ядро собирает части сообщения в единый поток данных. Чтение или запись части сообщения В некоторых случаях предпочтительнее считывать или записывать только часть сообщения для того, чтобы использовать буфер, уже выделенный для сообщения, и не заводить рабочий буфер. Например, администратор ввода/вывода может принимать для записи сообщения, состоящие из заголовка фиксированной длины и переменного количества данных. Заголовок содержит значение количества байт данных (от 0 до 64 Кбайт). Администратор ввода/вывода может принимать сначала только заголовок, а затем, используя функцию Readmsg(), считывать данные переменной длины в соответствующий буфер. Если количество посылаемых данных превышает размер буфера, администратор ввода/вывода может вызывать функцию Readmsg() несколько раз, передавая данные по мере освобождения буфера. Аналогично, функцию Writemsg() можно использовать для сбора и копирования данных в буфер отправителя по мере его освобождения, снижая таким образом требования к размеру внутреннего буфера администратора ввода/вывода. 66. PROXY Proxy представляет собой форму неблокирующей передачи сообщений, специально предназначенную для оповещения о событиях, при которых процесс-отправитель не нуждается во взаимодействии с процессом-получателем. Единственной функцией proxy является посылка фиксированного сообщения процессу, создавшему proxy. Proxy работают по всей сети. Благодаря использованию proxy, процесс или обработчик прерываний может послать сообщение другому процессу, не блокируясь и не ожидая ответа. Ниже приведены некоторые примеры использования proxy: процесс оповещает другой процесс о наступлении некоторого события, не желая при этом оставаться SEND-блокированным до тех пор, пока получатель не выдаст Receive() и Reply(); процесс посылает данные другому процессу, но не требует ни ответа, ни другого подтверждения о том, что получатель принял сообщение; обработчик прерываний оповещает процесс о том, что некоторые данные доступны для обработки. Proxy создаются с помощью функции qnx_proxy_attach(). Любой процесс или обработчик прерываний, которому известен идентификатор proxy, может воспользоваться функцией Trigger() для того, чтобы выдать заранее определенное сообщение. Запросами Trigger() управляет ядро. Процесс proxy может быть запущен несколько раз: выдача сообщения происходит каждый раз при его запуске. Процесс proxy может накопить в очереди для выдачи до 65535 сообщений.