MsgDeliverEvent(), MsgDeliverEvent_r()

Передать событие клиенту

Прототип:

#include <sys/neutrino.h>
int MsgDeliverEvent( int rcvid,
const struct sigevent *event );
int MsgDeliverEvent_r( int rcvid,
const struct sigevent *event );

Аргументы:

rcvid
Идентификатор сообщения, возвращаемый потоку-серверу функцией MsgReceivev*() при приеме сообщения.
event
Указатель на структуру struct sigevent, содержащую событие, которое должно быть сгенерировано. Возможные события определены в <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 являются серверами.

msgsendpulse1.png
Рисунок 1. Иерархия процессов

Такие иерархии легко проектируются и предотвращают взаимные блокировки (deadlocks). При нарушении данных правил может возникнуть взаимная блокировка как показано на рисунке ниже:

msgsendpulse2.png
Рисунок 2. Взаимная блокировка при неправильной отправке сообщений между процессами

Часто требуется передача потока данных в обратном направлении иерархии. Например, 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 Ответ, содержащий данные


Note: При проектировании клиент-серверных приложений MsgDeliverEvent() обычно используется сервером, а MsgSendPulse() клиентом.

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

Отсутствует. В случае сетевого взаимодействия могут получать управления потоки с более низким приоритетом.

Сетевые взаимодействия

В случае локального взаимодействия MsgDeliverEvent() всегда возвращает корректное значение успеха или ошибки. При использовании MsgDeliverEvent() для сетевого взаимодействия не гарантируется достоверность кода возврата. Это происходит из-за того, что MsgDeliverEvent() вынуждена блокироваться, ожидая, пока выполниться взаимодействие между двумя модулями lsm-qnet.so.

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

Возвращаемое значение:

MsgDeliverEvent()
Если возникла ошибка функция возвращает -1, код ошибки записывается в errno. Любое другое возвращенное значение считается успешным завершением.
MsgDeliverEvent_r()
EOK возвращается при успешном завершении. Функция НЕ устанавливает errno. При возникновении ошибки функция возвращает один из представленных ниже кодов.

Коды ошибок:

EAGAIN
У ядра недостаточно ресурсов для постановки события в очередь.
EBADF
Поток, на который указывает rcvid, закрыл соединение.
EFAULT
Ошибка при попытке доступа ядра к заданным буферам.
EINVAL
Событие некорректно. Например, несуществующий тип события или недопустимый номер сигнала для события SIGEV_SIGNAL.
ESRCH
Поток-клиент, ассоциированный с rcvid не существует.
ESRVRFAULT
Ошибка в адресном пространстве сервера при попытке записать содержимое структуры импульса в приемный буфер сервера (только для событий 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 системной библиотеки