Передать событие клиенту
#include <sys/neutrino.h>int MsgDeliverEvent( int rcvid,const struct sigevent *event );int MsgDeliverEvent_r( int rcvid,const struct sigevent *event );
<sys/siginfo.h>
. Тип события расположен в поле event.sigev_notify.libc
Функции MsgDeliverEvent() и MsgDeliverEvent_r() отправляют event от сервера к клиенту через соединение с каналом. Обычно эти функции используются для асинхронного ввода-вывода и асинхронных уведомлений клиентов, которые не хотят блокироваться по ожиданию ответа от сервера.
Данные функции идентичны за исключением способа возврата ошибок.
Поскольку сервер может явно посылать любые события, то удобным подходом является получение сервером от клиента структуры struct sigevent, уже содержащей внеобходимые для отправки события данные. Это сообщение также содержит информацию для сервера об условиях, при которых требуется уведомлять клиента данным событием. Сервер сохраняет rcvid из MsgReceivev*() и событие из сообщения без необходимости выяснять, что это за событие. Когда на сервере возникают условия, заданные клиентом в сообщении, например, данные готовы для чтения клиентом, сервер вызывает MsgDeliverEvent(), передавая в качестве параметров сохраненные rcvid и event.
Для программного прерывания клиента с помощью сигнала при возникновении события может использоваться набор уведомлений SIGEV_SIGNAL
. Клиент может обрабатывать сигналы синхронным образом с помощью функции SignalWaitinfo(), при вызове которой клиент будет ожидать сигнал. При реализации синхронности получения событий предпочтительнее по возможности использовать события типа SIGEV_PULSE
. В этом случае клиент посылает серверам сообщения и запрашивает уведомления с помощью импульсов.
Более редкими типами событий являются SIGEV_UNBLOCK
и SIGEV_INTR
.
MsgDeliverEvent() следует использовать когда двум процессам нужно взаимодействовать друг с другом так, чтобы исключить вероятность мертвой взаимной блокировки. Блокирующая природа механизма MsgSend*() требует, чтобы у процессов была иерархия, в которой поток отправки сообщений направлен в одну сторону, а поток ответов – в другую.
На следующем рисунке процессы на уровне A могут посылать сообщения процессам на уровни B или C. Процессы на уровне B могут посылать сообщения на уровень С, но никогда не должны посылать сообщений на уровень A. Подобным образом процессы на уровне C не должны отправлять сообщений на уровень A или B. Для A процессы уровней B и C являются серверами. Для B процессы A являются клиентами, а процессы C являются серверами.
Такие иерархии легко проектируются и предотвращают взаимные блокировки (deadlocks). При нарушении данных правил может возникнуть взаимная блокировка как показано на рисунке ниже:
Часто требуется передача потока данных в обратном направлении иерархии. Например, A посылает к B запрос на уведомления о готовности данных. B сразу отвечает A. Позже, когда B будет иметь данные, запрошенные A, В проинформирует об этом A. B не может стать сообщения для A, поскольку в случае, если А в тоже самое время отправит сообщение В, произойдет взаимная блокировка.
Решением является использование в поцессах B неблокирующей функции MsgDeliverEvent() для информирования A. A получает импульс и отправляет B сообщение с запросом данных. В ответ B отправляет данные. Это является основой асинхронного ввода-вывода. Клиенты шлют серверам сообщения по мере необходимости, а сервера для того чтобы сподвигнуть клиентов на отправку сообщений используют импульсы. Это проиллюстрировано в следующей таблице:
Сообщение | Назначение |
---|---|
A шлет сообщение → B | Запрос асинхронного ввода-вывода |
B отвечает → A | Получение запроса подтверждено |
B шлет импульс → A | Запрошенные данные готовы |
A шлет сообщение → B | Запрос данных |
B отвечает → A | Ответ, содержащий данные |
![]() | При проектировании клиент-серверных приложений MsgDeliverEvent() обычно используется сервером, а MsgSendPulse() клиентом. |
Отсутствует. В случае сетевого взаимодействия могут получать управления потоки с более низким приоритетом.
В случае локального взаимодействия MsgDeliverEvent() всегда возвращает корректное значение успеха или ошибки. При использовании MsgDeliverEvent() для сетевого взаимодействия не гарантируется достоверность кода возврата. Это происходит из-за того, что MsgDeliverEvent() вынуждена блокироваться, ожидая, пока выполниться взаимодействие между двумя модулями lsm-qnet.so
.
В общем случае это не проблема, поскольку MsgDeliverEvent() используется для решения задач клиента - если клиента не существует, то, очевидно, его не заботит, что ему не доставлено событие. При этом сервер, отправив событие, продолжает заниматься своими делами, независимо от того, доставлено событие или нет.
-1
, код ошибки записывается в errno. Любое другое возвращенное значение считается успешным завершением.EOK
возвращается при успешном завершении. Функция НЕ устанавливает errno. При возникновении ошибки функция возвращает один из представленных ниже кодов.
SIGEV_SIGNAL
. SIGEV_PULSE
). Рассмотрим пример, в котором клиент запрашивает у сервера уведомить о чем-то, что произойдет позже (в данном случае сервер уведомляет после двухсекундного "сна"). Сервер будет уведомлять клиента импульсом, используя MsgDeliverEvent().
Заголовочный файл, используемый и клиентом client.c
, и сервером server.c:
struct my_msg{short type;struct sigevent event;};#define MY_PULSE_CODE (_PULSE_CODE_MINAVAIL + 5)#define MSG_GIVE_PULSE (_IO_MAX + 4)#define MY_SERV "my_server_name"
Исходный текст клиента, который заполняет структуру struct sigevent, а затем принимает импульс:
/* client.c */#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <sys/neutrino.h>#include <sys/iomsg.h>#include "my_hdr.h"int main( int argc, char **argv ){int chid, coid, srv_coid, rcvid;struct my_msg msg;struct _pulse pulse;/* Создаем канал для получения импульсов-уведомлений */chid = ChannelCreate( 0 );/* Создаем соединение, через которое будет отправляться импульс в ранее созданный канал */coid = ConnectAttach( 0, 0, chid, _NTO_SIDE_CHANNEL, 0 );/* Заполняем (инициализируем) структуру event, чтобы использовать ее для отправки импульсов */SIGEV_PULSE_INIT( &msg.event, coid, SIGEV_PULSE_PRIO_INHERIT,MY_PULSE_CODE, 0 );msg.type = MSG_GIVE_PULSE;/* Ищем сервер */if ( (srv_coid = name_open( MY_SERV, 0 )) == -1 ){printf( "failed to find server, errno %d\n", errno );exit( 1 );}/* Отправляем серверу инициализированную структуру event типа импульс,чтобы он впеоследствии прислал нам импульс */MsgSend( srv_coid, &msg, sizeof( msg ), NULL, 0 );/* Ждем импульс от сервера */rcvid = MsgReceivePulse( chid, &pulse, sizeof( pulse ), NULL );printf( "got pulse with code %d, waiting for %d\n", pulse.code,MY_PULSE_CODE );return (0);}
Исходный текст сервера, который отправляет импульс, заданный в структуре struct sigevent:
/* server.c */#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <sys/neutrino.h>#include <sys/iomsg.h>#include <sys/iofunc.h>#include <sys/dispatch.h>#include "my_hdr.h"int main( int argc, char **argv ){int rcvid;union {struct my_msg mine;struct _pulse pulse;} msg;name_attach_t *attach;/* Регистрируем имя, чтобы клиент мог найти наш сервер. *//* Канал сервера будет в одном из полей структуры attach */if ( (attach = name_attach( NULL, MY_SERV, 0 )) == NULL ){printf( "server:failed to attach name, errno %d\n", errno );exit( 1 );}/* Ждем сообщение от клиента. */for ( ;; ){rcvid = MsgReceive( attach->chid, &msg, sizeof( msg ), NULL );switch( msg.mine.type ){case _PULSE_CODE_DISCONNECT:ConnectDetach( msg.pulse.scoid );break;case _IO_CONNECT:MsgReply( rcvid, 0, NULL, 0 );break;case MSG_GIVE_PULSE:/* Ждем время, через которое нужно уведомить клиента */sleep(2);/** Уведомляем клиента*/MsgDeliverEvent( rcvid, &msg.mine.event );printf( "server:delivered event\n" );return (0);default:printf( "server: unexpected message %d\n", msg.mine.type );return (0);}}return (0);}
ЗОСРВ «Нейтрино»
Если в качестве типа уведомления задан импульс, то в случае сбоя при отправке события сервером либо импульс будет утерян, либо серверу вернется код ошибки.
MsgReceive(), MsgReceivev(), MsgSend(), MsgSendPulse(), MsgSendv(), struct sigevent, SignalWaitinfo()
Предыдущий раздел: Описание API системной библиотеки