Читать книгу 📗 "Операционная система UNIX - Робачевский Андрей Михайлович"
Доступ к драйверам STREAMS осуществляется с помощью коммутатора символьных устройств — таблицы
cdevsw[]d_strNULLstreamtabstreamtabcdevsw[]Создание потока
Поток создается при первом открытии с помощью системного вызова специального файла устройства, ассоциированного с драйвером STREAMS. Как правило, процесс создает поток в два этапа: сначала создается элементарный поток, состоящий из нужного драйвера и головного модуля (являющегося обязательным приложением), а затем производится встраивание дополнительных модулей для получения требуемой функциональности.
Процесс открывает поток с помощью системного вызова open(2), передавая ему в качестве аргумента имя специального файла устройства. При этом ядро производит трансляцию имени и обнаруживает, что адресуемый файл принадлежит файловой системе specfs, через которую в дальнейшем производятся все операции работы с файлом. В памяти размещается vnode этого файла и вызывается функция открытия файла для файловой системы specfs —
spec_open()spec_open()cdevsw[]d_strstropen()
Рис. 5.22. Структура потока после открытия
Головной модуль представлен структурой
stdataq_ptrqueuestdataq_qinfoqueueqinitОчереди чтения и записи драйвера связываются с соответствующими очередями головного модуля. Информация, хранящаяся в структуре
streamtabq_qinfoВ завершение вызывается функция
<i>xx</i>open()stropen()<i>xx</i>open()После открытия потока процесс может произвести встраивание необходимых модулей. Для этого используется системный вызов ioctl(2). Команда
I_PUSHI_POPfd = 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Поскольку модули описываются такими же структурами данных, что и драйверы, схемы их встраивания похожи. Как и в случае драйверов, для заполнения полей
q_qinfostreamtabfmodsw[]streamtab<i>xx</i>open()Управление потоком
Управление потоком осуществляется прикладным процессом с помощью команд системного вызова ioctl(2):
#include <sys/types.h>#include <stropts.h>#include <sys/conf.h>int ioctl(int fildes, int command, ... /* arg */);Хотя часть команд обрабатывается исключительно головным модулем потока, другие предназначены промежуточным модулям или драйверу. Для этого головной модуль преобразует команды ioctl(2) в сообщения и направляет их вниз по потоку. При этом возникают две потенциальные проблемы: синхронизация процесса с системным вызовом (поскольку передача сообщения и реакция модуля имеют асинхронный характер) и передача данных между процессом и модулем.
Синхронизацию осуществляет головной модуль. Когда процесс выполняет системный вызов ioctl(2), который может быть обработан самим головным модулем, последний выполняет все операции в контексте процесса, и никаких проблем синхронизации и копирования данных не возникает. Именно так происходит обработка ioctl(2) для обычных драйверов устройств. Если же головной модуль не может обработать команду, он блокирует выполнение процесса и формирует сообщение
M_IOCTLM_IOCACKM_IOCNAK