КаталогИндекс раздела
НазадОглавлениеВперед


2. МИКРОЯДРО

2.1. Введение

Ядро операционной системы QNX, представленное на рис. 2, выполняет следующие функции:

Микроядро
Рис. 2

2.2. Связь между процессами

Ядро QNX поддерживает три типа связи между процессами: сообщениями, proxy и сигналами.

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

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

Связь сигналами - это традиционная форма IPC. Сигналы используются для обеспечения асинхронной связи между процессами.

2.3. Связь между процессами посредством сообщений

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

2.3.1. Передача сообщений

Для прямой связи друг с другом взаимодействующие процессы используют следующие функции языка Си:

C-функция Назначение
Send() для посылки сообщений;
Receive() для приема сообщений;
Reply() для ответа процессу, пославшему сообщение.

Эти функции могут использоваться локально или по всей сети.

Обратите внимание на то, что для прямой связи процессов друг с другом необязательно использование функций Send(), Receive() и Reply(). Система библиотечных функций QNX надстроена над системой обмена сообщениями, поэтому процессы могут использовать передачу сообщений косвенно при использовании стандартных сервисных средств, например, программных каналов (pipe).

Процесс А посылает сообщение процессу В, который принимает его, обрабатывает и передает ответ.
Рис. 3

Приведенный выше рис. 3 иллюстрирует простой пример использования функций Send(), Receive() и Reply() при взаимодействии двух процессов - А и В:

  1. Процесс А посылает сообщение процессу В, выдав ядру запрос Send(). С этого момента процесс А становится SEND-блокированным до тех пор, пока процесс В не выдаст Receive(), подтверждая получение сообщения;
  2. Процесс В выдает Receive() процессу А, ожидающему сообщения. Процесс А изменяет свое состояние на REPLY-блокированное. Поскольку от процесса В ожидается сообщение, он не блокируется.
    Обратите внимание на то, что, если бы процесс В выдал Receive() до отправления ему сообщения, он оставался бы RECEIVE-блокированным до момента приема сообщения. В этом случае процесс А (отправитель) перешел бы сразу в REPLY-блокированное состояние после отправления сообщения процессу В;
  3. Процесс В выполняет необходимую обработку, определяемую полученным от процесса А сообщением, и выдает Reply(). Процесс А получает ответное сообщение и разблокировывается. Процесс В также разблокировывается. Какой из процессов начнет выполняться первым, зависит от их относительных приоритетов.

2.3.2. Синхронизация процессов

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

Давайте обратимся снова к приведенному выше рис. 3. После того, как процесс А выдаст запрос Send(), он не сможет выполняться до тех пор, пока не получит ответа на переданное им сообщение. Это служит гарантией того, что обработка данных, выполняемая процессом В для процесса А завершится до того, как процесс А сможет продолжить свою работу. В свою очередь процесс В после выдачи запроса Receive() может продолжать свою работу до поступления другого сообщения.

Более подробно механизм планирования работы процессов рассмотрен в подразделе 2.7 "Планирование процессов".

2.3.3. Состояния блокировок

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

Если процесс выдал Процесс является
Запрос Send(), и отправленное им сообщение еще не получено процессом-получателем SEND-блокированным
Запрос Send(), и отправленное им сообщение получено процессом-получателем, но ответ еще не выдан REPLY-блокированным
Запрос Receive(), но сам еще не получил сообщение RECEIVE-блокированным

Состояния процесса в типичной транзакции Send-Receive-Reply представлены на рис. 4.
Рис. 4

Другие возможные состояния процессов рассмотрены в подразделе 3.3.

2.3.4. Использование функций Send(), Receive() и Reply()

Давайте теперь рассмотрим функции Send(), Receive() и Reply() более подробно. По-прежнему будем пользоваться нашим примером взаимодействия процессов А и В.

Использование функции Send()

Предположим, что процесс А выдает запрос на передачу сообщения процессу В. Запрос оформляется вызовом функции Send()

    Send (pid, smsg, rmsg, smsg_bn, rmsg_len);

Функция Send() имеет следующие аргументы:

pid идентификатор процесса-получателя сообщения (т.е. процесса В); pid - это идентификатор, посредством которого процесс опознается операционной системой и другими процессами;
smsg буфер сообщения (т.е. посылаемого сообщения);
rmsg буфер ответа (в который помещается ответ процесса В);
smsg_len длина посылаемого сообщения;
rmsg_len максимальная длина ответа, который должен получить процесс А.

Обратите внимание на то, что в сообщении будет передано не более, чем smsg_len байт и принято в ответе не более, чем rmsg_len байт, - это служит гарантией того, что буферы никогда не будут переполнены.

Использование функции Receive()

Процесс В может принять запрос Send(), выданный процессом А, с помощью функции Receive()

 pid = Receive (0, msg, msg_len);

Функция Receive() имеет следующие аргументы:

pid идентификатор процесса, пославшего сообщение (т.е. процесса А);
0 (ноль) указывает на то, что процесс В готов принять сообщение от любого процесса;
msg буфер, в который будет принято сообщение;
msg_len  максимальное количество байт данных, которое может поместиться в приемном буфере.

В том случае, если значения smsg_len в функции Send() и msg_len в функции Receive() различаются, то количество передаваемых данных будет определяться наименьшим из них.

Использование функции Reply()

После успешного приема сообщения от процесса А процесс В должен ответить ему, используя функцию Reply()

   Reply (pid, reply, reply_len)

Функция Reply имеет следующие аргументы:

pid идентификатор процесса, которому направляется ответ (т.е. процесса А);
reply буфер ответа;
reply_len длина сообщения, передаваемого в ответе.

Если значения reply_len в функции Reply() и rmsg_len в функции Send() различаются, то количество передаваемых данных определяется наименьшим из них.

2.3.5. Reply-управляемая передача сообщений

Пример передачи сообщений, который мы только что рассмотрели, иллюстрирует наиболее типичный способ передачи, при котором обслуживающий процесс находится в RECEIVE-блокированом состоянии, ожидая запроса от другого процесса на выполнение какой-либо работы. Этот способ передачи сообщений называется Send-управляемым, при котором процесс, требующий обслуживания, инициирует работу, посылая сообщение, а обслуживающий процесс завершает работу, выдавая ответ на принятое сообщение.

