KnigaRead.com/

Олег Цилюрик - QNX/UNIX: Анатомия параллелизма

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Олег Цилюрик, "QNX/UNIX: Анатомия параллелизма" бесплатно, без регистрации.
Перейти на страницу:

Существует несколько функций API, относящихся к службе глобальных имен: name_attach(), name_open() и name_close(). Программисты, знакомые с QNX 4, сразу «узнают» в них аналоги известных им функций qnx_name_attach(), qnx_name_open() и qnx_name_close(). Приложения используют эти функции для объявления имени службы, связи со службой и отсоединения от службы.

Итак, чтобы объявить свое имя глобально в сети, приложение-сервер должно на узле, где в режиме сервера функционирует менеджер службы глобальных имен, объявить свою службу, выполнив вызов:

if (!(NameServer = name_attach(NULL, "MyService", NAME_FLAG_ATTACH_GLOBAL)))

 return EXIT_FAILURE;

Флаг NAME_FLAG_ATTACH_GLOBAL указывает, что приложение-сервер объявляет свое имя глобально — в сети. Приложение, которое может подсоединить службу глобально, должно иметь право доступа root. После выполнения этого вызова в директории /dev/name/global появится подсоединенное имя MyService (если бы третий аргумент вызова был установлен в ноль, это имя оказалось бы подсоединенным к /dev/name/local и было бы доступно только локально).

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

• _NTO_CHF_UNBLOCK — доставлять владельцу канала импульс с кодом _PULSE_CODE_UNBLOCK и значением rcvid каждый раз, когда Reply-блокированный клиент попытается разблокироваться (скажем, по получению сигнала или по таймеру);

• _NTO_CHF_DISCONNECT — доставлять владельцу канала импульс с кодом _PULSE_CODE_DISCONNECT, когда от процесса отсоединились все установленные соединения клиента (клиент выполнил name_close() на каждый свой name_open() к имени сервера либо вообще умер);

• _NTO_CHF_COID_DISCONNECT — доставлять владельцу канала импульс с кодом _PULSE_CODE_COIDDEATH и значением coid (идентификатора соединения) для каждого соединения по этому каналу, когда канал закрывается.

Теперь, после создания канала, сервер может становиться на прием сообщений от клиентов:

rcvid = MsgReceive(NameServer->chid, &MsgBuf, sizeof MsgBuf);

Однако может так случиться, что клиент пошлет не непосредственное сообщение для сервера, а выполнит, скажем, чтение, что, по сути, тоже является отосланным сообщением. Поэтому при получении сообщений необходимо производить их «фильтрацию»:

if (MsgBuf.hdr_type >= _IO_BASE && Buffer.hdr.type <= _IO_MAX) {

 MsgError(rcvid, ENOSYS);

 continue;

}

Получив от клиента некое предопределенное сообщение, сервер сбрасывает флаг flagWork и выходит из петли ожидания сообщений, тем самым завершая свою работу.

С учетом этих деталей и организован нижеописанный сервер.

Код процесса-сервера, использующего службу глобальных имен

#include <stdio.h>

#include <errno.h>

#include <stdlib.h>

#include <sys/dispatch h>


/* На сервер могут приходить и импульсы. Как минимум. */

typedef struct _pulse msg_header_t;


/* Структура сообщения состоит из заголовка и буфера наших данных */

typedef struct _MsgBuf {

 msg_header_t hdr;

 char* Buffer;

} MsgBuf_t;


