KasperskyOS Community Edition 1.2

Транспортный код на языке 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

Тип С++

SInt8

int8_t

SInt16

int16_t

SInt32

int32_t

SInt64

int64_t

UInt8

uint8_t

UInt16

uint16_t

UInt32

uint32_t

UInt64

uint64_t

Handle

Handle (определен в coresrv/handle/handletype.h)

string

std::string

union

std::variant

struct

struct

array

std::array

sequence

std::vector

bytes

std::vector<std::byte>

Работа с транспортным кодом на C++

Сценарии разработки клиента и сервера, которые обмениваются IPC-сообщениями, представлены в разделах "Статическое создание IPC-каналов при разработке на языке C++" и "Динамическое создание IPC-каналов при разработке на языке C++"

В начало
[Topic cpp_proxy_stubs]

Статическое создание IPC-каналов при разработке на языке C++

Чтобы реализовать клиентскую программу, вызывающую метод службы, предоставляемой серверной программой, необходимо:

  1. Подключить сгенерированный заголовочный файл описания (*.edl.cpp.h) клиентской программы.
  2. Подключить сгенерированные заголовочные файлы описаний используемых интерфейсов (*.idl.cpp.h).
  3. Подключить заголовочные файлы:
    • /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
  4. Инициализировать объект приложения, вызвав функцию kosipc::MakeApplicationAutodetect(). (Также можно использовать функции kosipc::MakeApplication() и kosipc::MakeApplicationPureClient().)
  5. Получить клиентский IPC-дескриптор канала и идентификатор службы (riid) вызвав функцию kosipc::ConnectStaticChannel().

    Функция принимает имя IPC-канала (из файла init.yaml) и квалифицированное имя службы (из CDL- и EDL-описаний компонента решения).

  6. Инициализировать прокси-объект для используемой службы, вызвав функцию MakeProxy().

Пример

// Создание и инициализация объекта приложения kosipc::Application app = kosipc::MakeApplicationAutodetect(); // Создание и инициализация прокси-объекта auto proxy = app.MakeProxy<IDLInterface>( kosipc::ConnectStaticChannel(channelName, endpointName)) // Вызов метода требуемой службы proxy->DoSomeWork();

Чтобы реализовать серверную программу, предоставляющую службы другим программам, необходимо:

  1. Подключить сгенерированный заголовочный файл *.edl.cpp.h, содержащий описание компонентной структуры программы, включая все предоставляемые службы.
  2. Подключить заголовочные файлы:
    • /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
  3. Создать классы, содержащие реализации интерфейсов, которые данная программа и её компоненты предоставляют в виде служб.
  4. Инициализировать объект приложения, вызвав функцию kosipc::MakeApplicationAutodetect().
  5. Инициализировать структуру kosipc::components::Root, которая описывает компонентную структуру программы и описания интерфейсов всех предоставляемых программой служб.
  6. Связать поля структуры kosipc::components::Root с объектами, реализующими соответствующие службы.

    Поля структуры Root повторяют иерархию компонентов и служб, заданную совокупностью CDL- и EDL-файлов.

  7. Получить серверный IPC-дескриптор канала, вызвав функцию ServeStaticChannel().

    Функция принимает имя IPC-канала (из файла init.yaml) и структуру, созданную на шаге 5.

  8. Создать объект kosipc::EventLoop, вызвав функцию MakeEventLoop().
  9. Запустить цикл диспетчеризации входящих IPC-сообщений, вызвав метод Run() объекта kosipc::EventLoop.

Пример

// Создание объектов классов, которые реализуют интерфейсы, // предоставляемые сервером в виде служб MyIDLInterfaceImp_1 impl_1; MyIDLInterfaceImp_2 impl_2; // Создание и инициализация объекта приложения kosipc::Application app = kosipc::MakeApplicationAutodetect(); // Создание и инициализация объекта root, описывающего // компоненты и службы сервера kosipc::components::Root root; // Связывание объекта root с объектами классов, реализующими службы сервера root.component1.endpoint1 = &impl_1; root.component2.endpoint2 = &impl_2; // Создание и инициализация объекта, который реализует // цикл диспетчеризации входящих IPC-сообщений kosipc::EventLoop loop = app.MakeEventLoop(ServeStaticChannel(channelName, root)); // Запуск цикла в текущем потоке loop.Run();
В начало
[Topic static_IPC_kosipc]