Существует еще и другой, менее распространенный, чем Send-управляемый, но в отдельных случаях более предпочтительный способ передачи сообщений, а именно_

  • Reply-управляемый, при котором работа инициируется функцией Reply(). В соответствии с этим способом "рабочий" процесс посылает сообщение обслуживающему процессу, указывая на то, что он готов к работе. Обслуживающий процесс фиксирует, что "рабочий" процесс послал ему сообщение, но не отвечает ему немедленно. Через некоторое время обслуживающий процесс может ответить "рабочему" процессу. "Рабочий" процесс выполняет свою работу, а затем, завершив ее, посылает обслуживающему процессу сообщение, содержащее результаты.

    2.3.6. Дополнительная информация

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

    Обслуживающий процесс принял сообщения от обслуживаемых процессов А и В, но не ответил на них. Сообщения от процессов C, D, E еще не приняты.
    Риc. 5

    2.3.7. Дополнительные возможности передачи сообщений

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

    Условный прием сообщений

    Обычно для приема сообщения используется функция Receive(). Этот способ приема сообщений в большинстве случаев является наиболее предпочтительным.

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

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

    Чтение или запись части сообщения

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

    Например, администратор ввода/вывода может принимать для записи сообщения, состоящие из заголовка фиксированной длины и переменного количества данных. Заголовок содержит значение количества байт данных (от 0 до 64 Кбайт). Администратор ввода/вывода может принимать сначала только заголовок, а затем, используя функцию Readmsg(), считывать данные переменной длины в соответствующий буфер. Если количество посылаемых данных превышает размер буфера, администратор ввода/вывода может вызывать функцию Readmsg() несколько раз, передавая данные по мере освобождения буфера. Аналогично, функцию Writemsg() можно использовать для сбора и копирования данных в буфер отправителя по мере его освобождения, снижая таким образом требования к размеру внутреннего буфера администратора ввода/вывода.

    Передача составных сообщений

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

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

    Cоставное сообщение может быть описано с помощью управляющей структуры mx. Ядро собирает части сообщения в единый поток данных.
    Риc. 6

    2.3.8. Зарезервированные коды сообщений

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

    0X0000 - 0X00FF сообщения Администратора процессов;
    0X0100 - 0X01FF сообщения ввода/вывода (для всех обслуживающих программ);
    0X0200 - 0X02FF сообщения Администратора файловой системы;
    0X0300 - 0X03FF сообщения Администратора устройств;
    0X0400 - 0X04FF сообщения Сетевого администратора;
    0X0500 - 0X0FFF зарезервировано для системных процессов, которые могут появиться в будущем.

    2.4. Связь между процессами посредством proxy

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

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

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

    Процесс proxy может быть запущен несколько раз: выдача сообщения происходит каждый раз при его запуске. Процесс proxy может накопить в очереди для выдачи до 65535 сообщений.

    Обслуживаемый процесс трижды обратился к proxy, в результате чего обслуживающий процесс получил три "законсервированных" сообщения от proxy.
    Рис. 7

    2.5. Связь между процессами посредством сигналов

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

    В системе QNX поддерживается большой набор POSIX-совместимых сигналов, специальные QNX-сигналы, а так же исторически сложившиеся сигналы, используемые в некоторых версиях системы UNIX.

    2.5.1. Генерация сигналов

    Сигнал выдается процессу при наступлении некоторого заранее определенного для данного сигнала события. Процесс может выдать сигнал самому себе.

    Если вы хотите Используйте
    Сгенерировать сигнал из интерпретатора Shell Утилиты kill или slay
    Сгенерировать сигнал из процесса Функции kill() или raise()

    2.5.2. Прием сигналов

    В зависимости от того, каким образом был определен способ обработки сигнала, возможны три варианта его приема:

    1. Если процессу не предписано выполнять каких-либо специальных действия по обработке сигнала, то по умолчанию поступление сигнала прекращает выполнение процесса;
    2. Процесс может проигнорировать сигнал. В этом случае выдача сигнала не влияет на работу процесса (обратите внимание на то, что сигналы SIGCONT, SIGKILL и SIGSTOP не могут быть проигнорированы при обычных условиях);
    3. Процесс может иметь обработчик сигнала, которому передается управление при поступлении сигнала. В этом случае говорят, что процесс может "ловить" сигнал. Фактически такой процесс выполняет обработку программного прерывания. Данные с сигналом не передаются.

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

    2.5.3. Перечень сигналов

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

    Сигнал Обрабатывается Действие по умолчанию Описание
    Стандартные сигналы
    SIGABRT да Завершить процесс Сигнал ненормального завершения, такой же, какой выдается функцией abort()
    SIGALARM да Завершить процесс Сигнал истечения времени, такой же, какой выдается функцией alarm()
    SIGFPE** да Завершить процесс Ошибочная арифметическая операция (целочисленная или с плавающей точкой), например, деление на 0 или операция, приводящая к переполнению
    SIGHUP да Завершить процесс Гибель инициатора сессии, либо зависание на управляющем терминале
    SIGILL да Завершить процесс Обнаружение аппаратной ошибки
    SIGINT да Завершить процесс Интерактивный сигнал внимания ()
    SIGKILL да Завершить процесс Сигнал завершения (следует использовать только в чрезвычайных ситуациях)
    SIGPIPE да Завершить процесс Попытка записи в канал при отсутствии процессов, читающих из него
    SIGOUIT да Завершить процесс Интерактивный сигнал завершения
    SIGSEGV** да Завершить процесс Обнаружение неправильной ссылки в памяти
    SIGTERM да Завершить процесс Сигнал завершения
    SIGUSR1 да Завершить процесс Зарезервирован как 1-й сигнал, определяемый приложением
    SIGUSR2 да Завершить процесс Зарезервирован как 2-й сигнал, определяемый приложением
    Сигналы, управляющие работой процессов
    SIGHLD да Игнорировать сигнал Завершить порожденный процесс
    SIGCONT нет Продолжить процесс Продолжить, если данный процесс задержан задержки; игнориро вать сигнал, если этот процесс не задержан
    SIGSTOP* нет Приостановить процесс Сигнал задержкки процесса
    SIGTSTP* нет Игнорировать сигнал Не поддерживается в QNX
    SIGTTIN нет Игнорировать сигнал Не поддерживается в QNX
    SIGTTOU нет Игнорировать сигнал Не поддерживается в QNX
    Специальные сигналы QNX
    SIGBUS** да Завершить процесс Указывает на ошибку четности в памяти (специальная интерпретация QNX)
    SIGDEV да Завершить процесс Генерируется, когда в Администраторе устройств возникает важное и запрашиваемое событие
    SIGPWR да Завершить процесс Мягкая перезагрузка по нажатию клавиш <CTRL> <ALT> <SHIFT> <DEL> или по выполнению утилиты shutdown
    Исторически оставшиеся сигналы UNIX
    SIGIOT*** да Завершить процесс Команда IOT
    SIGSIS*** да Завершить процесс Некорректный аргумент в системном вызове
    SIGWINCH*** да Завершить процесс Смена окна
    SIGURG*** да Завершить процесс Выполнение необходимого условия
    SIGPOLL*** да Завершить процесс Выполнение выбранного события
    SIGEMT*** да Завершить процесс Команда EMT (эмулятор внутреннего прерывания)
    SIGTRAP да Завершить процесс Неподдерживаемое программное прерывание

    Условные обозначения:

    *обслуживающий процесс может "защитить" себя от этого сигнала посредством функции qnx_pflags(). Для этого обслуживающий процесс должен иметь уровень суперпользователя;
    **процесс завершается в случае возникновения второго сбоя во время обработки процессом первого;
    *** этот сигнал оставлен для исторической совместимости с некоторыми версиями системы UNIX, он не генерируется никакими компонентами системы QNX.

    2.5.4. Определение способа обработки сигнала

    Для задания способа обработки сигнала следует воспользоваться функцией ANSI C signal() или функцией POSIX sigaction().

    Функция sigaction() предоставляет больше возможностей по управлению средой обработки сигнала.

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

    2.5.5. Обработка сигналов

    Отметим некоторые особенности работы процессов, которые "ловят" сигналы с помощью обработчика сигналов.

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

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

    2.5.6. Рекомендуемые функции для обработчиков сигналов

    Приведенные ниже библиотечные функции стандартов POSIX и ANSI C рекомендуются к использованию в обработчиках сигналов. Не следует пытаться использовать другие библиотечные функции, так как результаты этого могут быть непредсказуемы. Функции пользователя, используемые в вашей программе, должны быть обязательно повторно входимыми.

    _exit()         getegid()     rmdir()         tcdrain()
    access()        geteuid()     setgid()        tcflow()
    alarm()         getgid()      setpgid()       tcflush()
    cfgetispeed()   getgroups()   setsid()        tcgetattr()
    cfgetospeed()   getpgrp()     setnid()        tcgetpgrp()
    cfsetispeed()   getpid()      sigaction()     tcsendbreak()
    cfsetospeed()   getppid()     sigaddset()     tcsetattr()
    chdir()         getuid()      sigdelset()     tcgetgrp()
    chmod()         kill()        sigemptyset()   time()
    chown()         link()        sigfillset()    times()
    close()         lseek()       sigismember()   umask()
    creat()         mkdir()       signal()        uname()
    dup2()          mkfifo()      sigpending()    unlink()
    dup()           open()        sigprocmask()   ustat()
    execle()        pathconf()    sigsuspend()    utime()
    execve()        pause()       slup()          wait()
    fcntl()         pipe()        stat()          waitpid()
    fork()          read()        sysconf()       write()
    fstat()         rename()
    

    2.5.7. Блокировка сигналов

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

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

    В некоторых версиях системы UNIX работа с обработчиком сигналов организована некорректно, так как в них не предусмотрена блокировка сигналов. В результате в некоторых приложениях, работающих под управлением UNIX, используется функция signal() внутри обработчика прерываний с целью "перевзвода" обработчика. В этом случае может возникнуть одна из двух аварийных ситуаций. Во-первых, если другой сигнал поступает, во время работы обработчика, но вызова функции signal() еще не было, то программа будет снята с обработки. Во-вторых, если сигнал поступает сразу же после вызова обработчиком функции signal(), то обработчик будет запускаться рекурсивно. В QNX выполняется блокировка сигналов, поэтому указанные выше проблемы не могут возникнуть. Нет необходимости вызывать signal() из обработчика. Если требуется выйти из любой точки обработчика, то следует воспользоваться функцией siglongjmp().

    2.5.8. Сигналы и сообщения

    Существует важная взаимосвязь между сигналами и сообщениями. Если при генерации сигнала ваш процесс окажется SEND-блокированным или RECEIVE-блокированным (причем имеется обработчик сигналов), то будут выполняться следующие действия:

    1. процесс разблокировывается;
    2. выполняется обработка сигнала;
    3. функции Send() или Receive() возвращают управление с кодом ошибки.

    Если процесс был SEND-блокированным, то проблемы не возникает, так как получатель не получит сообщение. Но если процесс был REPLY-блокированным, то неизвестно, было обработано отправленное сообщение или нет, а следовательно неизвестно, нужно ли еще раз выдавать Send().

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

    1. нормально завершить первоначальный запрос: отправитель будет уведомлен о том, что сообщение было обработано надлежащим образом;
    2. освободить все закрепленные ресурсы и возвратить управление с кодом ошибки, указывающим на то, что процесс был разблокирован сигналом: отправитель получит чистый код ошибки.

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

    2.6. Связь между процессами в сети

    2.6.1. Виртуальные каналы

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

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

    Виртуальные каналы (ВК) способствуют эффективному использованию ресурсов во всей сети QNX по нескольким причинам:

    1. при создании виртуального канала имеется возможность задать работу с сообщениями определенной длины: это означает, что вы можете распределить ресурсы для обработки сообщения. Тем не менее, если потребуется послать сообщение, длина которого превышает максимально заданную, виртуальный канал автоматически изменит установленный максимальный размер буфера в соответствии с длиной передаваемого сообщения;
    2. если два процесса, находящиеся на разных узлах, взаимодействуют между собой более, чем через один виртуальный канал, виртуальные каналы разделяются во времени, так как между процессами существует только один реальный виртуальный канал. Эта ситуация часто возникает, когда процесс обращается к нескольким файлам удаленной файловой системы;
    3. если процесс подключается к существующему разделенному виртуальному каналу и запрашивает размер буфера больший, чем тот, который используется в данное время, размер буфера автоматически увеличивается;
    4. когда процесс завершается, все связанные с ним виртуальные каналы освобождаются.

    2.6.2. Виртуальные процессы

    Процесс-отправитель отвечает за установку виртуального канала между собой и процессом, с которым устанавливается связь. Для этого процесс-отправитель обычно вызывает функцию qnx_vc_attach(). При этом, кроме создания виртуального канала, на каждом конце канала создается виртуальный процесс с идентификатором - VID. Для каждого процесса на обоих концах виртуального канала VID представляет собой идентификатор удаленного процесса, с которым устанавливается связь. Процессы связываются друг с другом посредством VID.

    Например, на рис. 8 виртуальный канал соединяет процессы PID1 и PID2. На узле 20, где находится PID1, VID2 представляет PID2. На узле 40, где находится PID2, VID1 представляет PID1. PID1 и PID2 могут относиться к виртуальному процессу на своем узле, как к любому другому локальному процессу: посылать и принимать сообщения, выдавать сигналы, ожидать и т.п. Так, например, PID1 может послать сообщение к VID на своем конце виртуального канала, которое будет передано по сети к VID на другом конце виртуального канала, представляющему там PID1. Там VID1 передает сообщение PID2.

    Связь по сети осуществляется посредством виртуальных каналов. Когда процесс PID1 посылает сообщение VID2 запрос send проходит по виртуальному каналу, в результате чего PID2 получает сообщение от VID1.
    Рис. 8

    Каждый VID обеспечивает соединение, которое содержит следующую информацию:

    Для управления параметрами, связанными с проверкой целостности виртуального канала, используется утилита netpoll.

    2.7. Планирование процессов

    2.7.1. Когда выполняется планирование

    Планировщик ядра запускается в следующих случаях:

    2.7.2. Приоритеты процессов

    В системе QNX каждому процессу присваивается приоритет. Планировщик выбирает для выполнения процессы, находящиеся в состоянии ГОТОВ, в соответствии с их приоритетами. (Центральный процессор может использовать только процесс, находящийся в состоянии ГОТОВ.) Для выполнения выбирается процесс, имеющий наивысший приоритет.

    На рис. 9 представлен пример выполнения процессов в соответствии с приоритетом.

    Очередь процессов (A-F), находящихся в состоянии ГОТОВ. Остальные процессы (G-Z) блокированы. В данный момент выполняется процесс А. Процессы А, B и С имеют высший приоритет, поэтому они будут разделять процессорное время в соответствии с установленным алгоритмом планирования.
    Рис. 9

    Процессам присваиваются приоритеты в диапазоне от 0 (низший) до 31 (высший). По умолчанию процесс наследует приоритет от породившего его процесса; обычно он равен 10 для приложений, запускаемых из интерпретатора Shell.

    Если вы хотите Используйте
    Определить приоритет процесса Функцию getprio()
    Задать приоритет процессу Функцию setprio()

    2.7.3. Методы планирования

    Для удовлетворения потребностей разных приложений в системе QNX реализованы три метода планирования:

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

    Запомните, что данные методы планирования используются только тогда, когда два или более процессов, разделяющих один и тот же приоритет, находятся в состоянии ГОТОВ (т.е. процессы непосредственно конкурируют друг с другом). Если в состояние ГОТОВ переходит процесс, имеющий более высокий приоритет, он немедленно выгружает все процессы с меньшим приоритетом.

    Три процесса, имеющие одинаковые приоритеты, находятся в состоянии ГОТОВ. Если процесс А блокируется, процесс В начнет выполняться.
    Рис. 10

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

    Если вы хотите Используйте
    Определить метод планирования для процесса Функцию getscheduler()
    Установить метод планирования для процесса Функцию setscheduler()

    Планирование по принципу простой очереди

    При планировании по принципу простой очереди процесс, выбранный для выполнения, продолжает работать до тех пор, пока он:

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

    Круговой метод планирования

    При круговом методе планирования процесс, выбранный для выполнения, продолжает работать до тех пор, пока он:

    Квант времени - это единица временного интервала, закрепляемая за каждым процессом. По истечении кванта времени, процесс выгружается, и управление передается процессу, находящемуся на том же уровне приоритета в состоянии ГОТОВ. Квант времени равен 100 миллисекундам.

    За исключением квантования времени круговой метод планирования идентичен планированию по принципу простой очереди.

    Адаптивное планирование

    При адаптивном планировании процесс ведет себя следующим образом:

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

    Программы, запущенные из интерпретатора Shell, используют по умолчанию адаптивный метод планирования.

    Методы планирования

    Процесс А продолжает выполняться до истечения кванта времени, после чего в зависимости от алгоритма планирования определяется, что произойдет дальше.
    Рис. 11

    Приоритет, управляемый клиентом

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

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

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

    Для разрешения этой проблемы, серверу можно устанавливать приоритет, соответствующий приоритету клиента, который послал ему сообщение. Когда сервер получает сообщение, его приоритет становится равным приоритету клиента. Обратите внимание на то, что меняется только приоритет, а метод планирования остается тем же. Если при работе сервера поступает другое сообщение, то приоритет сервера увеличивается в том случае, если приоритет нового клиента окажется выше приоритета сервера. В результате новый клиент "выравнивает" приоритет сервера под свой, позволяя ему закончить выполнение текущего запроса и перейти к выполнению вновь поступившего. Если этого не сделать, то приоритет нового клиента понизился бы, так как он заблокировался бы на низкоприоритетном сервере.

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

    Для установки приоритета, управляемого клиентом, воспользуйтесь функцией qnx_pflags()

        qnx_pflags(~0, _PPF_PRIORITY_FLOAT | _PPF_PRIORITY_REC, 0, 0);
    

    2.8. О работе в реальном времени

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

    2.8.1. Задержка прерывания

    Задержка прерывания - это интервал времени между приемом аппаратного прерывания и началом выполнения первой команды обработчика данного прерывания. В системе QNX все прерывания открыты все время, поэтому задержка прерывания обычно незначительна. Но некоторые критические программы требуют, чтобы на время их выполнения прерывания были закрыты. Максимальное время закрытия прерывания обычно определяет худший случай задержки прерывания; следует отметить, что в системе QNX это время очень мало.

    На рис._12 представлена диаграмма обработки аппаратного прерывания соответствующим обработчиком прерываний. Обработчик прерываний либо просто возвращает управление процессу, либо возвращает управление и вызывает "срабатывание" proxy.

    Методы планирования

    Обработчик прерываний нормально отрабатывает. Времена даны для процессора 386, 20 МГц в защищенном режиме.
    Рис. 12

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

    2.8.2. Задержка планирования

    В некоторых случаях низкоприоритетный обработчик аппаратных прерываний должен планировать выполнение высокоприоритетных процессов. В этом случае обработчик прерываний возвращает управление и вызывает срабатывание proxy. Это и есть вторая форма задержки - задержка планирования, которую мы рассмотрим ниже.

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

    На рис. 13 представлена диаграмма задержки планирования.

    Методы планирования

    Обработчик прерываний завершает работу и инициирует "срабатывание" proxy. Времена даны для процессора 386, 20 МГц в защищенном режиме.
    Рис. 13

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

    2.8.3. Вложенные прерывания

    Поскольку архитектура микрокомпьютера позволяет присваивать аппаратным прерываниям приоритеты, то высокоуровневые прерывания могут вытеснять низкоуровневые.

    Этот механизм полностью поддерживается в системе QNX.

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

    На рис. 14 представлен пример прерывания процесса.

    Методы планирования

    Выполняется процесс А. Прерывание IRQx запускает обработчик прерываний Intx, который вытесняется прерыванием IRQy и его обработчиком Inty. Inty вызывает "срабатывание" proxy, которое запускает процесс В, а Intx вызывает "срабатывание" proxy, запускающее процесс С.
    Рис. 14


    НазадОглавлениеВперед
    КаталогИндекс раздела