На рис. 5.14 показаны различные потоки, созданные из нескольких стандартных компонентов, для поддержки сетевых протоколов семейства TCP/IP. Причем модули IP, TCP и UDP могут поставляться одним производителем, а драйверы Ethernet или Token Ring соответствующими производителями сетевых адаптеров. В результате встраивания необходимых модулей первый поток будет обеспечивать передачу трафика TCP через адаптер Ethernet, в то время как второй — передачу трафика UDP через адаптер Token Ring.
Рис. 5.14. Использование одних и тех же модулей для создания различных потоков
Подсистема STREAMS также обеспечивает возможность мультиплексирования потоков . Мультиплексирующий драйвер может быть подключен к нескольким модулям как вверх, так и вниз по потоку. Различают три типа мультиплексоров — верхний , обеспечивающий мультиплексирование вверх по потоку, нижний , обеспечивающий мультиплексирование вниз по потоку, и гибридный , поддерживающий несколько потоков выше и ниже мультиплексора.
С помощью мультиплексирующих драйверов потоки, представленные на рис. 5.14, могут быть объединены в единый драйвер протоколов , поддерживающий несколько каналов передачи данных. Именно таким образом реализована поддержка сети во многих версиях операционной системы UNIX. Возможная организация компонентов STREAMS приведена на рис. 5.15.
Рис. 5.15. Конфигурация сетевого доступа с использованием подсистемы STREAMS
В этом случае модули TCP и UDP являются верхними мультиплексорами, а модуль IP реализован в виде гибридного мультиплексора [58] На самом деле мультиплексором может являться только драйвер STREAMS. Объединение драйверов в единый объект отлично от встраивания модулей и носит название связывания . Более подробно связывание и различия между модулями и драйверами STREAMS мы рассмотрим несколько позже в этой главе.
. Такая организация позволяет приложениям создавать потоки, используя различные комбинации сетевых протоколов и драйверов сетевых устройств. Задача мультиплексирующего драйвера помимо обработки данных заключается в хранении состояния всех потоков и правильной маршрутизации данных между ними, т. е. передаче данных в очередь требуемого модуля.
Модули являются основными компонентами потока. Каждый модуль состоит из пары очередей — очереди чтения и записи, а также набора функций, осуществляющих обработку данных и их передачу вверх или вниз по потоку. Архитектура модуля представлена на рис. 5.16.
Рис. 5.16. Модуль STREAMS
Каждая очередь представлена структурой данных queue
. Наиболее важными полями queue
являются:
q_qinfo |
Указатель на структуру qinit , описывающую функции обработки сообщений данной очереди. |
q_first , q_last |
Указатели на связанный список сообщений, ожидающих передачи вверх или вниз по потоку. |
q_next |
Указатель на очередь следующего модуля вверх или вниз по потоку. |
q_ptr |
Указатель на внутренние данные модуля (очереди). |
Помимо указанных полей, структура queue
содержит параметры для обеспечения управления потоком данных — верхнюю и нижнюю ватерлинии очереди.
Передача данных вверх или вниз по потоку осуществляется с помощью функций модуля, указатели на которые хранятся в структуре qinit
. Модуль должен определить четыре процедуры для обработки каждой из очередей: xx put()
, xx service()
, xx open()
и xx close()
, где xx , как и прежде, обозначает уникальный префикс драйвера. Эти функции адресуются указателями (*qi_putp)()
, (*qi_srvp)()
, (*qi_qopen)()
, (*qi_close)()
. Этих четырех функций достаточно для взаимодействия с соседними модулями, обработки и передачи данных. Функция xx open()
вызывается каждый раз, когда процесс открывает поток или при встраивании модуля. Соответственно функция xx close()
вызывается при закрытии потока или извлечении модуля. Функция xx put()
осуществляет обработку сообщений, проходящих через модуль. Если xx put()
не может передать сообщение следующему модулю (например, в случае, если очередь следующего модуля переполнена), она помещает сообщение в собственную очередь. Периодически ядро вызывает процедуру xx service()
каждого модуля для передачи отложенных сообщений.
Читать дальше