Динамическое создание IPC-каналов при разработке на языке C++

Динамическое создание IPC-канала на стороне клиента включает следующие шаги:

  1. Подключить к клиентской программе сгенерированный заголовочный файл описания (*.edl.cpp.h).
  2. Подключить сгенерированные заголовочные файлы описаний используемых интерфейсов (*.idl.cpp.h).
  3. Подключить заголовочные файлы:
    • /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
  4. Получить указатели на имя сервера и квалифицированное имя службы с помощью сервера имен – специального сервиса ядра, представленного программой NameServer. Для этого необходимо подключиться к серверу имен вызовом функции NsCreate() и найти сервер, предоставляющий требуемую службу, используя функцию NsEnumServices(). Подробнее см. "Динамическое создание IPC-каналов (cm_api.h, ns_api.h)".
  5. Создать объект приложения, вызвав функцию kosipc::MakeApplicationAutodetect(). (Также можно использовать функции kosipc::MakeApplication() и kosipc::MakeApplicationPureClient().)
  6. Создать прокси-объект для требуемой службы, вызвав функцию MakeProxy(). В качестве входного параметра функции MakeProxy() использовать вызов функции kosipc::ConnectDynamicChannel(). В функцию kosipc::ConnectDynamicChannel() передать указатели на имя сервера и квалифицированное имя службы, полученные на шаге 4.

После успешной инициализации прокси-объекта клиенту доступен вызов методов требуемой службы.

Пример

NsHandle ns; // Подключение к серверу имен Retcode rc = NsCreate(RTL_NULL, INFINITE_TIMEOUT, &ns); char serverName[kl_core_Types_UCoreStringSize]; char endpointName[kl_core_Types_UCoreStringSize]; // Получение указателей на имя сервера и квалифицированное имя службы rc = NsEnumServices( ns, interfaceName, 0, serverName, kl_core_Types_UCoreStringSize, endpointName, kl_core_Types_UCoreStringSize); // Создание и инициализация объекта приложения kosipc::Application app = kosipc::MakeApplicationAutodetect(); // Создание и инициализация прокси-объекта auto proxy = app.MakeProxy<IDLInterface>( kosipc::ConnectDynamicChannel(serverName, endpointName)) // Вызов метода требуемой службы proxy->DoSomeWork();

Динамическое создание IPC-канала на стороне сервера включает следующие шаги:

  1. Подключить к серверной программе сгенерированный заголовочный файл (*.edl.cpp.h), содержащий описание компонентной структуры сервера, включая все предоставляемые службы.
  2. Подключить заголовочные файлы:
    • /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
  3. Создать классы, содержащие реализации интерфейсов, которые сервер предоставляет в виде служб. Создать и инициализировать объекты этих классов.
  4. Создать объект приложения, вызвав функцию kosipc::MakeApplicationAutodetect().
  5. Создать и инициализировать объект класса kosipc::components::Root, который описывает структуру компонентов и служб сервера. Эта структура генерируется из описаний в CDL- и EDL-файлах.
  6. Cвязать объект класса kosipc::components::Root с объектами классов, созданными на шаге 3.
  7. Создать и инициализировать объект класса kosipc::EventLoop, который реализует цикл диспетчеризации входящих IPC-сообщений, вызвав функцию MakeEventLoop(). В качестве входного параметра функции MakeEventLoop() использовать вызов функции ServeDynamicChannel(). В функцию ServeDynamicChannel() передать объект класса kosipc::components::Root, созданный на шаге 5.
  8. Запустить цикл диспетчеризации входящих IPC-сообщений в отдельном потоке, вызвав метод Run() объекта kosipc::EventLoop.
  9. Создать и инициализировать объект, который реализует обработчик приема входящих запросов на динамическое создание IPC-канала.

    При создании объекта можно использовать класс kosipc::SimpleConnectionAcceptor, который является стандартной реализацией интерфейса kosipc::IConnectionAcceptor. (Интерфейс kosipc::IConnectionAcceptor определен в файле /opt/KasperskyOS-Community-Edition-<version>/sysroot-*-kos/include/kosipc/connection_acceptor.h.) В этом случае обработчик будет реализовать следующую логику: если запрашиваемая клиентом служба опубликована на сервере, то запрос от клиента будет принят, иначе отклонен.

    Если необходимо создать собственный обработчик, то следует реализовать свою логику обработки запросов в методе OnConnectionRequest(), унаследованном от интерфейса kosipc::IConnectionAcceptor. Этот метод будет вызываться сервером при получении от клиента запроса на динамическое создание IPC-канала.

  10. Создать объект класса kosipc::EventLoop, который реализует цикл приема входящих запросов на динамическое создание IPC-канала, вызвав функцию MakeEventLoop(). В качестве входного параметра функции MakeEventLoop() использовать вызов функции ServeConnectionRequests(). В функцию ServeConnectionRequests() передать объект, созданный на шаге 9.

    Цикл приема входящих запросов на динамическое создание IPC-канала может быть только один. Цикл должен работать в одном потоке. Цикл приема входящих запросов на динамическое создание IPC-канала должен быть создан после создания цикла диспетчеризации входящих IPC-сообщений (см. на шаг 7).

  11. Запустить цикл приема входящих запросов на динамическое соединение в текущем потоке, вызвав метод Run() объекта kosipc::EventLoop.

Пример

// Создание объектов классов, которые реализуют интерфейсы, // предоставляемые сервером в виде служб MyIDLInterfaceImp_1 impl_1; MyIDLInterfaceImp_2 impl_2; // Создание и инициализация объекта приложения kosipc::Application app = kosipc::MakeApplicationAutodetect(); // Создание и инициализация объекта root, описывающего // компоненты и службы сервера kosipc::components::Root root; // Связывание объекта root с объектами классов, реализующими службы сервера. // Поля объекта root повторяют описание компонентов и служб, // заданную совокупностью CDL- и EDL-файлов. root.component1.endpoint1 = &impl_1; root.component2.endpoint2 = &impl_2; // Создание и инициализация объекта, который реализует // цикл диспетчеризации входящих IPC-сообщений kosipc::EventLoop loopDynamicChannel = app.MakeEventLoop(ServeDynamicChannel(root)); // Запуск цикла диспетчеризации входящих IPC-сообщений в отдельном потоке std::thread dynChannelThread( [&loopDynamicChannel]() { loopDynamicChannel.Run(); } ); // Создание объекта, реализующего стандартный обработчик приема входящих запросов // на динамическое создание IPC-канала kosipc::SimpleConnectionAcceptor acceptor(root); // Создание объекта, реализующего цикл приема входящих запросов // на динамическое создание IPC-канала kosipc::EventLoop loopDynamicChannel = app.MakeEventLoop(ServeConnectionRequests(&acceptor)); // Запуск цикла приема входящих запросов на динамическое создание IPC-канала в текущем потоке loopConnectionReq.Run();

При необходимости можно создать и инициализировать несколько объектов класса kosipc::components::Root, объединенных в список объектов типа ServiceList с помощью метода AddServices(). Использование нескольких объектов позволяет, например, разделять компоненты и службы сервера на группы или публиковать службы под разными именами.

Пример

// Создание и инициализация объекта group_1 kosipc::components::Root group_1; group_1.component1.endpoint1 = &impl_1; group_1.component2.endpoint2 = &impl_2; // Создание и инициализация объекта group_2 kosipc::components::Root group_2; group_2.component1.endpoint1 = &impl_3; group_2.component2.endpoint2 = &impl_4; // Создание и инициализация объекта group_3 kosipc::components::Root group_3; group_3.component1.endoint1 = &impl_5; // Создание списка объектов ServiceList endpoints; endpoints.AddServices(group_1); endpoints.AddServices(group_2); endpoints.AddServices(group_3.component1.endpoint1, "SomeCustomEndpointName"); // Создание объекта, реализующего обработчик приема входящих запросов // на динамическое создание IPC-канала kosipc::SimpleConnectionAcceptor acceptor(std::move(endpoints)); // Создание объекта, реализующего цикл приема входящих запросов // на динамическое создание IPC-канала kosipc::EventLoop loopDynamicChannel = app.MakeEventLoop(ServeConnectionRequests(&acceptor));
В начало
[Topic dynamic_IPC_kosipc]