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 . |