Книга: Операционная система UNIX

Взаимодействие с прикладными процессами

Взаимодействие с прикладными процессами

Рассмотренный ранее программный интерфейс TLI полностью реализует функциональность TPI. Легко заметить соответствие между отдельными функциями TLI и примитивами TPI, приведенными в табл. 6.10. Схема вызова функций TPI и обмена соответствующими примитивами TPI между клиентом и сервером для типичного TCP-сеанса приведена на рис. 6.32.


Рис. 6.32. Функции TLI и примитивы TPI

Программный интерфейс потоков был рассмотрен в главе 5 при обсуждении подсистемы STREAMS. Основными функциями, обеспечивающими передачу и получение сообщений, являются системные вызовы putmsg(2) и getmsg(2). Таким образом, большинство функций TLI, составляющих программный интерфейс доступа прикладных процессов к транспортным протоколам, являются удобной оболочкой (реализованной в виде библиотеки, например, libnsl.so) более фундаментальным системным вызовам putmsg(2) и getmsg(2).

В качестве примера рассмотрим функцию t_connect(3N). Ее реализация может иметь следующий вид:

int t_connect(int fd, struct t_call *sndcall,
struct t_call *recvcall) {
 struct T_conn_req *connreq;
 struct T_conn_con* conncon;
 struct T_ok_ack *okack;
 struct T_error_ack *errack;
 struct strbuf connect, ack, confirm, m_data;
 struct netbuf addr, opt, udata;
 char *buf;
 int flags;
 ...
 /* Сохраним адреса буферов netbuf запроса sndcall */
 addr = sndcall->addr; opt = sndcall->opt;
 udata = sndcall->udata;
 /* Заполним поля структуры strbuf для формирования
    управляющей части (блок M_PROTO) сообщения T_CONN_REQ */
 connect.len =
  sizeof(struct T_conn_req) + addr.len + opt.len;
 connect.maxlen =
  sizeof(struct Т_conn_req) + addr.maxlen + opt.maxlen;
 buf = (char*)malloc(connect.maxlen);
 connect.buf = buf;
 /* Заполним поля заголовка блока M_PROTO сообщения T_CONN_REQ в
    соответствии с форматом структуры T_conn_req */
 connreq = (struct T_conn_req*)buf;
 connreq->PRIM_type = T_CONN_REQ;
 connreq->DEST_length = addr.len;
 connreq->DEST_offset = sizeof (struct T_conn_req);
 buf += sizeof(struct T_conn_req);
 memcpy(buf, addr.buf, addr.len);
 connreq->OPT_length = opt.len;
 connreq->OPT_offset = connreq->DEST_offset + opt.len;
 buf += addr.len;
 memcpy(buf, opt.buf, opt.len);
 /* Заполним поля структуры strbuf для формирования блока данных
    (блок M_DATA) */
 m_data.len = udata.len;
 m_data.maxlen = udata.maxlen;
 m_data.buf = udata.buf;
 /* Отправим запрос Т_CONN_REQ поставщику транспортных услуг
    по потоку fd */
 putmsg(fd, &connect, &m_data, 0);
 /* Подготовимся к приему подтверждения. Выделим максимальный
    размер для получения негативного подтверждения, поскольку
    примитив T_ERROR_ACK занимает больше места */
 ack.len = ack.maxlen = sizeof(struct T_error_ack);
 ack.buf = udata.buf;
 /* Подтверждение является приоритетным, поэтому установим флаг
    RS_HIPRI. До получения подтверждения не предпринимаем
    никаких действий */
 flags = RS_HIPRI;
 getmsg(fd, &ack, (struct strbuf*)0, &flags);
 free(connect.buf);
 okack = (struct T_ok_ack*)ack.buf;
 /* Проверим получено ли положительное или
    негативное подтверждение */
 if (okack->PRIM_type == T_OK_ACK) {
  /* Если подтверждение положительное, подготовимся к получению
     согласия удаленного пользователя на установление связи
     (примитив T_CONN_CON) */
  free(ack.buf);
  if (recvcall != NULL) {
   addr = recvcall->addr;
   opt = recvcall->opt;
   udata = recvcall->udata;
   confirm.len = sizeof(struct T_conn_con) + addr.len + opt.len;
   confirm.maxlen =
    sizeof(struct T_conn_con) + addr.maxlen + opt.maxlen;
   buf = (char*)malloc(confirm.maxlen);
   confirm.buf = buf;
   m_data.len = udata.len;
   m_data.maxlen = udata.maxlen;
   m_data.buf = udata.buf;
   /* Получим примитив T_CONN_CON */
   getmsg(fd, &confirm, &m_data, &flags);
   free(buf);
   conncon = (struct T_conn_con*)confirm.buf;
   if (conncon->PRIM_type == T_CONN_CON) {
    /* Если это действительно согласие, заполним
       структуру rcvcall для пользователя TLI */
    addr.len = conncon->OPT_length;
    opt.len = conncon->OPT_length;
    memcpy(addr.buf, conncon+conncon->RES_offset, addr.len);
    memcpy(opt.buf, conncon+conncon->OPT_offset, opt.len);
    free(confirm.buf);
    /* Все закончилось удачно — возвращаем 0 */
    return 0;
   }
  } else {
   /* В случае отказа мы готовы обработать примитив
      T_DISCON_IND */
   ...
   return -1;
  }
 } else {
  /* Если получен примитив T_ERROR_ACK — обработаем его */
  errack = (struct T_error_ack*)ack.buf;
  ...
  return -1;
 }
}

