После открытия потока процесс может произвести встраивание необходимых модулей. Для этого используется системный вызов ioctl(2) . Команда I_PUSH
этой функции служит для встраивания модулей, а команда I_POP
— для извлечения модулей из потока. Приведем типичный сценарий конструирования потока:
fd = open("/dev/stream", O_RDWR);
ioctl(fd, I_PUSH, "module1");
ioctl(fd, I_PUSH, "module2");
...
ioctl(fd, I_POP, (char*)0);
ioctl(fd, I_POP, (char*)0);
close(fd);
В этом примере процесс открыл поток /dev/stream, а затем последовательно встроил модули module1 и module2. Заметим, что команда I_PUSH
системного вызова ioctl(2) встраивает модуль непосредственно после головного модуля. После выполнения операций ввода/вывода, процесс извлек модули и закрыл поток. [64] При закрытии потока все встроенные модули извлекаются автоматически.
Поскольку модули описываются такими же структурами данных, что и драйверы, схемы их встраивания похожи. Как и в случае драйверов, для заполнения полей q_qinfo
структур queue используются данные из структуры streamtab
модуля. Для хранения информации, необходимой для инициализации модуля, во многих версиях UNIX используется таблица fmodsw[]
, каждый элемент которой хранит имя модуля и указатель на структуру streamtab
. После установления всех связей вызывается функция xx open()
модуля.
Управление потоком осуществляется прикладным процессом с помощью команд системного вызова ioctl(2) :
#include
#include
#include
int ioctl(int fildes, int command, ... /* arg */);
Хотя часть команд обрабатывается исключительно головным модулем потока, другие предназначены промежуточным модулям или драйверу. Для этого головной модуль преобразует команды ioctl(2) в сообщения и направляет их вниз по потоку. При этом возникают две потенциальные проблемы: синхронизация процесса с системным вызовом (поскольку передача сообщения и реакция модуля имеют асинхронный характер) и передача данных между процессом и модулем.
Синхронизацию осуществляет головной модуль. Когда процесс выполняет системный вызов ioctl(2) , который может быть обработан самим головным модулем, последний выполняет все операции в контексте процесса, и никаких проблем синхронизации и копирования данных не возникает. Именно так происходит обработка ioctl(2) для обычных драйверов устройств. Если же головной модуль не может обработать команду, он блокирует выполнение процесса и формирует сообщение M_IOCTL
, содержащее команду и ее параметры, и отправляет его вниз по потоку. Если какой- либо модуль вниз по потоку может выполнить указанную команду, в ответ он направляет подтверждение в виде сообщения M_IOCACK
. Если ни один из модулей и сам драйвер не смогли обработать команду, драйвер направляет вверх по потоку сообщение M_IOCNAK
. При получении одного из этих сообщений головной модуль пробуждает процесс и передает ему результаты выполнения команды.
При обработке сообщения промежуточным модулем или драйвером возникает проблема передачи данных. Как правило, команда ioctl(2) содержит ассоциированные с ней параметры, число и размер которых зависят от команды. При обработке команды ioctl(2) обычным драйвером последний имеет возможность копировать параметры из пространства задачи и подобным образом возвращать результаты, поскольку вся обработка команды происходит в контексте процесса.
Эта схема неприменима для подсистемы STREAMS. Обработка сообщений модулем или драйвером выполняется в системном контексте и не имеет отношения к адресному пространству текущего процесса. Поэтому модуль не имеет возможности копировать параметры команды и возвращать результаты обработки, используя адресное пространство задачи.
Для преодоления этой проблемы в подсистеме STREAMS предлагаются два подхода.
Первый из них основан на использовании специальной команды ioctl(2) I_STR
. При этом в качестве параметра передается указатель на структуру strioctl
:
ioctl(fd, I_STR, (struct strioctl*)arg);
struct strioctl {
int ic_cmd;
int ic_timout;
int ic_len;
char* ic_dp;
}
где ic_cmd
— фактическая команда,
ic_timeout
— число секунд, которое головной модуль будет ожидать подтверждения запроса, после он вернет процессу ошибку тайм-аута ETIME
,
ic_len
— размер блока параметров команды,
ic_dp
— указатель на блок параметров.
Читать дальше