Динамическое создание IPC-каналов
Возможность динамического создания IPC-каналов позволяет изменять топологию взаимодействия сущностей "на лету". Это требуется, если неизвестно, какая именно сущность-сервер станет поставщиком ресурса, требуемого сущности-клиенту. Например, может быть неизвестно, на какой именно накопитель нужно будет записывать данные.
Динамически созданный IPC-канал, в отличие от статически созданного, имеет следующие особенности:
- создается между запущенными сущностями;
- создается совместно сущностью-клиентом и сущностью-сервером;
- предусматривает удаление;
- предусматривает использование сервера имен (сущности
kl.core.NameServer
), который обеспечивает передачу сведений о доступных реализациях интерфейсов от сущностей-серверов к сущностям-клиентам.
При динамическом создании IPC-канала используются функции:
- интерфейса сервера имен (Name Server);
- менеджера соединений (Connection Manager).
Интерфейс сервера имен представлен для пользователя следующими файлами:
coresrv/ns/ns_api.h
– заголовочный файл библиотекиlibkos
;coresrv/ns/NameServer.idl
– описание IPC-интерфейса сервера имен на языке IDL.
Менеджер соединений представлен для пользователя следующими файлами:
coresrv/cm/cm_api.h
– заголовочный файл библиотекиlibkos
;services/cm/CM.idl
– описание IPC-интерфейса менеджера соединений на языке IDL.
Динамическое создание IPC-канала осуществляется по следующему сценарию:
- Запускаются сущность-клиент, сущность-сервер и сервер имен.
- Сущность-сервер подключается к серверу имен функцией
NsCreate()
и публикует имя сущности-сервера, имя интерфейса и имя реализации интерфейса функциейNsPublishService()
. - Сущность-клиент подключается к серверу имен функцией
NsCreate()
и выполняет поиск имени сущности-сервера и имени реализации интерфейса по имени интерфейса функциейNsEnumServices()
. - Сущность-клиент запрашивает доступ к реализации интерфейса функцией
KnCmConnect()
, передавая ей в качестве аргументов найденные имя сущности-сервера и имя реализации интерфейса. - Сущность-сервер вызывает функцию
KnCmListen()
для проверки наличия запроса доступа к предоставляемой реализации интерфейса от сущности-клиента. - Сущность-сервер принимает запрос доступа к предоставляемой реализации интерфейса функцией
KnCmAccept()
, передавая ей в качестве аргументов имя сущности-клиента и имя реализации интерфейса, которые получены функциейKnCmListen()
.
Для использования сервера имен политика безопасности решения должна разрешать взаимодействие сущности kl.core.NameServer
с сущностями, между которыми могут динамически создаваться IPC-каналы.
Сущность-сервер может снимать с публикации на сервере имен ранее опубликованные реализации интерфейсов функцией NsUnPublishService()
.
Сущность-сервер может отклонять запросы доступа к реализациям интерфейсов функцией KnCmDrop()
.
ns_api.h (фрагмент)
/**
* Функция осуществляет попытку подключиться к серверу имен name
* в течение msecs миллисекунд. Если параметр name имеет значение
* RTL_NULL, функция пытается подключиться к серверу имен ns
* (серверу имен по умолчанию). Выходной параметр ns содержит дескриптор
* соединения с сервером имен.
* В случае успеха функция возвращает rcOk, иначе возвращает код ошибки.
*/
Retcode NsCreate(const char *name, rtl_uint32_t msecs, NsHandle *ns);
/**
* Функция публикует реализацию интерфейса на сервере имен.
* Параметр ns задает дескриптор соединения с сервером имен.
* Параметр server задает имя сущности-сервера (например, ata).
* Параметр type задает имя публикуемого интерфейса (например, IBlkDev).
* Параметр service задает имя реализации публикуемого интерфейса
* (например, blkdev.blkdev).
* В случае успеха функция возвращает rcOk, иначе возвращает код ошибки.
*/
Retcode NsPublishService(NsHandle ns, const char *type, const char *server,
const char *service);
/**
* Функция снимает с публикации реализацию интерфейса на сервере имен.
* Параметр ns задает дескриптор соединения с сервером имен.
* Параметр server задает имя сущности-сервера. Параметр type задает имя
* опубликованного интерфейса. Параметр service задает имя реализации
* опубликованного интерфейса.
* В случае успеха функция возвращает rcOk, иначе возвращает код ошибки.
*/
Retcode NsUnPublishService( NsHandle ns, const char *type, const char *server,
const char *service);
/**
* Функция перечисляет реализации интерфейса, опубликованные на сервере
* имен. Параметр ns задает дескриптор соединения с сервером имен.
* Параметр type задает имя требуемого интерфейса. Параметр index
* задает индекс для перечисления реализаций интерфейса.
* Выходной параметр server содержит имя сущности-сервера, предоставляющей
* реализацию интерфейса. Выходной параметр service содержит имя реализации
* интерфейса.
* Например, перечисление реализаций интерфейса IBlkDev осуществляется
* следующим образом.
* rc = NsEnumServices(ns, "IBlkDev", 0, outServerName, outServiceName);
* rc = NsEnumServices(ns, "IBlkDev", 1, outServerName, outServiceName);
* ...
* rc = NsEnumServices(ns, "IBlkDev", N, outServerName, outServiceName);
* Вызовы функции с инкрементированием индекса продолжается до тех пор,
* пока функция не вернет rcResourceNotFound.
* В случае успеха функция возвращает rcOk, иначе возвращает код ошибки.
*/
Retcode NsEnumServices(NsHandle ns, const char *type, unsigned index,
char *server, char *service);
cm_api.h (фрагмент)
/**
* Функция запрашивает доступ к реализации интерфейса с именем service,
* предоставляемой сущностью-сервером server. Параметр msecs задает время
* ожидания принятия запроса сущностью-сервером в миллисекундах. Выходной
* параметр endpoint содержит клиентский IPC-дескриптор. Выходной параметр
* rsid содержит идентификатор реализации интерфейса.
* В случае успеха функция возвращает rcOk, иначе возвращает код ошибки.
*/
Retcode KnCmConnect(const char *server, const char *service,
rtl_uint32_t msecs, Handle *endpoint,
rtl_uint32_t *rsid);
/**
* Функция проверяет наличие запроса доступа к реализации интерфейса
* с именем filter. Если параметр filter имеет значение RTL_NULL,
* проверяется наличие запроса доступа к любой реализации
* интерфейса. Параметр msecs задает время ожидания запроса
* в миллисекундах. Выходной параметр client содержит имя
* сущности-клиента. Выходной параметр service содержит имя реализации
* интерфейса.
* В случае успеха функция возвращает rcOk, иначе возвращает код ошибки.
*/
Retcode KnCmListen(const char *filter, rtl_uint32_t msecs, char *client,
char *service);
/**
* Функция отклоняет запрос доступа к реализации интерфейса
* с именем service от сущности-клиента client.
* В случае успеха функция возвращает rcOk, иначе возвращает код ошибки.
*/
Retcode KnCmDrop(const char *client, const char *service);
/**
* Функция принимает запрос доступа к реализации интерфейса
* с именем service от сущности-клиента client. Параметр rsid задает
* идентификатор реализации интерфейса. Параметр listener задает
* слушающий дескриптор. Если параметр listener имеет значение
* INVALID_HANDLE, создается новый слушающий дескриптор.
* Выходной параметр endpoint содержит серверный IPC-дескриптор.
* В случае успеха функция возвращает rcOk, иначе возвращает код ошибки.
*/
Retcode KnCmAccept(const char *client, const char *service, rtl_uint32_t rsid,
Handle listener, Handle *endpoint);
Параметр filter
функции KnCmListen()
является зарезервированным и не влияет на ее поведение. Поведение функции соответствует значению RTL_NULL
параметра filter
.
Слушающий дескриптор – серверный IPC-дескриптор с расширенными правами. Он создается при вызове функции KnCmAccept()
с аргументом INVALID_HANDLE
в параметре listener
. Если при вызове функции KnCmAccept()
передать ей слушающий дескриптор, то созданный серверный IPC-дескриптор обеспечит возможность получать запросы по всем IPC-каналам, ассоциированным с этим слушающим дескриптором. (Первый IPC-канал, ассоциированный со слушающим дескриптором, создается при вызове функции KnCmAccept()
с аргументом INVALID_HANDLE
в параметре listener
. Второй и последующие IPC-каналы, ассоциированные со слушающим дескриптором, создаются при втором и последующих вызовах функции KnCmAccept()
с передачей ей слушающего дескриптора, полученного при первом вызове.)
Слушающий дескриптор используется также при статическом создании IPC-каналов. Он создается при вызове функции KnHandleConnectEx()
с аргументом INVALID_HANDLE
в параметре srListener
. Если при вызове функции KnHandleConnectEx()
передать ей слушающий дескриптор, то созданный серверный IPC-дескриптор обеспечит возможность получать запросы по всем IPC-каналам, ассоциированным с этим слушающим дескриптором. Ассоциация слушающего дескриптора с несколькими IPC-каналами происходит, когда для одной сущности-сервера создается несколько IPC-каналов с одинаковыми именами.
Если динамически созданный IPC-канал больше не требуется, его клиентский и серверный дескрипторы нужно удалить. При необходимости IPC-канал может быть создан снова.