Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform
Шрифт:
Наконец, для возврата данных клиенту мы должны вспомнить, что клиент ожидает не только возвращаемые данные (если таковые имеются), но и заголовочную структуру, за которой идут эти данные. Поэтому на этом этапе мы обнуляем заголовочную структуру и устанавливаем число байт (поле nbytes) в число байт, которые мы намерены возвратить (вспомните, мы обнуляли это значение ранее). Затем мы создаем одноэлементный вектор ввода/ вывода с указателем на заголовок и расширяем размер заголовка на число возвращаемых байт. В конце мы просто сообщаем библиотеке администратора ресурсов, что мы возвращаем клиенту одноэлементный вектор ввода/вывода.
Вспомните рассуждения про следующую за заголовком область данных из примера io_write, приведенного выше. Мы утверждали, что байты, расположенные сразу после заголовка, могут как быть полноценными, так и нет (то есть возможны случаи, когда область данных со стороны клиента была считана лишь частично) — в зависимости от того, сколько данных считала библиотека администратора ресурсов. Затем мы говорили о том, что было бы неэффективно пытаться «сэкономить» лишнюю операцию обмена сообщениями и «повторно использовать» область данных. Однако, в случае с devctl все обстоит несколько иначе, особенно если количество передаваемых данных достаточно невелико (как было и в наших примерах). Здесь у нас есть неплохой шанс того, что данные от клиента были-таки считаны в область данных целиком, и тогда повторное их считывание будет напрасной тратой сил. Узнать, сколько у вас доступно пространства, очень просто: поле size («размер») структуры ctp содержит число байт, доступных для вас, начиная с параметра msg. Размер доступной области данных, расположенной за буфером сообщений, вычисляется как разность между размером буфера сообщений и полем size структуры ctp:
Отметим, что этот размер будет действителен также и в случае возврата данных клиенту (как при команде DCMD_AUDIO_GET_SAMPLE_RATE).
Для всего, что превосходит по размеру выделенную область, вам придется получать данные от клиента так же, как мы это делали в примере с io_write (см. выше), а также выделить буфер для возврата данных клиенту.
Дополнительно
Теперь, после того как мы овладели «основами» построения администраторов ресурсов, пришло время рассмотреть более сложные вопросы. К ним относятся:
• расширение OCB;
• расширение атрибутной записи;
• блокирование в пределах администратора ресурсов;
• возврат элементов каталога.
Расширение OCB
В ряде случаев у вас может возникнуть необходимость расширения OCB. Процедура эта является относительно безболезненной. Обычно OCB расширяют дополнительными флагами, характеризующими каждый конкретный open. Один такой флаг можно было бы использовать с обработчиком io_unblock для кэширования значения флага ядра _NTO_MI_UNBLOCK_REQ (подробнее см. параграф «Применение флага _NTO_MI_UNBLOCK_REQ» в главе «Обмен сообщениями»).
Для расширения блока OCB вам нужно будет обеспечить две дополнительных функции: одну для выделения OCB, и одну — для его освобождения. Затем вы должны будете привязать эти две функции к записи точки монтирования. (Да-да, совершенно верно — вам понадобится запись точки монтирования, даже если только для этого.) И наконец, вы должны будете определить ваш собственный тип OCB, чтобы все прототипы в программе были корректны.
Давайте рассмотрим сначала описание типа OCB, а затем уже поглядим, как переопределяются функции:
Это сообщает включаемому файлу
Вот наш расширенный OCB:
А вот код, иллюстрирующий, как переопределяются функции выделения и освобождения OCB в записи точки монтирования:
После этого остается только привязать запись точки монтирования к атрибутной записи:
Функции my_ocb_calloc и my_ocb_free отвечают за выделение обнуленного расширенного OCB и освобождения OCB, соответственно. Вот их прототипы:
Это означает, что функции my_ocb_calloc передаются одновременно и внутренний контекст администратора ресурсов, и атрибутная запись. Функция отвечает за возврат обнуленного OCB. Функция my_ocb_free получает OCB и отвечает за освобождение выделенной под него памяти.
Для этих двух функций имеются два интересных применения (которые ничем не связаны с выполнением расширения блока OCB):
• контроль распределения/освобождения блока OCB;
• обеспечение более эффективного распределения/ освобождения
В этом случае вы можете просто «подключиться» к функциям распределения/освобождения и контролировать использование OCB (например, вам может быть необходимо ограничить суммарное количество OCB). Это может оказаться полезным, если вы не перехватываете функцию io_open, но создание (и, возможно, удаление) OCB все-таки хотите контролировать.