Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform
Шрифт:
Например, в системе сбора данных клиент может выделить 4-мегабайтный буфер и приказать драйверу собрать 4 мегабайта данных. Драйверу вовсе не обязательно держать под боком здоровенный буфер просто так, на случай если кто-то вдруг неожиданно запросит передачу большого массива данных.
Драйвер может иметь буфер размером 128Кб для обмена с аппаратурой посредством DMA, а сообщение пересылать в адресное пространство клиента по частям, используя функцию MsgWrite (разумеется, каждый раз увеличивая смещение на 128Кб). Когда будет передан последний фрагмент, можно будет вызывать MsgReply.
Передача
Отметим, что функция MsgWrite позволяет вам записать различные компоненты данных в различные места, а затем либо просто разбудить клиента вызовом MsgReply:
либо сделать это после записи заголовка в начало клиентского буфера:
Это довольно изящный трюк для записи неизвестного количества данных, когда вы узнаете, сколько данных нужно было записать, только когда запись уже закончена. Главное — если вы будете использовать второй метод, с записью заголовка после записи данных, не забудьте зарезервировать место под заголовок в начале клиентского буфера!
Составные сообщения
До сих пор мы демонстрировали только обмен сообщениями, когда данные передаются из одного буфера в адресном пространстве клиента в другой буфер в адресном пространстве сервера (и наоборот — в случае ответа на сообщение).
При том, что данный подход вполне приемлем для большинства приложений, его применение далеко не всегда эффективно. Вспомните: наша функция write из Си-библиотеки берет переданный ей буфер и добавляет в его начало небольшой заголовок. Используя то, что мы уже изучили ранее, вы могли бы ожидать, что реализация write в Си-библиотеке может выглядеть примерно так (это не реальный код!):
Понимаете, что произошло? Несколько неприятных вещей:
• Функция write
• Мы были должны скопировать данные дважды: в первый раз — при использовании функции memcpy, и затем еще раз, снова — уже при осуществлении передачи сообщения.
• Мы должны были предусмотреть указатель на тип
Поскольку ядро намерено копировать данные в любом случае, было бы хорошо, если бы мы смогли сообщить ему о том, что одна часть данных (заголовок) фиксирована по некоторому адресу, а другая часть (собственно данные) фиксирована где- нибудь еще, без необходимости самим вручную собирать буферы из частей и копировать данные.
На наше счастье, в QNX/Neutrino реализован механизм, который позволяет нам сделать именно так! Механизм этот называется IOV (i/o vector), или «вектор ввода/вывода».
Давайте для начала рассмотрим некоторую программу, а затем обсудим, что происходит с применением такого вектора.
Прежде всего, обратите внимание на то, что не применяется никакой функции malloc и никакой функции memcpy. Затем обратим внимание на тип применяемого вектора IOV —
Определение типа вектора
Мы заполняем в этой структуре пары «адрес — длина» для заголовка операции записи (первая часть) и для данных клиента (вторая часть). Существует удобная макрокоманда, SETIOV, которая выполняет за нас необходимые присвоения. Она формально определена следующим образом: