Когда придет время заполнить таблицы функций установления соединения и ввода/вывода, рекомендуется сначала воспользоваться функцией 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);
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 в действительности имеет вид: