IPC посредством сообщений
Сообщения в QNX — это пакеты байт, которые синхронно передаются от одного процесса к другому. QNX при этом не анализирует содержание сообщения. Передаваемые данные понятны только отправителю и получателю и никому более.
Примитивы передачи сообщений
Для непосредственной связи друг с другом взаимодействующие процессы используют следующие функции языка программирования Си:
Эти функции могут быть использованы как локально, т.е. для связи между процессами на одном компьютере, так и в пределах сети, т.е. для связи между процессами на разных узлах.
Следует заметить, однако, что далеко не всегда возникает необходимость использовать функции Send(), Receive() и Reply() в явном виде. Библиотека функций языка Си в QNX построена на основе использования сообщений — в результате, когда процесс использует стандартные механизмы передачи данных (такие, как, например, программный канал — pipe ), он косвенным образом использует передачу сообщений.
Процесс A посылает сообщение процессу B, который получает его, обрабатывает и посылает ответ
На рисунке изображена последовательность событий, имеющих место, когда два процесса, процесс A и процесс B, используют функции Send(), Receive() и Reply() для связи друг с другом:
Процесс A посылает сообщение процессу B, вызвав функцию Send(), которая передает соответствующий запрос ядру. В этот момент времени процесс A переходит в SEND-блокированное состояние и остается в этом состоянии до тех пор, пока процесс B не вызовет функцию Receive() для получения сообщения.
Процесс B вызывает Receive() и получает сообщение от процесса A. При этом состояние процесса A изменяется на REPLY-блокирован. Процесс B при вызове функции Receive() в данном случае не блокируется, т.к. к этому моменту его уже ожидало сообщение от процесса A.
Заметьте, что если бы процесс B вызвал Receive() до того, как ему было послано сообщение, то он бы попал в состояние RECEIVE-блокирован до получения сообщения. В этом случае процесс-отправитель сообщения немедленно после посылки сообщения попал бы в состояние REPLY-блокирован.
Процесс B выполняет обработку полученного от процесса A сообщения и затем вызывает функцию Reply(). Ответное сообщение передается процессу A, который переходит в состояние готовности к выполнению. Вызов Reply() не блокирует процесс B, который также готов к выполнению. Какой из этих процессов будет выполняться, зависит от их приоритетов.
Синхронизация процессов
Передача сообщений не только позволяет процессам обмениваться данными, но и предоставляет механизм синхронизации выполнения нескольких взаимодействующих процессов.
Давайте снова рассмотрим приведенный выше рисунок. После того как процесс A вызвал функцию Send(), он не может продолжать выполнение до тех пор, пока не получит ответ на посланное сообщение. Это гарантирует, что выполняемая процессом B по запросу процесса A обработка данных будет завершена прежде, чем процесс A продолжит выполнение. Более того, после вызова процессом B запроса на получение данных Receive(), он не может продолжать выполнение до тех пор, пока не получит следующее сообщение. К содержанию
Блокированные состояния
Когда процессу не разрешается продолжать выполнение, т.к. он должен ожидать окончания определенной стадии протокола передачи сообщения, — процесс называется блокированным.
Возможные блокированные состояния процессов приведены в следующей таблице:
Изменение состояния процессов в типичном случае передачи сообщения.
Использование Send(), Receive() и Reply()
Давайте теперь более подробно рассмотрим вызовы функций Send(), Receive() и Reply(). Воспользуемся рассмотренным выше примером передачи сообщения от процесса A к процессу B.
Функция Send()
Предположим, что процесс А выдает запрос на передачу сообщения процессу В.Это выполняется посредством вызова функции Send( ).
Send( pid, smsg, rmsg, smsg_len, rmsg_len )
При вызове функции Send() используются следующие аргументы:
Pid — идентификатор процесса(process ID), которому предназначается сообщение(т.е. процесса B); этот идентификатор используется для обращения к процессу со стороны операционной системы и других процессов.
Smsg — буфер сообщения(т.е. сообщение, подлежащее посылке).
Rmsg — буфер ответа(будет содержать ответ от процесса B).
Smsg_len — длина посылаемого сообщения в байтах.
Rmsg_len — максимальная длина ответа, который может принять процесс A, в байтах.
Обратите внимание, что будет передано не более smsg_len байт и не более чем rmsg_len байт будет получено в качестве ответа — это гарантирует, что не произойдет случайного переполнения буферов.
Функция Receive()
Вызвав запрос Receive(), процесс B может получить сообщение, направленное ему процессом A.
Receive( Pid,0, msg, msg_len )
Вызов функции Receive() содержит следующие аргументы:
Pid — идентификатор процесса, который послал сообщение (т.е. процесс A).
0(ноль) — означает, что процесс B желает принять сообщение от любого процесса.
Msg — буфер, куда будет помещено принимаемое сообщение.
Если значения аргументов smsg_len в вызове функции Send() и msg_len в вызове функции Receive() отличаются, друг от друга, то наименьшее из них определяет размер данных, которые будут переданы.
Функция Reply() После успешного получения сообщения от процесса A, процесс B должен ответить процессу A, вызвав функцию Reply().
Reply( pid, reply, reply_len )
Вызов функции Reply() содержит следующие аргументы:
Pid — идентификатор процесса, которому предназначается ответ (т.е. процесс A).
Reply — буфер, содержащий ответ.
Reply_len — длина данных, передаваемых в качестве ответа, в байтах.
Если значения аргументов reply_len при вызове функции Reply() и rmsg_len при вызове функции Send() отличаются друг от друга, то наименьшее из них определяет размер передаваемых данных.
Reply-управляемый обмен сообщениями
Пример обмена сообщениями, который мы только что рассмотрели, иллюстрирует наиболее распространенный случай использования сообщений — случай, когда для процесса, выполняющего функции сервера, нормальным является состояние RECEIVE-блокирован в ожидании каких-либо запросов клиента. Это называется send-управляемый обмен сообщениями: процесс-клиент инициирует действие, посылая сообщения, ответ сервера на сообщение завершает действие.
Существует и другая модель обмена сообщениями, не столь распространенная, как рассмотренная выше, хотя в некоторых ситуациях она даже более предпочтительна: reply-управляемый обмен сообщениями, при котором действие инициируется вызовом функции Reply(). В этом случае процесс-«работник» посылает серверу сообщение, говорящее о том, что он готов к работе. Сервер не отвечает сразу, а «запоминает», что работник готов выполнить его задание. В последствии сервер может инициировать действие, ответив ожидающему задание работнику. Процесс-работник выполнит задание и завершит действие, послав серверу сообщение, содержащее результаты своей работы.


