Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
IPC.doc
Скачиваний:
0
Добавлен:
13.01.2020
Размер:
536.06 Кб
Скачать

Архитектура системы QNX

Рис.1 Микроядро системы QNX координирует работу системных менеджеров (Proc, Fsys, Dev, Net).

Рис. 2. Внутри микроядра QNX

Обмен сообщениями

Рис. 3. Последовательность событий при синхронном обмене сообщениями

Сервер

#include <sys/neutrino.h>

int ChannelCreate (unsigned flags);

int ChannelDestroy (int chid);

Для создания канала сервер должен сделать так:

int chid;

chid = ChannelCreate (0);

Рис. 4 Связь между каналом сервера и клиентским соединением

Клиент

#include <sys/neutrino.h>

int ConnectAttach (int nd, int pid, int chid, unsigned index, int flags);

ConnectDetach (int coid);

Для подключения к каналу сервера клиент должен сделать так:

int coid;

coid = ConnectAttach (0, 77, 1, 0, 0);

Обработка сообщений

В терминах обмена сообщениями, сервер отрабатывает схему обмена в два этапа:

  • этап «приема» (receive);

  • этап «ответа» (reply).

Рис.5 Взаимосвязь функций клиента и сервера при обмене сообщениями

#include <sys/neutrino.h>

int MsgReceive (int chid,void *rmsg, int rbytes, struct _msg_info *info);

int MsgReply (int rcvid, int status, const void *msg, int nbytes);

;

MsgSend(coid, smsg, strlen(smsg)+1, rmsg, sizeof(rmsg));

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

// server.c

#include <sys/neutrino.h>

………….

void server(void )

{

int rcvid; int chid; char message[512];

chid=ChannelCreate(0);

while(1)

{

rcvid=MsgReceive(chid, message, sizeof(message), NULL);

………………………………………………………..

MsgReply(rcvid, EOK, message, sizeof(message)) ;

}

}

//client.c

#include <sys/neutrino.h>

…………..

int main(void)

{…………

char smsg[20] ; char rmsg[200]; int coid; long serv_pid;

coid=ConnectAttach(0, serv_pid, 1, 0, 0);

…………..

if (MsgSend(coid, smsg, strlen(smsg)+1, rmsg, sizeof(rmsg)) = = -1)

{…..

printf("Error MsgSend \n");

}

return(1);

}

Если единственная цель ответа — разблокировать клиента, то достаточно будет только идентификатора отправителя:

  1. MsgReply (rcvid, EOK, NULL, 0);

Если клиент не желает быть блокированным, то такой вызов разблокирует клиента (но не передаст ему никаких данных) и возвратит код ЕОК («успешное завершение»).

Возможно также, что данные уже переданы ранее (с помощью функции MsgWrite()), и больше никаких данных нет.

  1. MsgError (rcvid, EROFS);

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

И так, последовательность событий при обмене сообщениями следующая:

  1. Клиент вызывает функцию MsgSend() и указывает ей на буфер передачи (указателем smsg и длиной sbytes). Данные передаются в буфер функции MsgReceive() на стороне сервера, по адресу rmsg и длиной rbytes. Клиент блокируется.

  2. Функция MsgReceive() сервера разблокируется и возвращает идентификатор отправителя rcvid, который будет впоследствии использован для ответа. Теперь сервер может использовать полученные от клиента данные.

  3. Сервер завершил обработку сообщения и теперь использует идентификатор отправителя rcvid, полученный от функции MsgReceive(), передавая его функции MsgReply(). Заметьте, что местоположение данных для передачи функции MsgReply() задается как указатель на буфер (smsg) определенного размера (sbytes). Ядро передает данные клиенту.

  4. Наконец, ядро передает параметр sts, который используется функцией MsgSend() клиента как возвращаемое значение. После этого клиент разблокируется.

Определение идентификаторов узла, процесса и канала (nd/pid/chid) нужного сервера.

Если один процесс создает другой процесс, тогда это просто — вызов создания процесса возвращает идентификатор вновь созданного процесса. Создающий процесс может либо передать собственные PID и CHID вновь созданному процессу в командной строке, либо вновь созданный процесс может вызвать функцию getppid() для получения идентификатора родительского процесса, и использовать некоторый «известный» идентификатор канала.

Иначе используется один из следующих способов:

  1. Открыть файл с известным именем и сохранить в нем ND/PID/CHID. Такой метод является традиционным для серверов UNIX, когда сервер открывает файл (например, /etc/httpd.pid), записывает туда свой идентификатор процесса в виде строки ASCII и предполагают, что клиенты откроют этот файл и прочитают из него идентификатор.

  2. Использовать для объявления идентификаторов ND/PID/CHID глобальные переменные. Такой способ обычно применяется в многопоточных серверах, которые могут посылать сообщение сами себе. Этот вариант по самой своей природе является очень редким.

  3. Занять часть пространства имен путей и стать администратором ресурсов.

Первый подход относительно прост, но он чреват «загрязнением файловой системы», когда в каталоге /etc лежит куча файлов * .pid.

Поскольку процесс, который создал файл, может умереть, не удалив этот файл, то вы не сможете узнать, жив ли еще этот процесс, пока не попробуете передать ему сообщение. Если комбинация ND/PID/CHID указанная в файле, оказывается настолько старой, что может быть повторно использована другой программой и, получив «чужое» сообщение, эта программа в лучшем случае его проигнорирует, а ведь может и предпринять некорректные действия.

Второй подход, где мы используем глобальные переменные для объявления значений ND/PID/CHID, не является общим решением проблемы, поскольку в нем предполагается способность клиента обратиться к этим глобальным переменным. А поскольку для этого требуется использование разделяемой памяти, это не будет работать в сети.

Обмен длинными сообщениями

Функции MsgRead() и MsgWrite().

MsgRead (int rcvid, void *msg, int nbytes, int offset).

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

rcvid =MsgReceive (chid, header, size of header, 0);

MsgRead (rcvid, rmsg, rbytes, size of header);

………………………………………………..

MsgWrite (int rcvid, void *msg, int nbytes, int offset).

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

Функция MsgWrite() позволяет записать различные компоненты данных в различные места, а затем либо просто разбудить клиента:

MsgWrite (rcvid, header, sizeof (header));

…………………………………………

MsgReply (rcvid, EOK, NULL, 0);

либо сделать это после записи заголовка в начало клиентского буфера:

MsgWrite (rcvid, smsg, sbytes, header);

………………………………………………….

MsgReply (rcvid, ЕОK, header, sizeof (header)).

Здесь важно зарезервировать место под заголовок в начале клиентского буфера.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]