Главная страница
Навигация по странице:

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

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

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

  • 2.3.1 Обработка сигналов

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


    Скачать 3.97 Mb.
    НазваниеОсновные принципы, которые позволяют создавать операционные системы реального времени
    АнкорОперационная система реального времени QNX
    Дата20.12.2022
    Размер3.97 Mb.
    Формат файлаrtf
    Имя файлакурсовая работа.rtf
    ТипДокументы
    #853934
    страница2 из 3
    1   2   3

    2.1.1 Использование функций 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() различаются, то количество передаваемых данных определяется наименьшим из них.

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

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

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

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

    • При выдаче запроса Reply() данные, содержащиеся в ответном сообщении, передаются от отвечающего процесса REPLY-блокированному процессу за одну операцию. Функция Reply() не блокирует отвечающий процесс, так как REPLY-блокированный процесс разблокировывается сразу после того, как данные скопируются в его адресное пространство.

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

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

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

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

    • Несколько процессов могут посылать сообщения одному процессу. Обычно процесс-получатель принимает сообщения в порядке их поступления, однако, может быть установлен режим приема сообщений в порядке приоритетов процессов-отправителей, представленный на рисунке 3.




    Рисунок 3. Режим приемов сообщений в порядке приоритетов.
    2.1.2 Дополнительные возможности передачи сообщений

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

    • условный прием сообщений;

    • чтение и запись части сообщения;

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

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

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

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

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

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

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

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

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

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

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

    • Creceivemx()

    • Readmsgmx()

    • Receivemx()

    • Replymx()

    • Sendmx()

    • Writemsgmx()




    Рисунок 4. Составное сообщение может быть описано с помощью управляющей структуры. Ядро собирает части сообщения в единый поток данных.
    Зарезервированные коды сообщений:

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

    0X0000 - 0X00FF

    сообщения Администратора процессов;

    0X0100 - 0X01FF

    сообщения ввода/вывода (для всех обслуживающих программ);

    0X0200 - 0X02FF

    сообщения Администратора файловой системы;

    0X0300 - 0X03FF

    сообщения Администратора устройств;

    0X0400 - 0X04FF

    сообщения Сетевого администратора;

    0X0500 - 0X0FFF

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


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

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

    • процесс оповещает другой процесс о наступлении некоторого события, не желая при этом оставаться SEND-блокированным до тех пор, пока получатель не выдаст Receive() и Reply();

    • процесс посылает данные другому процессу, но не требует ни ответа, ни другого подтверждения о том, что получатель принял сообщение;

    • обработчик прерываний оповещает процесс о том, что некоторые данные доступны для обработки.

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

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


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

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

    Генерация сигналов происходит следующим образом: сигнал выдается процессу при наступлении некоторого заранее определенного для данного сигнала события. Процесс может выдать сигнал самому себе.
    Таблица 4. Работа с сигналами в QNX.

    Если вы хотите

    Используйте

    Сгенерировать сигнал из интерпретатора Shell

    Утилиты kill или slay

    Сгенерировать сигнал из процесса

    Функции kill() или raise()


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

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

    2. Процесс может проигнорировать сигнал. В этом случае выдача сигнала не влияет на работу процесса (обратите внимание на то, что сигналы SIGCONT, SIGKILL и SIGSTOP не могут быть проигнорированы при обычных условиях);

    3. Процесс может иметь обработчик сигнала, которому передается управление при поступлении сигнала. В этом случае говорят, что процесс может "ловить" сигнал. Фактически такой процесс выполняет обработку программного прерывания. Данные с сигналом не передаются.

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

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


    Таблица 6. Сигналы, управляющие работой процессов.


    Таблица 7. Специальные сигналы QNX.


    Таблица 8. Исторически оставшиеся сигналы Unix.


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

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

    ** процесс завершается в случае возникновения второго сбоя во время обработки процессом первого;

    *** этот сигнал оставлен для исторической совместимости с некоторыми версиями системы UNIX, он не генерируется никакими компонентами системы QNX.
    2.3.1 Обработка сигналов

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

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

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

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

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

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

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

    Приведенные ниже библиотечные функции стандартов 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()
    Блокировка сигналов.

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

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

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

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

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

    1. процесс разблокировывается;

    2. выполняется обработка сигнала;

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

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

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

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

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

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


    написать администратору сайта