#include
int MsgWrite(int rcvid, const void *msg, int nbytes,
int offset);
Применение функции
Например, в системе сбора данных клиент может выделить 4-мегабайтный буфер и приказать драйверу собрать 4 мегабайта данных. Драйверу вовсе не обязательно держать под боком здоровенный буфер просто так, на случай если кто-то вдруг неожиданно запросит передачу большого массива данных.
Драйвер может иметь буфер размером 128Кб для обмена с аппаратурой посредством DMA, а сообщение пересылать в адресное пространство клиента по частям, используя функцию
Передача нескольких фрагментов сообщения с помощью функции
Отметим, что функция
MsgReply(rcvid, EOK, NULL, 0);
либо сделать это после записи заголовка в начало клиентского буфера:
MsgReply(rcvid, EOK, &header, sizeof(header));
Это довольно изящный трюк для записи неизвестного количества данных, когда вы узнаете, сколько данных нужно было записать, только когда запись уже закончена. Главное — если вы будете использовать второй метод, с записью заголовка после записи данных, не забудьте зарезервировать место под заголовок в начале клиентского буфера!
Составные сообщения
До сих пор мы демонстрировали только обмен сообщениями, когда данные передаются из одного буфера в адресном пространстве клиента в другой буфер в адресном пространстве сервера (и наоборот — в случае ответа на сообщение).
При том, что данный подход вполне приемлем для большинства приложений, его применение далеко не всегда эффективно. Вспомните: наша функция
ssize_t write(int fd, const void *buf, size_t nbytes) {
char *newbuf;
io_write_t *wptr;
int nwritten;
newbuf = malloc(nbytes + sizeof(io_write_t));
// Заполнить write_header
wptr = (io_write_t*)newbuf;
wptr->type = _IO_WRITE;
wptr->nbytes = nbytes;
// Сохранить данные от клиента
memcpy(newbuf + sizeof(io_write_t), buf, nbytes);
// Отправить сообщение серверу
nwritten =
MsgSend(fd, newbuf, nbytes + sizeof(io_write_t),
newbuf, sizeof(io_write_t));
free(newbuf);
return(nwritten);
}
Понимаете, что произошло? Несколько неприятных вещей:
• Функция
• Мы были должны скопировать данные дважды: в первый раз — при использовании функции
• Мы должны были предусмотреть указатель на тип io_write_t
и установить его на начало буфера, вместо использования обычных механизмов доступа (впрочем, это незначительный недостаток).
Поскольку ядро намерено копировать данные в любом случае, было бы хорошо, если бы мы смогли сообщить ему о том, что одна часть данных (заголовок) фиксирована по некоторому адресу, а другая часть (собственно данные) фиксирована где- нибудь еще, без необходимости самим вручную собирать буферы из частей и копировать данные.
На наше счастье, в QNX/Neutrino реализован механизм, который позволяет нам сделать именно так! Механизм этот называется IOV (i/o vector), или «вектор ввода/вывода».
Давайте для начала рассмотрим некоторую программу, а затем обсудим, что происходит с применением такого вектора.
#include
ssize_t write(int fd, const void *buf, size_t nbytes) {
io_write_t whdr;
iov_t iov[2];