int main() {

 name_attach_t* NameServer;

 MsgBuf_t MsgBuf;

 int rcvid;

 char BufReply[100];

 int flagWork = 1;

 /* Создаем глобальное имя /dev/name/global/MyService */

 if (!(NameServer = name_attach(NULL, "MyService",

  NAME_FLAG_ATTACH_GLOBAL)))

  return EXIT_FAILURE;

 /* Становимся на петлю получения сообщений */

 while (flgWork) {

  if ((rcvid = MsgReceive(NameServer->chid, &MsgBuf,

   sizeof MsgBuf, NULL)) == -1) {

   printf("Ошибка при получении сервером MyService "

    "сообщения от клиентаn");

   fflush(stdout);

   break;

  }

  if (!rcvid) {

   // Получен импульс

   switch(MsgBuf.hdr.code) {

   case _PULSE_CODE_DISCONNECT:

    /* Поскольку для канала установлен флаг _NTO_CHF_DISCONNECT, ядро

       автоматически не освобождает связи, установленные клиентом ранее.

       Сервер должен выполнить это со своей стороны сам, "сознательно"

       удалив маршрут от себя обратно к клиенту */

    ConnectDetach(MsgBuf.hdr.scoid);

    break;

   case _PULSE_CODE_UNBLOCK;

    /* Клиент пытается разблокироваться, не дождавшись ответа по Reply. Надо

       выполнить какие-то действия, чтобы корректно (для себя)

       обработать эту ситуацию, и все-таки отпустить этого клиента - ему

       ведь надо! При этом импульсе в MsgBuf.hdr.value приходит rcvid */

    MsgReply(MsgBuf.hdr.value.sival_int, EAGAIN, NULL, 0);

    break;

   default:

    break;

   }

   continue;

   // вновь уходим на петлю приема сообщений

  }

  /* Полученное сообщение находится в диапазоне системных сообщений

     ввода/вывода. Не обрабатываем. */

  if (MsgBuf.hdr.type >= _IO_BASE && MsgBuf.hdr.type <= _IO_MAX) {

   MsgError(rcvid, ENOSYS);

   continue;

  }

  /* А вот это - сообщение для сервера. Обрабатываем. */

  if (MsgBuf.hdr.type <= 0x50001 || MsgBuf.hdr.type >= 0x500ff) {

   printf("Сервер получил сообщение неизвестно от"

    " кого с меткой %#xn", MsgBuf.hdr.type);

   strcpy(BufReply, "а кто это???");

  } else {

   printf("Сервер получил сообщение. "%s"n",

    MsgBuf.Buffer);

   strcpy(BufReply, "а, это ты, клиент");

  }

  MsgReply(rcvid, EOK, BufReply, strlen(BufReply) + 1);

 }

 // Конец петли получения сообщений

 /* Отсоединяемся от службы глобальных имен */

 name_detach(NameServer, 0);

 return EXIT_SUCCESS;

}

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

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

• Если локальный поставщик отсутствует или по каким-то причинам отказывает в поставке службы, менеджер пытается связать приложение с другими поставщиками. Если имеется несколько удаленных поставщиков, то порядок, в котором производятся попытки установить с ними связь (т.е. кто получит связь первым), не определен.

Код процесса-клиента, использующего службу глобальных имен

#include <stdio.h>

#include <errno.h>

#include <stdlib.h>

#include <sys/dispatch.h>


/* На сервер могут приходить и импульсы. Как минимум. */

typedef struct _pulse msg_header_t;


/* Структура сообщения состоит из заголовка и буфера наших данных */

typedef struct _MsgBuf {

 msg_header_t hdr;

 char* Buffer;

} MsgBuf_t;


int main() {

 MsgBuf_t MsgBuf;

 int fd;

 char BufReply[100];

 if ((fd = name_open("MyService", NAME_FLAG_ATTACH_GLOBAL)) == -1) {

  printf("Клиенту не удалось присоединиться к"

   " сервисуn");

  fflush(stdout);

  return EXIT_FAILURE;

 }

 // Инвентаризационная метка данного клиента

 MsgBuf.hdr.type = 0x50001;

 MsgBuf.hdr.subtype = 0x00;

 strcpy (MsgBuf.Buffer, "Здравствуй, дорогой сервер!");

 if (MsgSend(fd, &MsgBuf, sizeof MsgBuf, BufReply, sizeof BufReply) == -1) {

  printf("Клиент имеет проблемы с передачей сообщений"

   " серверуn");

  fflush(stdout);

  name_close(fd);

  return EXIT_FAILURE;

 }

 printf("Клиент получил от сервера такой ответ: "

  ""%s" n", BufReply);

 name_close(fd);

 return EXIT_SUCCESS;

}

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

Раньше, когда приложение-клиент использовало вызов функции name_open() для связи с сервером, сервер об этом не знал. Теперь это изменено: серверу фактически отсылается сообщение _IO_CONNECT/_IO_OPEN. Кроме того, изменено приложение-сервер, чтобы иметь возможность обрабатывать приход сообщения _IO_CONNECT.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*