Модули являются основными компонентами потока. Каждый модуль состоит из пары очередей — очереди чтения и записи, а также набора функций, осуществляющих обработку данных и их передачу вверх или вниз по потоку. Архитектура модуля представлена на рис. 5.16.
Рис. 5.16. Модуль STREAMS
Каждая очередь представлена структурой данных
queue
. Наиболее важными полями
queue
являются:
q_qinfo
Указатель на структуру
qinit
, описывающую функции
обработки сообщений данной очереди.
q_first
,
q_last
Указатели на связанный список сообщений, ожидающих передачи вверх или вниз по потоку.
q_next
Указатель на очередь следующего модуля вверх или вниз по потоку.
q_ptr
Указатель на внутренние данные модуля (очереди).
Помимо указанных полей, структура
queue
содержит параметры для обеспечения управления потоком данных — верхнюю и нижнюю ватерлинии очереди.
Передача данных вверх или вниз по потоку осуществляется с помощью функций модуля, указатели на которые хранятся в структуре
qinit
. Модуль должен определить четыре процедуры для обработки каждой из очередей:
xxput
,
xxservice
,
xxopen
и
xxclose
, где xx, как и прежде, обозначает уникальный префикс драйвера. Эти функции адресуются указателями
(*qi_putp)
,
(*qi_srvp)
,
(*qi_qopen)
,
(*qi_close)
. Этих четырех функций достаточно для взаимодействия с соседними модулями, обработки и передачи данных. Функция
xxopen
вызывается каждый раз, когда процесс открывает поток или при встраивании модуля. Соответственно функция
xxclose
вызывается при закрытии потока или извлечении модуля. Функция
xxput
осуществляет обработку сообщений, проходящих через модуль. Если
xxput
не может передать сообщение следующему модулю (например, в случае, если очередь следующего модуля переполнена), она помещает сообщение в собственную очередь. Периодически ядро вызывает процедуру
xxservice
каждого модуля для передачи отложенных сообщений.
Модуль должен иметь функцию
xxput
для каждой очереди. Функция
xxservice
может не существовать, в этом случае
xxput
не имеет возможности отложить передачу сообщения и должна передать его немедленно, даже если очередь следующего модуля переполнена. Таким образом модули, не имеющие процедур
xxservice
, не обладают возможностью управления потоком данных. Эти аспекты мы подробнее рассмотрим в следующих разделах.
Оставшиеся поля структуры
qinit
:
module_info
В этой структуре хранятся базовые значения таких параметров, как ватерлинии, размер сообщений и т.д. Некоторые из этих параметров также находятся в структуре queue. Это дает возможность динамически изменять их, сохраняя при этом базовые значения.
module_stat
Эта структура непосредственно не используется подсистемой STREAMS. Однако модуль имеет возможность осуществлять сбор разнообразной статистики своего участка потока с помощью полей этой структуры.
Сообщения
В подсистеме STREAMS все данные передаются в виде сообщений. С помощью сообщений передаются данные от приложений
к драйверу и обратно. Сообщения используются для взаимодействия модулей между собой. Модули могут также генерировать сообщения для уведомления прикладного процесса или друг друга о возникновении ошибок или непредвиденных ситуаций. Таким образом, сообщения являются единственным способом передачи информации между различными компонентами потока и потому занимают ключевое место в подсистеме STREAMS.
Сообщение описывается двумя структурами данных: заголовком сообщения
msgb
(message block) и заголовком блока данных
datab
(data block). Обе эти структуры адресуют буфер данных, где находятся фактические данные сообщения.
Заголовок сообщения
msgb
имеет следующие поля:
b_next
,
b_prev
Используются для формирования связанного списка сообщений и соответственно адресуют следующее и предыдущее сообщение очереди
b_cont
Указывает на продолжение сообщения и используется для связывания различных частей одного сообщения
b_datap
Указатель на заголовок блока данных
b_rptr
,
b_wptr
Указатели, определяющие расположение (начало и конец) данных в буфере данных
b_cont
Содержит ссылку на следующую структуру
msgb
Заголовок блока данных
datab
используется для описания буфера и имеет следующие поля:
db_base
Адрес начала буфера
db_lim
Адрес ячейки памяти, следующей непосредственно за буфером. Таким образом, размер буфера равен
db_lim - db_base
db_type
Тип сообщения
db_ref
Число заголовков сообщения, адресующих этот блок
Использование этих структур данных для формирования очереди сообщений и сообщений, состоящих из нескольких частей, показано на рис. 5.17.
Рис. 5.17. Сообщения STREAMS
Поле
b_cont
заголовка сообщения позволяет объединять несколько блоков данных в одно сообщение. Эта возможность особенно полезна при использовании подсистемы STREAMS для реализации сетевых протоколов. Сетевые протоколы имеют уровневую организацию. По мере передачи данных вниз по потоку, каждый последующий модуль (реализующий протокол определенного уровня) добавляет собственную управляющую информацию. Поскольку протоколы верхнего уровня не имеют представления об архитектуре нижних, невозможно заранее зарезервировать необходимую память под сообщение. Вместо того чтобы изменять размер буфера данных сообщения, модуль может добавлять управляющую информацию в виде отдельных частей, связывая их с помощью указателя
b_cont
. Этот процесс, получивший название инкапсуляции данных, графически представлен на рис. 5.18.
Рис. 5.18. Инкапсуляция данных с использованием составных сообщений
Поле
db_ref
заголовка блока данных позволяет нескольким заголовкам сообщения совместно использовать один и тот же буфер. При этом происходит виртуальное копирование сообщения, каждая копия которого может обрабатываться отдельно. Как правило, такой буфер используется совместно только для чтения, хотя сама подсистема STREAMS не накладывает никаких ограничений, возлагая всю ответственность за обработку данных на модули потока.