Выбрать главу

Отметим, что формат у всех прототипов один и тот же. Первый параметр, ctp, указывает на структуру resmgr_context_t. Это внутренний контекстный блок, используемый библиотекой администратора ресурсов и который изменять не следует (за исключением одного поля, к обсуждению которого мы еще вернемся).

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

Третий параметр — структура типа RESMGR_HANDLE_T, называемая дескриптором (handle). Она используется для идентификации устройства, которому предназначалось сообщение. Мы тоже рассмотрим ее позже, когда будем говорить об атрибутной записи.

И, наконец, последний параметр является «резервным», или «дополнительным», и используется для функций, которым необходимы какие-либо дополнительные данные. Мы продемонстрируем применение параметра extra по назначению в обсуждении функций-обработчиков сообщений.

Таблица функций ввода/вывода resmgr_io_funcs_t

Таблица функций ввода/вывода подобна таблице функций установления соединения. Вот она (взято из <sys/resmgr.h>):

typedef struct _resmgr_io_funcs {

 unsigned nfuncs;

 int (*read)(ctp, io_read_t *msg, ocb);

 int (*write)(ctp, io_write_t *msg, ocb);

 int (*close_ocb)(ctp, void *reserved, ocb);

 int (*stat)(ctp, io_stat_t *msg, ocb);

 int (*notify)(ctp, io_notify_t *msg, ocb);

 int (*devctl)(ctp, io_devctl_t *msg, ocb);

 int (*unblock)(ctp, io_pulse_t *msg, ocb);

 int (*pathconf)(ctp, io_pathconf_t *msg, ocb);

 int (*lseek)(ctp, io_lseek_t *msg, ocb);

 int (*chmod)(ctp, io_chmod_t *msg, ocb);

 int (*chown)(ctp, io_chown_t *msg, ocb);

 int (*utime)(ctp, io_utime_t *msg, ocb);

 int (*openfd)(ctp, io_openfd_t *msg, ocb);

 int (*fdinfo)(ctp, io_fdinfo_t *msg, ocb);

 int (*lock)(ctp, io_lock_t *msg, ocb);

 int (*space)(ctp, io_space_t *msg, ocb);

 int (*shutdown)(ctp, io_shutdown_t *msg, ocb);

 int (*mmap)(ctp, io_mmap_t *msg, ocb);

 int (*msg)(ctp, io_msg_t *msg, ocb);

 int (*umount)(ctp, void *msg, ocb);

 int (*dup)(ctp, io_dup_t *msg, ocb);

 int (*close_dup)(ctp, io_close_t *msg, ocb);

 int (*lock_ocb)(ctp, void *reserved, ocb);

 int (*unlock_ocb)(ctp, void *reserved, ocb);

 int (*sync)(ctp, io_sync_t *msg, ocb);

} resmgr_io_funcs_t;

В этой структуре я тоже сократил прототипы, опустив тип элемента ctp (resmgr_context_t*) и тип последнего элемента, ocb (RESMGR_OCB_T*). Полный прототип, например, для функции read() в действительности имеет вид:

int (*read)(resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb);

Самый первый элемент структуры (nfuncs) указывает, насколько она велика (то есть сколько элементов она содержит). Текущее значение этого элемента содержится в константе _RESMGR_IO_NFUNCS.

Отметим, что списки параметров в таблице функций ввода/вывода также довольно однообразны. Первый параметр — ctp, второй параметр — msg, как и у обработчиков из таблицы функций установления соединения.

Третий параметр, однако, отличается. Этот параметр называется ocb, что расшифровывается как «Open Context Block» — «блок открытого контекста». Этот блок содержит контекст, созданный обработчиком сообщения установления соединения (например, в результате клиентского запроса open()) и доступный функциям ввода/вывода.

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

Внутренний контекстный блок resmgr_context_t

И, наконец, еще одна структура данных используется базовым уровнем библиотеки, чтобы отслеживать кое-какую информацию для себя. Вам не следует изменять содержимое этой структуры, за исключением одного элемента — вектора ввода/вывода iov.

Вот эта структура данных (взято из <sys/resmgr.h>):

typedef struct _resmgr_context {

 int                 rcvid;

 struct _msg_info    info;

 resmgr_iomsgs_t     *msg;

 struct _resmgr_ctrl *ctrl;

 int                 id;

 int                 status;

 int                 offset;

 int                 size;

 iov_t               iov[1];

} resmgr_context_t;

Как и в случае с другими структурами данных, я позволил себе опустить зарезервированные поля.

Давайте взглянем на ее содержимое.

rcvid Идентификатор отправителя, полученный от MsgReceivev(). Указывает, кому вы должны ответить (если вы намерены отвечать самостоятельно).
info Содержит информационную структуру, возвращаемую функцией MsgReceivev() в основном цикле приема сообщений библиотеки администратора ресурсов. Полезна для получения информации о клиенте, включая дескриптор узла, идентификатор процесса (PID), идентификатор потока и т.д. Подробнее см. документацию по функции MsgReceivev().
msg Указатель на объединение (union) всех возможных типов сообщений. Практически бесполезен, потому что каждая из ваших функций-обработчиков получает соответствующий элемент объединения вторым параметром.
ctrl Указатель на управляющую структуру, которую вы передали в самом начале. Опять же, для вас этот параметр не очень полезен, но зато полезен для библиотеки администратора ресурсов.
id Идентификатор точки монтирования, которой предназначалось сообщение. Когда вы вызывали resmgr_attach(), она вернула вам небольшой целочисленный идентификатор. Это и есть значение id. Отметим, что вы вероятнее всего никогда не будете использовать этот параметр самостоятельно, а будете полагаться вместо этого на атрибутную запись, передаваемую вам обработчиком io_open().
status Сюда ваша функция-обработчик помещает результат выполнения операции. Отметим, что вы должны всегда использовать макрос _RESMGR_STATUS() для заполнения этого поля. Например, если вы обрабатываете сообщение установления соединения от функции open(), причем ваш администратор ресурса предоставляет доступ «только для чтения», а клиент хотел открыть ресурс на запись, вы возвратите клиенту через errno код EROFS при помощи (обычно) _RESMGR_STATUS(ctp, EROFS).
offset Текущее смещение (в байтах) в клиентском буфере сообщений. Имеет смысл для базового уровня библиотеки только при чтении составных сообщений функцией resmgr_msgreadv().
size Этот параметр говорит, сколько байт в буфере сообщения, переданном вашей функции-обработчику, являются достоверными. Это важная цифра, поскольку она указывает на то, требуется ли читать дополнительные данные от клиента (например, если не все данные были считаны базовым уровнем библиотеки), и надо ли выделить память для ответа клиенту (например, для ответа на запрос read()). (Отметим, что в версии 2.00 есть ошибка, из-за которой это поле не заполняется в случае несоставного сообщения установления соединения. Все остальные сообщения обрабатываются корректно. Обходной путь здесь (и только здесь!) заключается в использовании параметра msglen структуры info.)
iov Таблица векторов ввода/вывода, в которую вы можете записывать возвращаемые значения, если это необходимо. Например, когда клиент вызывает read(), и у вас вызывается соответствующий обработчик read(), вам может потребоваться возвратить клиенту данные. Можно задать эти данные при помощи массива iov и возвратить что-нибудь типа _RESMGR_NPARTS(2), указав тем самым (в нашем случае), векторы iov[0] и iov[1] содержат данные для клиента. Заметьте, что массив iov определен как одноэлементный. Однако, заметьте также, что он очень удобно расположен в конце структуры. Фактическое число элементов в массиве iov определяете вы сами, когда присваиваете значение полю nparts_max вышеупомянутой управляющей структуры (см. параграф «Управляющая структура resmgr_attr_t»).