Системный вызов putmsg(2) имеет вид:
#include
int putmsg(int fildes, const struct strbuf *ctlptr,
const struct strbuf* dataptr, int flags);
С помощью этого вызова головной модуль формирует сообщение, состоящее из управляющей части M_PROTO
и данных, передаваемых в блоках M_DATA
. Содержимое сообщения передается с помощью указателей на структуру strbuf
— ctlptr
для управляющего блока и dataptr
для блоков данных.
Структура strbuf
имеет следующий формат:
struct strbuf {
int maxlen;
int len;
void *buf;
}
где maxlen
не используется, len
— размер передаваемых данных, buf
— указатель на буфер.
С помощью аргумента flags
процесс может передавать экстренные сообщения, установив флаг RS_HIPRI
.
В обоих случаях головной модуль формирует сообщение и с помощью функции canput(9F) проверяет, способен ли следующий вниз по потоку модуль, обеспечивающий механизм управления передачей, принять его. Если canput(9F) возвращает истинный ответ, сообщение передается вниз по потоку с помощью функции putnext(9F) , а управление возвращается процессу. Если canput(9F) возвращает ложный ответ, выполнение процесса блокируется, и он переходит в состояние сна, пока не рассосется образовавшийся затор. Заметим, что возврат системного вызова еще не гарантирует, что данные получены устройством. Возврат из write(2) или putmsg(2) свидетельствует лишь о том, что данные были успешно скопированы в адресное пространство ядра, и в виде сообщения направлены вниз по потоку.
Процесс может получить данные из потока с помощью системных вызовов read(2) и getmsg(2) . Стандартный вызов read(2) позволяет получать только обычные данные без сохранения границ сообщений. [63] С помощью сообщения M_SETOPTS можно дать указания головному модулю обрабатывать сообщения M_PROTO как обычные данные. В этом случае вызов read(2) будет возвращать содержимое как сообщений M_DATA , так и M_PROTO . Однако информация о типе сообщения (данных) и границы сообщений сохранены не будут.
В отличие от этого вызова getmsg(2) позволяет получать данные сообщений типов M_DATA
и M_PROTO
, при этом сохраняются границы сообщений. Например, если полученное сообщение состоит из блока M_PROTO
и нескольких блоков M_DATA
, вызов getmsg(2) корректно разделит сообщение на две части: управляющую информацию и собственно данные.
Вызов getmsg(2) имеет вид:
#include
int getmsg(int fildes, struct strbuf *ctlptr,
struct strbuf *dataptr, int *flagsp);
С помощью вызова getmsg(2) прикладной процесс может получить сообщение, причем его управляющие и прикладные данные будут помещены в буферы, адресуемые ctlptr
и dataptr
соответственно. Так же как и в случае putmsg(2) эти указатели адресуют структуру strbuf
, которая отличается только тем, что поле maxlen
определяет максимальный размер буфера, a len
устанавливается равным фактическому числу полученных байтов. По умолчанию getmsg(2) получает первое полученное сообщение, однако с помощью флага RS_HIPRI
, установленного в переменной, адресуемой аргументом flagsp
, процесс может потребовать получение только экстренных сообщений.
В обоих случаях, если данные находятся в головном модуле, ядро извлекает их из сообщения, копирует в адресное пространство процесса и возвращает управление последнему. Если же в головном модуле отсутствуют сообщения, ожидающие получения, выполнение процесса блокируется, и он переходит в состояние сна до прихода сообщения.
Когда головной модуль получает сообщение, ядро проверяет, ожидает ли его какой-либо процесс. Если такой процесс имеется, ядро пробуждает процесс, копирует данные в пространство задачи и производит возврат из системного вызова. Если ни один из процессов не ожидает получения сообщения, оно буферизуется в очереди чтения головного модуля.
Как и для обычных драйверов устройств, рассмотренных ранее, прежде чем процесс сможет получить доступ к драйверу STREAMS, необходимо встроить драйвер в ядро системы и создать специальный файл устройства — файловый интерфейс доступа. Независимо от того, как именно осуществляется встраивание (статически с перекомпиляцией ядра, или динамически), для этого используются три структуры данных, определенных для любого драйвера или модуля STREAMS: module_info
, qinit
и streamtab
. Связь между ними представлена на рис. 5.21.
Рис. 5.21. Конфигурационные данные драйвера (модуля) STREAMS
Читать дальше