Структуры данных
Первое, в чем следует разобраться, — это структуры данных, которые управляют работой библиотеки:
• управляющая структура resmgr_attr_t
• таблица функций установления соединения resmgr_connect_funcs_t
• таблица функций ввода-вывода resmgr_io_funcs_t и еще одна внутренняя структура данных библиотеки:
• внутренний блок контекста resmgr_context_t
Позже мы рассмотрим такие типы данных как блок открытого контекста (OCB), атрибутную запись (attributes structure) и запись точки монтирования (mount structure), которые используются POSIX-уровнем библиотеки.
Управляющая структура resmgr_attr_t
Управляющая структура (типа resmgr_attr_t) передается функции resmgr_start(), которая несколько ее элементов и отвечает за основной цикл приема сообщений.
Управляющая структура (взято из <sys/dispatch.h>) содержит следующее:
typedef struct _resmgr_attr {
unsigned flags;
unsigned nparts_max;
unsigned msg_max_size;
int (*other_func)(resmgr_context_t *ctp, void *msg);
} resmgr_attr_t;
Обработчик сообщений
other_funcВообще говоря, использования этого элемента следует избегать. Этот элемент, если не равен NULL, указывает на подпрограмму, которая должна быть вызвана, если принято сообщение, не распознанное библиотекой. Хоть это и можно было бы использовать для реализации «нестандартных» сообщений, но это нехорошая практика (применяйте либо обработчики _IO_DEVCTL, либо _IO_MSG — см. ниже). Если вы хотите обрабатывать входящие импульсы, рекомендую для этого применять функцию pulse_attach().
Так что оставьте у этого элемента значение NULL.
Параметры, задающие размеры структур данных
Эти два параметра используются для управления размерами областей памяти, используемых при обмене сообщениями.
Параметр nparts_max управляет размером динамически выделяемого вектора ввода/вывода (элемент iov в структуре типа resmgr_context_t — контекстном блоке библиотеки администратора ресурсов, см. ниже). Обычно этот параметр подстраивают, когда некоторые из функций-обработчиков возвращают более чем одноэлементный вектор ввода-вывода (IOV). Отметим, что этот параметр применяется только к исходящим сообщениям на поступающие сообщения не влияет.
Параметр msg_max_size управляет размером буферного пространства, которое библиотека администратора ресурсов должна выделить под входящее сообщение. Библиотека администратора ресурсов установит этот параметр в значение как минимум соответствующее наибольшему заголовку принимаемого сообщения. Это гарантирует, что когда будет вызвана функция- обработчик, ей будет передан полный заголовок сообщения. Отметим, однако, что присутствие в буфере следующих за заголовком данных (если таковые имеются) не гарантируется, даже если параметр msg_max_size задан достаточно большим. (Размеры буферов обсуждаются в параграфе «Внутренний контекстный блок resmgr_context_t», см. ниже).
Параметр
flagsЭтот параметр дает библиотеке администратора ресурсов дополнительную информацию. В нашем случае мы передадим просто нуль (0). Другие значения этого параметра можно найти в справочном руководстве по Си-библиотеке, в разделе, посвященном функции resmgr_attach().
Таблица функций установления соединения resmgr_connect_funcs_t
Когда библиотека администратора ресурсов принимает сообщение, она проверяет тип сообщения и смотрит, что можно сделать. Базовый уровень библиотеки содержит две таблицы, которые определяют это поведение. Это таблица типа resmgr_connect_funcs_t, которая содержит список обработчиков сообщений установления соединения, а также таблица типа геsmgr_io_funсs_t, которая содержит аналогичный список обработчиков сообщений ввода/вывода — ее мы рассмотрим несколько позже.
Когда придет время заполнить таблицы функций установления соединения и ввода/вывода, рекомендуется сначала воспользоваться функцией iofunc_func_init(), чтобы инициализировать таблицы функциями по умолчанию, определенными на уровне POSIX. Тогда, если вам потребуется заменить обработчик какого-либо сообщения, вы просто подставляете вместо POSIX-обработчика по умолчанию свою собственную функцию. Мы увидим это в разделе «Подстановка своих собственных функций». А сейчас давайте рассмотрим собственно таблицу функций установления соединения (взято из <sys/resmgr.h>):
typedef struct _resmgr_connect_funcs {
unsigned nfuncs;
int (*open)(ctp, io_open_t *msg, handle, void *extra);
int (*unlink)(ctp, io_unlink_t *msg, handle,
void *reserved);
int (*rename)(ctp, io_rename_t *msg, handle,
io_rename_extra_t *extra);
int (*mknod)(ctp, io_mknod_t *msg, handle,
void *reserved);
int (*readlink)(ctp, io_readlink_t *msg, handle,
void *reserved);
int (*link)(ctp, io_link_t *msg, handle,
io_link_extra_t *extra);
int (*unblock)(ctp, io_pulse_t *msg, handle,
void *reserved);
int (*mount) (ctp, io_mount_t *msg, handle,
io_mount_extra_t *extra);
} resmgr_connect_funcs_t;
Заметьте, что я сократил прототипы, опустив тип первого параметра, ctp (resmgr_context_t*), и третьего, handle (RESMGR_HANDLE_T).
Полный прототип для, например, функции open() в действительности имеет вид:
int (*open)(resmgr_context_t *ctp, io_open_t *msg,
RESMGR_HANDLE_T *handle, void *extra);
Первый элемент структуры (nfuncs) указывает, насколько она велика (то есть сколько в ней содержится элементов). Для приведенной выше структуры он должен быть равен 8, поскольку элементов в ней восемь (от open() до mount()). Этот элемент нужен главным образом для того, чтобы позволить QSSL обновлять данную библиотеку без каких бы то ни было вредных последствий для вашего кода. Предположим, к примеру, что вы компилировали свой код со значением 8, а затем QSSL обновила библиотеку, и параметр стал равен 9. Библиотека могла бы себе сказать: «Ага! Пользователь библиотеки был скомпилирован с расчетом на 8 функций, а их у нас теперь 9. Надо бы назначить 9-й функции обработчик по умолчанию.» Текущее значение параметра nfuncs хранится в заголовочном файле <sys/resmgr.h> в виде именованной константы _RESMGR_CONNECT_NFUNCS. Используйте эту константу при заполнении таблицы функций установления соединения вручную (хотя лучше всего применять для этого функцию iofunc_func_init()).
Отметим, что формат у всех прототипов один и тот же. Первый параметр, 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);