Подобным образом реализовано большинство функций TLI. Заметим, что в конкретном случае использования транспортного протокола TCP прием и передача данных осуществляются в виде потока, не содержащего каких-либо логических записей. В этом случае не требуется формирование примитивов типа T_DATA_REQ и T_DATA_IND. В то же время, для передачи и получения экстренных данных будут использованы примитивы T_EXDATA_REQ и T_EXDATA_IND. При использовании протокола UDP все данные будут передаваться с помощью примитивов T_UNITDATA_REQ и T_UNITDATA_IND.

Описанная реализация программного интерфейса TLI имеет один существенный недостаток — операции функций не являются атомарными. Другими словами, выполнение функции t_connect(3N) может быть прервано другими процессами, которые могут также связываться с удаленным узлом. Это возможно, поскольку выполнение значительной части операций происходит в режиме задачи. Если для функции t_connect(3N) нарушение атомарности допустимо, то ряд функций, таких, например, как связывание (t_bind(3N)), получение информации (t_open(3N), t_getinfo(3N)) и установка или получение опций протокола (t_optmgmt(3N)) должны быть защищены от возможного нарушения целостности данных по причине прерывания операции. Единственным способом гарантировать атомарность является перевод выполнения критических участков (например, между отправлением примитива и получением подтверждения от поставщика транспортных услуг) в режим ядра. Для этого подсистема STREAMS предлагает механизм обмена управляющими командами с помощью вызова ioctl(2).

Однако с помощью ioctl(2), как было показано в разделе "Подсистема STREAMS" главы 5, можно формировать лишь сообщения типа M_IOCTL. Для преобразования этих сообщений в примитивы TPI служит дополнительный модуль timod(7M), встраиваемый в поток между головным и транспортным модулями. На рис. 6.33 показано местоположение модуля timod(7M) и схематически отображены его функции.


Рис. 6.33. Архитектура доступа к транспортным услугам

Для всех сообщений STREAMS, за исключением сообщений M_IOCTL, которые генерируются головным модулем в ответ на системный вызов ioctl(fd, I_STR, ...), модуль timod(7M) является прозрачным, т.е. он просто передает эти сообщения следующему модулю вниз по потоку без какой-либо обработки. Несколько сообщений M_IOCTL обрабатываются модулем и преобразуются в соответствующие примитивы TPI.

При этом вызов ioctl(2) имеет следующий формат:

#include <sys/stropts.h>
struct strioctl my_strioctl
...
strioctl.ic_cmd = cmd;
strioctl.ic_timeout = INFTIM;
strioctl.ic_len = size;
strioctl.ic_dp = (char*)buf;
ioctl(fd, I_STR, &my_strioctl);

При вызове ioctl(2) поле size устанавливается равным размеру соответствующего примитива TPI, определенного полем cmd и расположенного в буфере buf. При возврате из функции поле size содержит размер примитива, возвращенного поставщиком транспортных услуг и расположенного в буфере buf.

Модуль timod(7M) служит для обработки следующих команд cmd:

Значение cmd Обработка модулем timod(7M)
TI_BIND Команда преобразуется в примитив T_BIND_REQ. При успешном завершении функции ioctl(2) в буфере buf находится примитив T_BIND_ACK.
TI_UNBIND Команда преобразуется в примитив T_UNBIND_REQ. При успешном завершении функции ioctl(2) в буфере buf находится примитив T_OK_ACK.
TI_GETINFO Команда преобразуется в примитив T_INFO_REQ. При успешном завершении функции ioctl(2) в буфере buf находится примитив T_INFO_ACK.
TI_OPTMGMT Команда преобразуется в примитив T_OPTMT_REQ. При успешном завершении функции ioctl(2) в буфере buf находится примитив T_OPTMGMT_ACK.

Оглавление книги


Генерация: 1.240. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз