Содержание
- IPC и транспорт
- Создание IPC-каналов
- Добавление в решение службы из состава KasperskyOS Community Edition
- Создание и использование собственных служб
Создание IPC-каналов
Есть два способа создания IPC-каналов: статический и динамический.
Статическое создание IPC-каналов проще в реализации, поскольку для него можно использовать init-описание.
Динамическое создание IPC-каналов позволяет изменять топологию взаимодействия процессов "на лету". Это требуется, если неизвестно, какой именно сервер предоставляет службу, необходимую клиенту. Например, может быть неизвестно, на какой именно накопитель нужно будет записывать данные.
Статическое создание IPC-канала
Статическое создание IPC-каналов имеет следующие особенности:
- Клиент и сервер еще не запущены в момент создания IPC-канала.
- Создание IPC-канала выполняется родительским процессом, запускающим клиента и сервера (обычно это Einit).
- В случае удаления IPC-канал невозможно восстановить.
- Чтобы получить IPC-дескриптор и идентификатор службы (riid) после создания IPC-канала, клиент и сервер должны использовать API, определенный в заголовочном файле
sysroot-*-kos/include/coresrv/sl/sl_api.h
из состава KasperskyOS SDK.
Статически создаются IPC-каналы, заданные в init-описании.
Динамическое создание IPC-канала
Динамическое создание IPC-каналов имеет следующие особенности:
- Клиент и сервер уже запущены в момент создания IPC-канала.
- Создание IPC-канала выполняется совместно клиентом и сервером.
- Вместо удаленного может быть создан новый IPC-канал.
- Клиент и сервер получают IPC-дескриптор и идентификатор службы (riid) сразу после успешного создания IPC-канала.
Добавление в решение службы из состава KasperskyOS Community Edition
Чтобы программа Client
могла использовать ту или иную функциональность через механизм IPC, необходимо:
- Найти в составе KasperskyOS Community Edition исполняемый файл (условно назовем его
Server
), реализующий нужную функциональность. (Под функциональностью мы здесь понимаем одну или несколько служб, имеющих самостоятельные IPC-интерфейсы) - Подключить CMake-пакет, содержащий файл
Server
и его клиентскую библиотеку. - Добавить исполняемый файл
Server
в образ решения. - Изменить init-описание так, чтобы при старте решения программа
Einit
запускала новый серверный процесс из исполняемого файлаServer
и соединяла его IPC-каналом с процессом, запускаемым из файлаClient
.Необходимо указать корректное имя IPC-канала, чтобы транспортные библиотеки могли идентифицировать этот канал и найти его IPC-дескрипторы. Корректное имя IPC-канала, как правило, совпадает с именем класса серверного процесса. VFS при этом является исключением.
- Изменить PSL-описание так, чтобы разрешить запуск серверного процесса и IPC-взаимодействие между клиентом и сервером.
- Подключить в исходном коде программы
Client
заголовочный файл с методами сервера. - Скомпоновать программу
Client
с клиентской библиотекой.
Пример добавления GPIO-драйвера в решение
В составе KasperskyOS Community Edition есть файл gpio_hw
, реализующий функциональность GPIO-драйвера.
Следующие команды подключают CMake‑пакет gpio:
.\CMakeLists.txt
Добавление исполняемого файла gpio_hw
в образ решения производится с помощью переменной gpio_HW_ENTITY
, имя которой можно найти в конфигурационном файле пакета – /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/lib/cmake/gpio/gpio-config.cmake:
einit\CMakeLists.txt
В init-описание нужно добавить следующие строки:
init.yaml.in
В PSL-описание нужно добавить следующие строки:
security.psl.in
В коде программы Client
нужно подключить заголовочный файл, в котором объявлены методы GPIO-драйвера:
client.c
Наконец, нужно скомпоновать программу Client
с клиентской библиотекой GPIO:
client\CMakeLists.txt
Для корректной работы GPIO‑драйвера может понадобиться добавить в решение компонент BSP. Чтобы не усложнять этот пример, мы не рассматриваем здесь BSP. Подробнее см. пример gpio_output: /opt/KasperskyOS-Community-Edition-<version>/examples/gpio_output
Обзор: структура IPC-сообщения
В KasperskyOS все взаимодействия между процессами статически типизированы. Допустимые структуры IPC-сообщения определяются IDL-описаниями серверов.
IPC-сообщение (как запрос, так и ответ) содержит фиксированную часть и опционально арену.
Фиксированная часть IPC-сообщения
Фиксированная часть IPC-сообщения содержит RIID, MID и опционально параметры интерфейсных методов фиксированного размера.
Параметры фиксированного размера – это параметры, которые имеют IDL-типы фиксированного размера.
RIID и MID идентифицируют вызываемый интерфейс и метод:
- RIID (Runtime Implementation ID) является порядковым номером используемой службы в наборе служб сервера (начиная с нуля).
- MID (Method ID) является порядковым номером вызываемого метода в наборе методов используемой службы (начиная с нуля).
Тип фиксированной части IPC-сообщения генерируется компилятором NK на основе IDL-описания интерфейса. Для каждого метода интерфейса генерируется отдельная структура. Также генерируются типы union
для хранения любого запроса к процессу, компоненту или интерфейсу. Подробнее см. Пример генерации транспортных методов и типов.
Арена IPC-сообщения
Арена IPC-сообщения (далее также арена) содержит параметры интерфейсных методов (и/или элементы этих параметров) переменного размера.
Параметры переменного размера – это параметры, которые имеют IDL‑типы переменного размера.
Подробнее см. "Работа с ареной IPC-сообщений".
Максимальный размер IPC-сообщения
Максимальный размер IPC-сообщения определяется параметрами ядра KasperskyOS. На большинстве поддерживаемых KasperskyOS аппаратных платформ совокупный размер фиксированной части и арены IPC-сообщения не может превышать 4, 8 или 16 МБ.
Проверка структуры IPC-сообщения модулем безопасности
Перед тем как вызывать связанные с IPC-сообщением правила, подсистема Kaspersky Security Module проверяет отправляемое IPC-сообщение на корректность. Проверяются как запросы, так и ответы. Если IPC-сообщение имеет некорректную структуру, оно будет отклонено без вызова связанных с ним методов моделей безопасности.
Реализация IPC-взаимодействия
Чтобы упростить разработчику работу над реализацией IPC-взаимодействия, в составе KasperskyOS Community Edition поставляются:
- Компилятор NK, позволяющий сгенерировать транспортные методы и типы.
- Библиотека
libkos
, которая предоставляет API для работы с IPC-транспортом.
Реализация простейшего IPC-взаимодействия показана в примерах echo и ping (/opt/KasperskyOS-Community-Edition-<version>/examples/
).
Получение IPC-дескриптора
Клиентский и серверный IPC-дескрипторы требуется получить, если для используемой службы нет готовых транспортных библиотек (например, вы написали собственную службу). Для самостоятельной работы с IPC-транспортом нужно предварительно инициализировать его с помощью метода NkKosTransport_Init()
, передав в качестве второго аргумента IPC-дескриптор используемого канала.
Подробнее см. примеры echo и ping (/opt/KasperskyOS-Community-Edition-<version>/examples/
).
Для использования служб, которые реализованы в исполняемых файлах в составе KasperskyOS Community Edition, нет необходимости получать IPC-дескриптор. Вся работа с транспортом, включая получение IPC-дескрипторов, выполняется поставляемыми транспортными библиотеками.
См. примеры gpio_*, net_*, net2_* и multi_vfs_* (/opt/KasperskyOS-Community-Edition-<version>/examples/
).
Получение IPC-дескриптора при статическом создании канала
При статическом создании IPC-канала как клиент, так и сервер могут сразу после своего запуска получить свои IPC-дескрипторы с помощью методов ServiceLocatorRegister()
и ServiceLocatorConnect()
, указав имя созданного IPC-канала.
Например, если IPC-канал имеет имя server_connection
, то на клиентской стороне необходимо вызвать:
На серверной стороне необходимо вызвать:
Подробнее см. примеры echo и ping (/opt/KasperskyOS-Community-Edition-<version>/examples/
), а также заголовочный файл /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/sl/sl_api.h
.
Закрытие полученного IPC-дескриптора приведет к недоступности IPC-канала. Если IPC-дескриптор был закрыт, то получить его повторно и восстановить доступ к IPC-каналу невозможно.
Получение IPC-дескриптора при динамическом создании канала
Как клиент, так и сервер получают свои IPC-дескрипторы сразу при успешном динамическом создании IPC-канала.
Клиентский IPC-дескриптор является одним из выходных (out
) аргументов метода KnCmConnect()
. Серверный IPC-дескриптор является выходным аргументом метода KnCmAccept()
. Подробнее см. заголовочный файл /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/cm/cm_api.h
.
Если динамически созданный IPC-канал больше не требуется, его клиентский и серверный дескрипторы нужно закрыть. При необходимости IPC-канал может быть создан снова.
В началоПолучение идентификатора службы (riid)
Идентификатор службы (riid) требуется получить на клиентской стороне, если для используемой службы нет готовых транспортных библиотек (например, вы написали собственную службу). Для вызова методов сервера необходимо на клиентской стороне предварительно вызвать метод инициализации прокси-объекта, передав в качестве третьего параметра идентификатор службы. Например, для интерфейса Filesystem
:
Подробнее см. примеры echo и ping (/opt/KasperskyOS-Community-Edition-<version>/examples/
).
Для использования служб, которые реализованы в исполняемых файлах в составе KasperskyOS Community Edition, нет необходимости получать идентификатор службы. Вся работа с транспортом выполняется поставляемыми транспортными библиотеками.
См. примеры gpio_*, net_*, net2_* и multi_vfs_* (/opt/KasperskyOS-Community-Edition-<version>/examples/
).
Получение идентификатора службы при статическом создании канала
При статическом создании IPC-канала клиент может получить идентификатор нужной службы с помощью метода ServiceLocatorGetRiid()
, указав дескриптор IPC-канала и квалифицированное имя службы. Например, если экземпляр компонента OpsComp
предоставляет службу FS
, то на клиентской стороне необходимо вызвать:
Подробнее см. примеры echo и ping (/opt/KasperskyOS-Community-Edition-<version>/examples/
), а также заголовочный файл /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/sl/sl_api.h
.
Получение идентификатора службы при динамическом создании канала
Клиент получает идентификатор службы сразу при успешном динамическом создании IPC-канала. Клиентский IPC-дескриптор является одним из выходных (out
) аргументов метода KnCmConnect()
. Подробнее см. заголовочный файл /opt/KasperskyOS-Community-Edition-<version>/sysroot-aarch64-kos/include/coresrv/cm/cm_api.h
.
Пример генерации транспортных методов и типов
При сборке решения компилятор NK на основе EDL-, CDL- и IDL-описаний генерирует набор специальных методов и типов, упрощающих формирование, отправку, прием и обработку IPC-сообщений.
В качестве примера рассмотрим класс процессов Server
, предоставляющий службу FS
, которая содержит единственный метод Open()
:
Server.edl
Operations.cdl
Filesystem.idl
На основе этих описаний будут сгенерированы файлы Server.edl.h
, Operations.cdl.h
, и Filesystem.idl.h
содержащие следующие методы и типы:
Методы и типы, общие для клиента и сервера
- Абстрактные интерфейсы, содержащие указатели на реализации входящих в них методов.
В нашем примере будет сгенерирован один абстрактный интерфейс –
Filesystem
:typedef struct Filesystem { const struct Filesystem_ops *ops; } Filesystem; typedef nk_err_t Filesystem_Open_fn(struct Filesystem *, const struct Filesystem_Open_req *, const struct nk_arena *, struct Filesystem_Open_res *, struct nk_arena *); typedef struct Filesystem_ops { Filesystem_Open_fn *Open; } Filesystem_ops; - Набор интерфейсных методов.
При вызове интерфейсного метода в запросе автоматически проставляются соответствующие значения RIID и MID.
В нашем примере будет сгенерирован единственный интерфейсный метод
Filesystem_Open
:nk_err_t Filesystem_Open(struct Filesystem *self, struct Filesystem_Open_req *req, const struct nk_arena *req_arena, struct Filesystem_Open_res *res, struct nk_arena *res_arena)
Методы и типы, используемые только на клиенте
- Типы прокси-объектов.
Прокси-объект используется как аргумент интерфейсного метода. В нашем примере будет сгенерирован единственный тип прокси-объекта
Filesystem_proxy
:typedef struct Filesystem_proxy { struct Filesystem base; struct nk_transport *transport; nk_iid_t iid; } Filesystem_proxy; - Функции для инициализации прокси-объектов.
В нашем примере будет сгенерирована единственная инициализирующая функция
Filesystem_proxy_init
:void Filesystem_proxy_init(struct Filesystem_proxy *self, struct nk_transport *transport, nk_iid_t iid) - Типы, определяющие структуру фиксированной части сообщения для каждого конкретного метода.
В нашем примере будет сгенерировано два таких типа:
Filesystem_Open_req
(для запроса) иFilesystem_Open_res
(для ответа).typedef struct __nk_packed Filesystem_Open_req { __nk_alignas(8) struct nk_message base_; __nk_alignas(4) nk_ptr_t name; } Filesystem_Open_req; typedef struct Filesystem_Open_res { union { struct { __nk_alignas(8) struct nk_message base_; __nk_alignas(4) nk_uint32_t h; }; struct { __nk_alignas(8) struct nk_message base_; __nk_alignas(4) nk_uint32_t h; } res_; struct Filesystem_Open_err err_; }; } Filesystem_Open_res;
Методы и типы, используемые только на сервере
- Тип, содержащий все службы компонента, а также инициализирующая функция. (Для каждого компонента сервера.)
При наличии вложенных компонентов этот тип также содержит их экземпляры, а инициализирующая функция принимает соответствующие им инициализированные структуры. Таким образом, при наличии вложенных компонентов, их инициализацию необходимо начинать с самого вложенного.
В нашем примере будет сгенерирована структура
Operations_component
и функцияOperations_component_init
:typedef struct Operations_component { struct Filesystem *FS; }; void Operations_component_init(struct Operations_component *self, struct Filesystem *FS) - Тип, содержащий все службы, предоставляемые сервером непосредственно; все экземпляры компонентов, входящие в сервер; а также инициализирующая функция.
В нашем примере будет сгенерирована структура
Server_entity
и функцияServer_entity_init
:typedef struct Server_component { struct : Operations_component *OpsComp; } Server_component; void Server_entity_init(struct Server_entity *self, struct Operations_component *OpsComp) - Типы, определяющие структуру фиксированной части сообщения для любого метода конкретного интерфейса.
В нашем примере будет сгенерировано два таких типа:
Filesystem_req
(для запроса) иFilesystem_res
(для ответа).typedef union Filesystem_req { struct nk_message base_; struct Filesystem_Open_req Open; }; typedef union Filesystem_res { struct nk_message base_; struct Filesystem_Open_res Open; }; - Типы, определяющие структуру фиксированной части сообщения для любого метода любой службы конкретного компонента.
При наличии вложенных компонентов эти типы также содержат структуры фиксированной части сообщения для любых методов любых служб, включенных во все вложенные компоненты.
В нашем примере будет сгенерировано два таких типа:
Operations_component_req
(для запроса) иOperations_component_res
(для ответа).typedef union Operations_component_req { struct nk_message base_; Filesystem_req FS; } Operations_component_req; typedef union Operations_component_res { struct nk_message base_; Filesystem_res FS; } Operations_component_res; - Типы, определяющие структуру фиксированной части сообщения для любого метода любой службы конкретного компонента, экземпляр которого входит в сервер.
При наличии вложенных компонентов эти типы также содержат структуры фиксированной части сообщения для любых методов любых служб, включенных во все вложенные компоненты.
В нашем примере будет сгенерировано два таких типа:
Server_entity_req
(для запроса) иServer_entity_res
(для ответа).typedef union Server_component_req { struct nk_message base_; Filesystem_req OpsComp_FS; } Server_component_req; typedef union Server_component_res { struct nk_message base_; Filesystem_res OpsComp_FS; } Server_component_res; - Dispatch-методы (диспетчеры) для отдельного интерфейса, компонента или класса процессов.
Диспетчеры анализируют полученный запрос (значения RIID и MID), вызывают реализацию соответствующего метода, после чего сохраняют ответ в буфер. В нашем примере будут сгенерированы диспетчеры
Filesystem_interface_dispatch
,Operations_component_dispatch
иServer_entity_dispatch
.Диспетчер класса процессов обрабатывает запрос и вызывает методы, реализуемые этим классом. Если запрос содержит некорректный RIID (например, относящийся к другой службе, которой нет у этого класса процессов) или некорректный MID, диспетчер возвращает
NK_EOK
илиNK_ENOENT
.nk_err_t Server_entity_dispatch(struct Server_entity *self, const struct nk_message *req, const struct nk_arena *req_arena, struct nk_message *res, struct nk_arena *res_arena)В специальных случаях можно использовать диспетчеры интерфейса и компонента. Они принимают дополнительный аргумент – ID реализации интерфейса (
nk_iid_t
). Запрос будет обработан только если переданный аргумент и RIID из запроса совпадают, а MID корректен. В противном случае диспетчеры возвращаютNK_EOK
илиNK_ENOENT
.nk_err_t Operations_component_dispatch(struct Operations_component *self, nk_iid_t iidOffset, const struct nk_message *req, const struct nk_arena *req_arena, struct nk_message *res, struct nk_arena *res_arena) nk_err_t Filesystem_interface_dispatch(struct Filesystem *impl, nk_iid_t iid, const struct nk_message *req, const struct nk_arena *req_arena, struct nk_message *res, struct nk_arena *res_arena)
Работа с ареной IPC-сообщений
Общие сведения об арене
С точки зрения разработчика решения на базе KasperskyOS арена IPC-сообщений представляет собой байтовый буфер в памяти процесса, предназначенный для хранения передаваемых через IPC данных переменного размера, то есть входных, выходных и error-параметров интерфейсных методов (и/или элементов этих параметров), которые имеют IDL-типы переменного размера. Также арена используется при обращении к модулю безопасности Kaspersky Security Module для хранения входных параметров методов интерфейса безопасности (и/или элементов этих параметров), которые имеют IDL-типы переменного размера. (Параметры интерфейсных методов постоянного размера хранятся в фиксированной части IPC-сообщения.) Арены используются как на стороне клиента, так и на стороне сервера. Одна арена предназначена либо для передачи, либо для приема через IPC данных переменного размера, но не для передачи и приема этих данных одновременно, то есть условно арены можно разделить на арены IPC-запросов (содержат входные параметры интерфейсных методов) и арены IPC-ответов (содержат выходные и error-параметры интерфейсных методов).
Через IPC передается только использованная часть арены, то есть занятая данными. (При отсутствии данных арена не передается.) Использованная часть арены включает один или несколько участков. В одном участке арены хранится массив объектов одного типа, например, массив однобайтовых объектов или массив структур. В разных участках арены могут храниться массивы объектов разного типа. Адрес начала арены должен быть выравнен на границу 2^N-байтовой последовательности, где 2^N – значение, которое больше либо равно размеру наибольшего примитивного типа в арене (например, наибольшего поля примитивного типа в структуре). Адрес участка арены также должен быть выравнен на границу 2^N-байтовой последовательности, где 2^N – значение, которое больше либо равно размеру наибольшего примитивного типа в участке арены.
Необходимость наличия нескольких участков в арене возникает, если интерфейсный метод имеет несколько входных, выходных или error-параметров переменного размера, а также если несколько элементов входных, выходных или error-параметров интерфейсного метода имеют переменный размер. Например, если интерфейсный метод имеет входной параметр IDL-типа sequence
и входной параметр IDL-типа bytes
, то в арене IPC-запросов будет как минимум два участка, но могут потребоваться и дополнительные участки, если параметр IDL-типа sequence
состоит из элементов IDL-типа переменного размера (например, string
, то есть элементами последовательности являются строковые буферы). Также, к примеру, если интерфейсный метод имеет один выходной параметр IDL-типа struct
, который содержит два поля типа bytes
и string
, то в арене IPC-ответов будет два участка.
Из-за выравнивания адресов участков арены между этими участками могут появляться неиспользованные промежутки, поэтому размер использованной части арены может превышать размер помещенных в нее данных.
API для работы с ареной
Набор функций и макросов для работы с ареной определен в заголовочном файле sysroot-*-kos/include/nk/arena.h
из состава KasperskyOS SDK. Также функция для копирования строки в арену объявлена в заголовочном файле sysroot-*-kos/include/coresrv/nk/transport-kos.h
из состава KasperskyOS SDK.
Сведения о функциях и макросах, определенных в заголовочном файле sysroot-*-kos/include/nk/arena.h
, приведены в таблице ниже. В этих функциях и макросах арена и участок арены идентифицируются дескриптором арены (тип nk_arena
) и дескриптором участка арены (тип nk_ptr_t
) соответственно. Дескриптор арены представляет собой структуру, содержащую три указателя: на начало арены, на начало неиспользованной части арены и на конец арены. Дескриптор участка арены представляет собой структуру, содержащую смещение участка арены в байтах (относительно начала арены) и размер участка арены в байтах. (Тип дескриптора участка арены определен в заголовочном файле sysroot-*-kos/include/nk/types.h
. из состава KasperskyOS SDK.)
Создание арены
Чтобы передавать через IPC параметры интерфейсных методов переменного размера, нужно создать арены как на стороне клиента, так и на стороне сервера. (При обработке IPC-запросов на стороне сервера с использованием функции NkKosDoDispatch()
, определенной в заголовочном файле sysroot-*-kos/include/coresrv/nk/transport-kos-dispatch.h
из состава KasperskyOS SDK, арены IPC-запросов и IPC-ответов создаются автоматически.)
Чтобы создать арену, нужно создать буфер (в стеке или куче) и инициализировать дескриптор арены.
Адрес буфера должен быть выравнен так, чтобы удовлетворять максимальному размеру примитивного типа, который может быть помещен в этот буфер. Адрес буфера, созданного динамически, обычно имеет достаточное выравнивание для помещения в этот буфер данных примитивного типа максимального размера. Чтобы обеспечить требуемое выравнивание адреса статически созданного буфера, можно использовать спецификатор alignas
.
Чтобы инициализировать дескриптор арены, используя указатель на уже созданный буфер, нужно использовать функцию или макрос API:
- макрос
NK_ARENA_INITIALIZER()
; - функцию
nk_arena_init()
; - функцию
nk_arena_create()
; - макрос
NK_ARENA_FINAL()
; - макрос
nk_arena_init_final()
.
Тип указателя не имеет значения, поскольку в коде функций и макросов API этот указатель приводится к указателю на однобайтовый объект.
Макрос NK_ARENA_INITIALIZER()
и функции nk_arena_init()
и nk_arena_create()
инициализируют дескриптор арены, которая может содержать один и более участков. Макросы NK_ARENA_FINAL()
и nk_arena_init_final()
инициализируют дескриптор арены, которая на протяжении всего своего жизненного цикла содержит только один участок, занимающий всю арену.
Чтобы создать буфер в стеке и инициализировать дескриптор одним шагом, нужно использовать макрос NK_ARENA_AUTO()
. Этот макрос создает арену, которая может содержать один и более участков, а адрес буфера, созданного этим макросом, имеет достаточное выравнивание для помещения в этот буфер данных примитивного типа максимального размера.
Размер арены должен быть достаточен, чтобы с учетом выравнивания адресов участков вместить параметры переменного размера для IPC-запросов или IPC-ответов одного интерфейсного метода или множества интерфейсных методов, соответствующих одному интерфейсу, компоненту или классу процессов. Автоматически генерируемый транспортный код (заголовочные файлы *.idl.h
, *.cdl.h
, *.edl.h
) содержит константы *_arena_size
, значения которых гарантированно соответствуют достаточным размерам арен в байтах.
Заголовочные файлы *.idl.h
, *.cdl.h
, *.edl.h
содержат следующие константы *_arena_size
:
<имя интерфейса>_<имя интерфейсного метода>_req_arena_size
– размер арены IPC-запросов для указанного интерфейсного метода указанного интерфейса;<имя интерфейса>_<имя интерфейсного метода>_res_arena_size
– размер арены IPC-ответов для указанного интерфейсного метода указанного интерфейса;<имя интерфейса>_req_arena_size
– размер арены IPC-запросов для любого интерфейсного метода указанного интерфейса;<имя интерфейса>_res_arena_size
– размер арены IPC-ответов для любого интерфейсного метода указанного интерфейса.
Заголовочные файлы *.cdl.h
, *.edl.h
дополнительно содержат следующие константы *_arena_size
:
<имя компонента>_component_req_arena_size
– размер арены IPC-запросов для любого интерфейсного метода указанного компонента;<имя компонента>_component_res_arena_size
– размер арены IPC-ответов для любого интерфейсного метода указанного компонента.
Заголовочные файлы *.edl.h
дополнительно содержат следующие константы *_arena_size
:
<имя класса процессов>_entity_req_arena_size
– размер арены IPC-запросов для любого интерфейсного метода указанного класса процессов;<имя класса процессов>_entity_res_arena_size
– размер арены IPC-ответов для любого интерфейсного метода указанного класса процессов.
Константы, содержащие размер арены IPC-запросов или IPC-ответов для одного интерфейсного метода (<имя интерфейса>_<имя интерфейсного метода>_req_arena_size
и <имя интерфейса>_<имя интерфейсного метода>_res_arena_size
), предназначены для использования на стороне клиента. Остальные константы могут использоваться как на стороне клиента, так и на стороне сервера.
Примеры создания арены:
Заполнение арены данными перед передачей через IPC
Перед передачей IPC-запроса на стороне клиента или IPC-ответа на стороне сервера арену нужно заполнить данными. Если для создания арены используется макрос NK_ARENA_FINAL()
или nk_arena_init_final()
, то резервировать участок арены не требуется, а нужно только заполнить этот участок данными. Если для создания арены используется макрос NK_ARENA_INITIALIZER()
или NK_ARENA_AUTO()
либо функция nk_arena_init()
или nk_arena_create()
, то в арене необходимо зарезервировать один или несколько участков, чтобы поместить в них данные. Чтобы зарезервировать участок арены, нужно использовать функцию или макрос API:
- функцию
__nk_arena_alloc()
; - макрос
nk_arena_store()
; - функцию
__nk_arena_store()
; - макрос
nk_arena_alloc()
; - функцию
NkKosCopyStringToArena()
.
Дескриптор участка арены, который передается через выходной параметр этих функций и макросов, а также макросов NK_ARENA_FINAL()
и nk_arena_init_final()
, нужно поместить в фиксированную часть либо в арену IPC-сообщения. Если интерфейсный метод имеет параметр переменного размера, то фиксированная часть IPC-сообщений вместо самого параметра содержит дескриптор участка арены, в котором находится этот параметр. Если интерфейсный метод имеет параметр постоянного размера с элементами переменного размера, то фиксированная часть IPC-сообщений вместо самих элементов параметра содержит дескрипторы участков арены, в которых находятся эти элементы параметра. Если интерфейсный метод имеет параметр переменного размера, содержащий элементы переменного размера, то фиксированная часть IPC-сообщений содержит дескриптор участка арены, в котором находятся дескрипторы других участков арены, содержащих эти элементы параметра.
Макрос nk_arena_store()
и функции __nk_arena_store()
и NkKosCopyStringToArena()
не только резервируют участок арены, но и копируют данные в этот участок.
Макрос nk_arena_alloc()
позволяет получить адрес зарезервированного участка арены. Также адрес участка арены можно получить, используя функцию __nk_arena_get()
или макрос nk_arena_get()
, которые дополнительно через выходной параметр передают размер арены.
Зарезервированный участок арены можно уменьшить. Для этого нужно использовать макрос nk_arena_shrink()
или функцию _nk_arena_shrink()
.
Чтобы отменить текущее резервирование участков арены для последующего резервирования новых участков под другие данные (после отправки IPC-сообщения), нужно вызвать функцию nk_arena_reset()
. Если для создания арены используется макрос NK_ARENA_FINAL()
или nk_arena_init_final()
, то отменять резервирование участка не требуется, так как такая арена на протяжении всего своего жизненного цикла содержит один участок, занимающий всю арену.
Примеры заполнения арены данными:
Получение данных из арены после приема через IPC
Перед получением IPC-запроса на стороне сервера или IPC-ответа на стороне клиента для арены, в которую будут помещены полученные через IPC-данные, нужно отменить текущее резервирование участков, вызвав функцию nk_arena_reset()
. Это требуется сделать, даже если для создания арены используется макрос NK_ARENA_FINAL()
или nk_arena_init_final()
. (Макросы NK_ARENA_INITIALIZER()
и NK_ARENA_AUTO()
, а также функции nk_arena_init()
и nk_arena_create()
создают арену без зарезервированных участков. Перед однократном использовании такой арены для сохранения полученных через IPC данных вызывать функцию nk_arena_reset()
не требуется.)
Чтобы получить указатели на участки арены и размеры этих участков, нужно использовать функцию __nk_arena_get()
или макрос nk_arena_get()
, передавая через входной параметр соответствующие дескрипторы участков арены, полученные из фиксированной части и арены IPC-сообщения.
Пример получения данных из арены:
Дополнительные возможности API
Чтобы получить размер арены, нужно вызвать функцию nk_arena_capacity()
.
Чтобы получить размер использованной части арены, нужно вызвать функцию nk_arena_allocated_size()
.
Чтобы проверить, является ли корректным дескриптор участка арены, нужно использовать макрос nk_arena_validate()
или функцию __nk_arena_validate()
.
Сведения о функциях и макросах API
Функции и макросы arena.h
Функция/Макрос |
Сведения о функции/макросе |
---|---|
|
Назначение Инициализирует дескриптор арены. Параметры
Значения макроса Код инициализации дескриптора арены. |
|
Назначение Инициализирует дескриптор арены. Параметры
Возвращаемые значения Нет. |
|
Назначение Создает и инициализирует дескриптор арены. Параметры
Возвращаемые значения Дескриптор арены. |
|
Назначение Создает в стеке буфер, а также создает и инициализирует дескриптор арены. Параметры
Значения макроса Дескриптор арены. |
|
Назначение Инициализирует дескриптор арены, содержащей только один участок. Параметры
Значения макроса Дескриптор арены. |
|
Назначение Отменяет резервирование участков арены. Параметры
Возвращаемые значения Нет. |
|
Назначение Резервирует участок арены заданного размера с заданным выравниванием. Параметры
Возвращаемые значения В случае успеха возвращает |
|
Назначение Позволяет получить размер арены. Параметры
Возвращаемые значения Размер арены в байтах. Дополнительные сведения Если параметр имеет значение |
|
Назначение Проверяет, является ли корректным дескриптор участка арены. Параметры
Значения макроса Имеет значение
Имеет значение
Имеет значение |
|
Назначение Проверяет, является ли корректным дескриптор участка арены. Параметры
Возвращаемые значения Возвращает
Возвращает
Возвращает |
|
Назначение Позволяет получить указатель на участок арены и размер этого участка. Параметры
Возвращаемые значения Указатель на участок арены или |
|
Назначение Позволяет получить размер использованной части арены. Параметры
Возвращаемые значения Размер использованной части арены в байтах. Дополнительные сведения Если параметр имеет значение |
|
Назначение Резервирует участок арены для заданного числа объектов заданного типа и копирует эти объекты в зарезервированный участок. Параметры
Значения макроса В случае успеха имеет значение |
|
Назначение Резервирует участок арены с заданным выравниванием для данных заданного размера и копирует эти данные в зарезервированный участок. Параметры
Возвращаемые значения В случае успеха возвращает |
|
Назначение Инициализирует дескриптор арены, содержащей только один участок. Параметры
Значения макроса Нет. |
|
Назначение Резервирует участок арены для заданного числа объектов заданного типа. Параметры
Значения макроса В случае успеха имеет значение адреса зарезервированного участка арены, иначе имеет значение |
|
Назначение Позволяет получить адрес участка арены и число объектов заданного типа, которое вмещается в этом участке. Параметры
Значения макроса В случае успеха имеет значение адреса участка арены, иначе имеет значение Дополнительные сведения Если размер участка арены не кратен размеру типа объектов, для которых этот участок предназначен, имеет значение |
|
Назначение Уменьшает размер участка арены. Параметры
Значения макроса В случае успеха имеет значение адреса уменьшенного участка арены, иначе имеет значение Дополнительные сведения Если требуемый размер участка арены превышает текущий, имеет значение Если выравнивание участка арены, который нужно уменьшить, не удовлетворяет типу объектов, для которых предназначен уменьшенный участок, имеет значение Если участок арены, который нужно уменьшить, является последним в арене, то освободившаяся часть этого участка становится доступной для резервирования последующих участков. |
|
Назначение Уменьшает размер участка арены. Параметры
Возвращаемые значения В случае успеха возвращает адрес уменьшенного участка арены, иначе возвращает Дополнительные сведения Если требуемый размер участка арены превышает текущий, возвращает Если выравнивание участка арены, который нужно уменьшить, не удовлетворяет заданному выравниванию, возвращает Если участок арены, который нужно уменьшить, является последним в арене, то освободившаяся часть этого участка становится доступной для резервирования последующих участков. |
Транспортный код на языке C++
Перед чтением этого раздела, нужно ознакомиться со сведениями о механизме IPC в KasperskyOS и об IDL-, CDL-, EDL-описаниях.
Чтобы реализовать взаимодействие процессов, необходим транспортный код, отвечающий за формирование, отправку, прием и обработку IPC-сообщений.
У разработчика решения на базе KasperskyOS нет необходимости самостоятельно писать транспортный код. Вместо этого можно использовать специальные инструменты и библиотеки, поставляемые в составе KasperskyOS SDK. Эти библиотеки позволяют разработчику компонента решения сгенерировать транспортный код на основе IDL-, CDL-, EDL-описаний, относящихся к этому компоненту.
Транспортный код
Для генерации транспортного кода на языке C++ в составе KasperskyOS SDK поставляется компилятор nkppmeta
.
Компилятор nkppmeta
позволяет генерировать транспортные C++ прокси-объекты (proxy) и стабы (stub) для использования как клиентом, так и сервером.
Прокси-объекты используются клиентом для упаковки параметров вызываемого метода в IPC-запрос, выполнения IPC-запроса и распаковки IPC-ответа.
Стабы используются сервером для распаковки параметров из IPC-запроса, диспетчеризации вызова на соответствующую реализацию метода и упаковки IPC-ответа.
Генерация транспортного кода для разработки на C++
Для генерации транспортных прокси-объектов и стабов с помощью генератора nkppmeta
при сборке решения используются CMake
-команды add_nk_idl(), add_nk_cdl() и add_nk_edl().
Типы С++ в файле *.idl.cpp.h
Каждый интерфейс определяется в IDL-описании. Это описание задает имя интерфейса, сигнатуры интерфейсных методов и типы данных для параметров интерфейсных методов.
Для генерации транспортного кода при сборке решения используются CMake
-команда add_nk_idl(), которая создает CMake
-цель для генерации заголовочных файлов для заданного IDL-файла при помощи компилятора nkppmeta
.
Генерируемые заголовочные файлы содержат представление на языке C++ для интерфейса и типов данных, описанных в IDL-файле, а также методы, необходимые для использования прокси-объектов и стабов.
Соответствие типов данных, объявленных в IDL-файле, типам C++ приведены в таблице ниже.
Соответствие типов IDL типам C++
Тип IDL |
Тип С++ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Работа с транспортным кодом на C++
Сценарии разработки клиента и сервера, которые обмениваются IPC-сообщениями, представлены в разделах "Статическое создание IPC-каналов при разработке на языке C++" и "Динамическое создание IPC-каналов при разработке на языке C++"
В началоСтатическое создание IPC-каналов при разработке на языке C++
Чтобы реализовать клиентскую программу, вызывающую метод службы, предоставляемой серверной программой, необходимо:
- Подключить сгенерированный заголовочный файл описания (
*.edl.cpp.h
) клиентской программы. - Подключить сгенерированные заголовочные файлы описаний используемых интерфейсов (
*.idl.cpp.h
). - Подключить заголовочные файлы:
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/application.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/api.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/connect_static_channel.h
- Инициализировать объект приложения, вызвав функцию
kosipc::MakeApplicationAutodetect()
. (Также можно использовать функцииkosipc::MakeApplication()
иkosipc::MakeApplicationPureClient()
.) - Получить клиентский IPC-дескриптор канала и идентификатор службы (
riid
) вызвав функциюkosipc::ConnectStaticChannel()
.Функция принимает имя IPC-канала (из файла init.yaml) и квалифицированное имя службы (из CDL- и EDL-описаний компонента решения).
- Инициализировать прокси-объект для используемой службы, вызвав функцию
MakeProxy()
.
Пример
Чтобы реализовать серверную программу, предоставляющую службы другим программам, необходимо:
- Подключить сгенерированный заголовочный файл
*.edl.cpp.h
, содержащий описание компонентной структуры программы, включая все предоставляемые службы. - Подключить заголовочные файлы:
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/event_loop.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/api.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/serve_static_channel.h
- Создать классы, содержащие реализации интерфейсов, которые данная программа и её компоненты предоставляют в виде служб.
- Инициализировать объект приложения, вызвав функцию
kosipc::MakeApplicationAutodetect()
. - Инициализировать структуру
kosipc::components::Root
, которая описывает компонентную структуру программы и описания интерфейсов всех предоставляемых программой служб. - Связать поля структуры
kosipc::components::Root
с объектами, реализующими соответствующие службы.Поля структуры
Root
повторяют иерархию компонентов и служб, заданную совокупностью CDL- и EDL-файлов. - Получить серверный IPC-дескриптор канала, вызвав функцию
ServeStaticChannel()
.Функция принимает имя IPC-канала (из файла init.yaml) и структуру, созданную на шаге 5.
- Создать объект
kosipc::EventLoop
, вызвав функциюMakeEventLoop()
. - Запустить цикл диспетчеризации входящих IPC-сообщений, вызвав метод
Run()
объектаkosipc::EventLoop
.
Пример
Динамическое создание IPC-каналов при разработке на языке C++
Динамическое создание IPC-канала на стороне клиента включает следующие шаги:
- Подключить к клиентской программе сгенерированный заголовочный файл описания (
*.edl.cpp.h
). - Подключить сгенерированные заголовочные файлы описаний используемых интерфейсов (
*.idl.cpp.h
). - Подключить заголовочные файлы:
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/application.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/make_application.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/connect_dynamic_channel.h
- Получить указатели на имя сервера и квалифицированное имя службы с помощью сервера имен – специального сервиса ядра, представленного программой
NameServer
. Для этого необходимо подключиться к серверу имен вызовом функцииNsCreate()
и найти сервер, предоставляющий требуемую службу, используя функциюNsEnumServices()
. Подробнее см. "Динамическое создание IPC-каналов (cm_api.h, ns_api.h)". - Создать объект приложения, вызвав функцию
kosipc::MakeApplicationAutodetect()
. (Также можно использовать функцииkosipc::MakeApplication()
иkosipc::MakeApplicationPureClient()
.) - Создать прокси-объект для требуемой службы, вызвав функцию
MakeProxy()
. В качестве входного параметра функцииMakeProxy()
использовать вызов функцииkosipc::ConnectDynamicChannel()
. В функциюkosipc::ConnectDynamicChannel()
передать указатели на имя сервера и квалифицированное имя службы, полученные на шаге 4.
После успешной инициализации прокси-объекта клиенту доступен вызов методов требуемой службы.
Пример
Динамическое создание IPC-канала на стороне сервера включает следующие шаги:
- Подключить к серверной программе сгенерированный заголовочный файл (
*.edl.cpp.h
), содержащий описание компонентной структуры сервера, включая все предоставляемые службы. - Подключить заголовочные файлы:
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/application.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/event_loop.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/make_application.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/root_component.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/serve_dynamic_channel.h
/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/simple_connection_acceptor.h
- Создать классы, содержащие реализации интерфейсов, которые сервер предоставляет в виде служб. Создать и инициализировать объекты этих классов.
- Создать объект приложения, вызвав функцию
kosipc::MakeApplicationAutodetect()
. - Создать и инициализировать объект класса
kosipc::components::Root
, который описывает структуру компонентов и служб сервера. Эта структура генерируется из описаний в CDL- и EDL-файлах. - Cвязать объект класса
kosipc::components::Root
с объектами классов, созданными на шаге 3. - Создать и инициализировать объект класса
kosipc::EventLoop
, который реализует цикл диспетчеризации входящих IPC-сообщений, вызвав функциюMakeEventLoop()
. В качестве входного параметра функцииMakeEventLoop()
использовать вызов функцииServeDynamicChannel()
. В функциюServeDynamicChannel()
передать объект классаkosipc::components::Root
, созданный на шаге 5. - Запустить цикл диспетчеризации входящих IPC-сообщений в отдельном потоке, вызвав метод
Run()
объектаkosipc::EventLoop
. - Создать и инициализировать объект, который реализует обработчик приема входящих запросов на динамическое создание IPC-канала.
При создании объекта можно использовать класс
kosipc::SimpleConnectionAcceptor
, который является стандартной реализацией интерфейсаkosipc::IConnectionAcceptor
. (Интерфейсkosipc::IConnectionAcceptor
определен в файле/opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/connection_acceptor.h
.) В этом случае обработчик будет реализовать следующую логику: если запрашиваемая клиентом служба опубликована на сервере, то запрос от клиента будет принят, иначе отклонен.Если необходимо создать собственный обработчик, то следует реализовать свою логику обработки запросов в методе
OnConnectionRequest()
, унаследованном от интерфейсаkosipc::IConnectionAcceptor
. Этот метод будет вызываться сервером при получении от клиента запроса на динамическое создание IPC-канала. - Создать объект класса
kosipc::EventLoop
, который реализует цикл приема входящих запросов на динамическое создание IPC-канала, вызвав функциюMakeEventLoop()
. В качестве входного параметра функцииMakeEventLoop()
использовать вызов функцииServeConnectionRequests()
. В функциюServeConnectionRequests()
передать объект, созданный на шаге 9.Цикл приема входящих запросов на динамическое создание IPC-канала может быть только один. Цикл должен работать в одном потоке. Цикл приема входящих запросов на динамическое создание IPC-канала должен быть создан после создания цикла диспетчеризации входящих IPC-сообщений (см. на шаг 7).
- Запустить цикл приема входящих запросов на динамическое соединение в текущем потоке, вызвав метод
Run()
объектаkosipc::EventLoop
.
Пример
При необходимости можно создать и инициализировать несколько объектов класса kosipc::components::Root
, объединенных в список объектов типа ServiceList
с помощью метода AddServices()
. Использование нескольких объектов позволяет, например, разделять компоненты и службы сервера на группы или публиковать службы под разными именами.
Пример