Чтение онлайн

ЖАНРЫ

Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform

Кёртен Роб

Шрифт:
Более эффективное распределение

Другое применение для переопределения встроенных библиотечных функций распределения/освобождения OCB может заключаться в том, что вы можете захотеть хранить OCB в свободном списке вместо использования библиотечных calloc и free. Если вы распределяете и освобождаете OCB с большой частотой, это может оказаться более эффективно.

Расширение атрибутной записи

Вы можете захотеть расширить атрибутную запись в случаях, когда вам необходимо хранить дополнительную информацию об устройствах. Поскольку атрибутные записи создаются «по каждому устройству», это означает, что любая дополнительная информация, которую вы сохраните там, будет доступна

для всех OCB, относящихся к этому устройству (поскольку OCB содержит указатель на атрибутную запись). В расширенных атрибутных записях часто хранятся такие параметры как скорость передачи данных по последовательному каналу, и т.п.

Расширять атрибутную запись намного проще, чем OCB, потому что атрибутные записи в любом случае распределяются и освобождаются вашим кодом.

Вам нужно будет выполнить тот же трюк с переопределением атрибутной записи в заголовочных файлах, как мы это делали ранее при расширении OCB:

#define IOFUNC_ATTR_T struct my_attr

#include <sys/iofunc.h>

Затем вы фактически определяете содержимое ваших расширенных атрибутных записей. Отметьте, что расширенная атрибутная запись должна включать в себя стандартную атрибутную запись первым элементом — аналогично случаю с расширением OCB (и по тем же самым причинам).

Блокирование в пределах администратора ресурсов

До настоящего момента мы избегали разговоров о возможности блокирования в пределах администратора ресурсов. Мы предполагали, что у нас есть функция-обработчик (например, io_read), и что данные будут доступны немедленно. А что если нам придется блокироваться в ожидании данных? Например, выполнение read применительно к последовательному порту может потребовать блокирования до приема символа. Очевидно, что мы не можем предсказать, сколько может продолжаться такое ожидание.

Блокирование в пределах администратора ресурсов базируется на тех же самых принципах, которые мы обсуждали в главе «Обмен сообщениями» — в конце концов, администратор ресурса фактически является сервером, который обрабатывает рад четко определенных сообщений. Когда прибывает сообщение, соответствующее клиентскому запросу read, оно прибывает вместе с идентификатором отправителя (receive ID), и клиент блокируется. Если у администратора ресурсов есть данные, он просто возвращает их клиенту, как мы уже видели в различных приведенных ранее примерах. Однако, если данные недоступны, администратор ресурсов должен будет удерживать этого клиента в заблокированном состоянии (конечно, если клиент для этой операции определил блокирующий режим), чтобы иметь возможность продолжить обработку других сообщений. Реально это означает, что поток администратора ресурсов, который принял сообщение от клиента, не должен блокироваться в ожидании данных — в противном случае это может закончиться для администратора ресурсов огромным числом заблокированных потоков, каждый из которых ожидал бы данные от некоего устройства.

Правильным решением для этой проблемы является сохранение идентификатора отправителя полученного сообщения в какой-нибудь очереди и возврат из вашего обработчика константы _RESMGR._NOREPLY. Это укажет библиотеке администратора ресурсов, что обработка сообщения закончена, но клиента пока разблокировать не надо.

Несколько позже, по готовности данных, вы сможете выбрать идентификатор отправителя нужного клиента и сконструировать ответное сообщение с данными. После этого можно отвечать клиенту.

Вы могли бы также расширить эту концепцию добавлением тайм-аутов, как мы это делали в главе «Часы, таймеры и периодические уведомления» (параграф «Поддерживаемые сервером тайм-ауты»). Если говорить вкратце, там по истечении некоторого интервала времени клиентский запрос считался «просроченным», и сервер отвечал по сохраненному по идентификатору отправителя неким сообщением об ошибке.

Возврат элементов каталога

В приведенном ранее примере функции io_read мы уже видели, как происходит возврат данных. Как было упомянуто в описании функции io_read (в разделе «Алфавитный список

функций установления соединения и ввода-вывода»), io_read можно возвращать и элементы каталога тоже. Поскольку это может понадобиться далеко не всем, я решил рассмотреть этот вопрос здесь отдельно.

Прежде всего давайте посмотрим, почему и когда вашей io_read могло бы понадобиться возвращать элементы каталога, а не «сырые» данные.

Если вы дискретно объявляете элементы в пространстве имени путей, и эти элементы не помечены флагом _RESMGR_FLAG_DIR, тогда вам не придется возвращать элементы каталога из функции io_read. Если рассматривать это как «файловую систему», то ваш объект будет «файлом». Если же, с другой стороны, вы указываете _RESMGR_FLAG_DIR, то будет создан объект типа «каталог». Никто, кроме вас, не знает ничего о содержимом этого каталога, поэтому вы должны будете предоставить эти данные. Это и есть ответ на вопрос, почему функции io_read может понадобиться возвращать элементы каталогов.

Вообще говоря…

Вообще говоря, возврат элементов каталога — это почти то же самое, что и возврат «сырых» данных, за исключением того, что:

• вы должны возвратить целое число структур типа

struct dirent
;

• эти структуры

struct dirent
должны быть заполнены.

Первый пункт означает, что вы не можете возвратить, например, семь с половиной структур

struct dirent
. Если восемь структур не вписываются в выделенное пространство, то вы должны будете возвратить только семь элементов.

Второй пункт достаточно очевиден. Он упомянут здесь только потому, что заполнение структуры

struct dirent
может быть несколько «хитрее», чем «сырой» подход к данным в случае с «обычной» io_read.

Структура
struct dirent
и ее друзья

Давайте взглянем на структуру

struct dirent
, поскольку это именно то, что возвращает функция io_read в случае чтения каталога. Мы также вкратце рассмотрим клиентские вызовы, имеющие дело с элементами каталогов, поскольку у них со структурой
struct dirent
существует ряд интересных взаимоотношений.

Чтобы работать с каталогами, клиент использует функции closedir, opendir, readdir, rewinddir, seekdir и telldir.

Обратите внимание на сходство с «нормальными» функций для файлов (и совпадение применяемых типов сообщений):

Функция для работы с каталогами Функция для работы с файлами Сообщение
closedir close IO_CLOSE_DUP
opendir open _IO_CONNECT
readdir read _IO_READ
rewinddir lseek _IO_LSEEK
seekdir lseek _IO_LSEEK
telldir tell _IO_LSEEK
Поделиться с друзьями: