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

Значения выбираются из следующего списка:

_RESMGR_NOREPLY

Указывает библиотеке администратора ресурсов, что она не должна выполнять MsgReplyv() — в предположении, что вы либо уже сделали это самостоятельно в вашей функции-обработчике, либо собираетесь сделать это несколько позже.

_RESMGR_NPARTS(n)

Указывает библиотеке администратора ресурсов при выполнении MsgReplyv() возвратить n-элементный вектор ввода/вывода (он располагается в ctp->iov). Ваша функция ответственна за заполнение поля iov структуры ctp и возврат _RESMGR_NPARTS с корректным числом элементов.

Память под поле iov структуры ctp выделяется динамически, и ее должно быть достаточно, чтобы вместить столько число элементов массива, сколько вы записываете в iov! Детали о настройке поля nparts_max см. выше в разделе «Управляющая структура resmgr_attr_t».

_RESMGR_DEFAULT

Это говорит библиотеке администратора ресурсов выполнить низкоуровневую функцию по умолчанию (это другое семейство функций; не путайте их с iofunc_*_default()!). Это возвращаемое значение вам вряд ли когда-нибудь пригодится. В общем случае оно заставляет библиотеку администратора ресурсов возвратить клиенту значение errno, равное ENOSYS, что означает «функция не поддерживается».

_RESMGR_ERRNO(errno)

(Устаревшее.) Данное возвращаемое значение использовалось для «инкапсуляции» значения errno в возвращаемое сообщением значение. Например, если бы клиент выдал запрос open() (по записи — прим. ред.) устройству, доступному только для чтения, корректно было бы возвратить код ошибки EROFS. Поскольку данный способ сделать это считается устаревшим, вы можете возвратить код ошибки непосредственно (например, при помощи return (EROFS); вместо громоздкого _RESMGR_ERRNO(EROFS);).

_RESMGR_PTR(ctp, addr, len)

Это макрос для удобства. Он берет указатель на контекст ctp и заполняет его первый элемент IOV адресом addr и длиной len, а затем возвращает библиотеке эквивалент _RESMGR_NPARTS(1). Это может быть полезно для функций, возвращающих одноэлементные IOV.

Блокировки, разблокировки и обработка составных сообщений

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

Данные рассуждения применимы не только к составным сообщениям, но и ко всем сообщениям, принимаемым библиотекой администратора ресурсов. Первое, что делает библиотека администратора ресурсов, — она блокирует атрибутную запись, соответствующую ресурсу, используемому полученным сообщением. Затем она обрабатывает одно или более «подсообщений», содержащихся в полученном сообщении. Затем она снова разблокирует атрибутную запись.

Это гарантирует, что поступающие сообщения обрабатываются атомарно, поскольку никакой другой поток администратора ресурсов (в случае многопоточного администратора, конечно) не может «влезть» и изменить ресурс, пока наш поток этот ресурс использует. Без блокировок два клиентских потока могли бы оба выдать то, что, по их мнению, являлось бы атомарным составным сообщением (скажем, пару «lseek() — read()»). Поскольку администратор ресурсов мог выделить на обработку этих сообщений два различных потока, эти потоки могли бы в произвольном порядке вытеснять друг друга, и их lseek() могли бы друг другу помешать. Блокировки же позволяют это предотвратить, потому что каждое сообщение, получающее доступ к ресурсу, обрабатывается целиком и атомарно.

Блокировка и разблокировка ресурса выполняются вспомогательными функциями по умолчанию (iofunc_lock_ocb_default() и iofunc_unlock_ocb_default()), которые размещаются в таблице функций ввода/вывода в полях lock_ocb и unlock_ocb соответственно. Вы можете, конечно, переназначить эти функции, если хотите выполнить в процессе блокировки и разблокировки какие-либо дополнительные действия.

Заметьте, что ресурс разблокируется перед вызовом io_close(). Это необходимо, поскольку функция io_close() освободит OCB, что автоматически сделает недействительным указатель на атрибутную запись, а блокировка хранится именно там!

Замечания о функциях установления соединения

Перед тем как углубиться в отдельные сообщения, однако, есть смысл подчеркнуть, что для всех функции установления соединения структура сообщений идентична (взято из <sys/iomsg.h>, с небольшими изменениями):

struct _io_connect {

 // Для внутреннего использования

 uint16_t type;

 uint16_t subtype;

 uint32_t file_type;

 uint16_t reply_max;

 uint16_t entry_max;

 uint32_t key;

 uint32_t handle;

 uint32_t ioflag;

 uint32_t mode;

 uint16_t sflag;

 uint16_t access;

 uint16_t zero;

 uint8_t  eflag;

 // Для конечного пользователя

 uint16_t path_len;

 uint8_t  extra_type;

 uint16_t extra_len;

 char path[1];

};

Вы заметите, что я разделил структуру struct _io_connect на две части, часть «Для внутреннего использования» и часть «Для конечного пользователя».

Поля для внутреннего использования

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

• определения типа сообщения, полученного от клиента;

• проверки сообщения на достоверность (не является ли оно дезинформацией);

• отслеживания режима доступа (используется вспомогательными функциями).

Для простоты я бы рекомендовал вам всегда применять вспомогательные функции (из семейства iofunc_*_default()) во всех функциях установления соединения. Эти функции возвратят вам признак успешного/неудачного завершения, после чего вы сможете использовать в функции установления соединения «поля для конечного пользователя».