Перевод статьи написанной Íñigo Huguet для сайта Fedora Magazine. Тут будет общее описание и принципы работы D-Bus.
Что такое D-Bus
D-Bus служит различным целям, правленным на облегчение взаимодействия различных процессов в системе. В этой статье будет описан D-Bus и то, как он выполняет эту функцию.
Из определения создателей D-Bus:
D-Bus - это система шины сообщений, простой способ взаимодействия приложений друг с другом. Помимо межпроцессного взаимодействия, D-Bus помогает координировать жизненный цикл процесса; это позволяет просто и надежно писать код приложения или демона с «одним экземпляром», а также запускать приложения и демоны по требованию, когда их услуги необходимы.
D-Bus - это, главным образом, протокол межпроцессного взаимодействия (IPC
). Его главное преимущество заключается в
том, что приложениям не нужно установить индивидуальную связь со всеми остальными процессами, с которыми им необходимо
общаться. Вместо этого D-Bus предлагает виртуальную шину, к которой подключаются все приложения и отправляют сообщения.
Процессы без D-Bus | Processes with D-Bus |
Достижение желаемого приложения
Мы говорим о системе D-Bus, но в системе может быть более одной шины. На самом деле, обычно их как минимум 2:
- Системная шина: приложения, управляющие общесистемными ресурсами, подключенными к ней.
- Одна сеансовая шина на каждого вошедшего в систему пользователя: настольные приложения обычно используют эту шину.
Чтобы отправлять сообщения D-Bus в желаемый пункт назначения по шине, необходим способ идентификации каждого из приложений, подключенных к шине. По этой причине каждое приложение получает по крайней мере одно имя шины, которое другие могут указать в качестве места назначения своих сообщений. Существует два типа имен:
- Уникальные имена соединений: Каждое приложение, подключающееся к шине, автоматически получает его. Это уникальный
идентификатор, который никогда не будет повторно использоваться для другого приложения. Эти имена начинаются с
двоеточия (
:
) и обычно выглядят примерно так::1.94
(символы после двоеточия не имеют особого значения, но уникальны). - Известные имена: Хотя уникальные имена соединений назначаются динамически, приложения должны быть легко
обнаружены и должны иметь фиксированное имя, которое хорошо известно. Они выглядят как перевернутые доменные
имена, похожие на
org.freedesktop.NetworkManager
.
Вы можете увидеть, какие приложения подключены к двум основным шинам, используя команды busctl ниже. Мы обнаружим, что в настоящее время многие основные компоненты системы используют D-Bus. В следующих примерах опустите параметр –acquired, чтобы увидеть уникальные имена соединений.
|
|
По аналогии с IP-сетями уникальные имена соединений подобны динамическим IP-адресам, а общеизвестные имена подобны именам хостов или доменным именам.
Например, NetworkManager - это демон, который управляет сетевой конфигурацией системы. Клиенты, такие как центр
управления GNOME или инструменты командной строки nmcli и nmstate, подключаются к системной шине и отправляют
сообщения D-Bus на широко известное имя org.freedesktop.NetworkManager
, чтобы получить информацию или запросить
изменения.
Примечание: в спецификации D-Bus используется термин “имя шины”, так что это как-то официально. Это может сбивать с толку, поскольку люди склонны думать, что это относится к разным шинам, а не к приложениям, подключенным к одной шине. Во многих статьях и инструментах используется термин “сервис” вместо “имя шины”, потому что он более интуитивен, но в спецификации он имеет другое значение, поэтому не рекомендуется. В этой статье я использую термин “пункт назначения” везде, где это возможно. В любом случае помните, что “имя шины” относится к “имени В шине”, а не к “имени шины”.
Объекты D-Bus
Приложения D-Bus могут предоставлять некоторые из своих ресурсов и действий в том, что мы могли бы назвать API D-Bus.
Открытые ресурсы напоминают основные концепции объектно-ориентированных языков программирования, поэтому их называют объектами D-Bus. В частности, каждое приложение может предоставлять неопределенное количество объектов со свойствами и методами. Отдельные объекты D-Bus идентифицируются по своим путям D-Bus, которые очень похожи на пути Unix (т. е. /path/to/the/object).
Вы можете проверить, какие объекты предоставляет определенное приложение, с помощью команды busctl. Например:
|
|
Приложения могут интуитивно выражать иерархию между объектами благодаря этому идентификатору, напоминающему путь. Легко
понять, что /Background/Color
- это объект, который иерархически принадлежит объекту /Background
. Спецификация не
требует соблюдения этой иерархической структуры, но почти все приложения это делают.
Примечание. Во многих приложениях пути к объектам обычно начинаются с обратного домена автора (т. е. /org/freedesktop/NetworkManager/*). Это позволяет избежать конфликтов имен с другими объектами из разных библиотек или источников. Однако это не является обязательным и не все приложения это делают.
Интерфейсы, методы и свойства D-Bus
Каждый объект D-Bus реализует один или несколько интерфейсов D-Bus, и каждый интерфейс определяет некоторые
свойства и методы. Свойства подобны переменным, а методы - функциям. Имена интерфейсов D-Bus выглядят
так: org.example.AppName.InterfaceName
.
Если вы уже знаете какой-либо язык программирования, использующий концепцию интерфейса, например Java, он будет вам знаком. Если нет, просто помните, что интерфейсы подобны типам объектов, и каждый объект может иметь более одного типа одновременно. Но имейте в виду, что, в отличие от того, что происходит во многих из этих языков, объекты D-Bus никогда не определяют прямых членов, а только интерфейсы.
Пример:
- Приложение сервера печати определяет следующие интерфейсы:
Принтер
: определяет методPrintDocument
и свойствоInkLevel
.Сканер
: определяет методСканировать документ
.
- Объект
/Devices/1
представляет собой обычный принтер, поэтому он реализует только интерфейсПринтер
. - Объект
/Devices/2
представляет принтер со сканером, поэтому реализует оба интерфейса.
Интроспекция
Интроспекция позволяет вам получить список интерфейсов с их методами и свойствами, которые реализует объект D-Bus. Например, команда busctl используется следующим образом:
|
|
Методы и свойства
Теперь давайте посмотрим, как вызывать методы D-Bus и читать свойства из терминала на двух примерах:
|
|
Чтобы читать или записывать свойства, нам фактически нужно вызывать методы стандартного интерфейса
org.freedesktop.DBus.Properties
,
определенного спецификациями D-Bus.
Busctl, однако, делает это скрытно с помощью команды get/set-property, чтобы немного абстрагироваться от этого.
Совет по упражнению: прочитайте свойство с помощью вызова busctl (подсказка: вам нужно указать сигнатуру аргументов,
то есть ss
).
Примечание: при вызове методов D-Bus указывать интерфейс не обязательно, но если два интерфейса определяют одно и то же имя метода, результат не определен, поэтому его следует всегда указывать. По этой причине инструмент busctl делает это обязательным.
Система типов D-Bus
D-Bus использует строгую систему типов, поэтому все свойства, аргументы и возвращаемые значения должны иметь четко определенный тип.
Существует несколько основных типов, таких как BYTE
, BOOLEAN
, INT32
, UINT32
, DOUBLE
, STRING
или OBJECT_PATH
. Эти базовые типы можно сгруппировать в три различных типа контейнеров: STRUCT
, ARRAY
и DICT_ENTRY
(словари). Контейнеры могут быть вложены в другие контейнеры того же или другого типа.
Существует также тип VARIANT
, который допускает динамическую типизацию.
Каждый тип можно идентифицировать по сигнатуре. Сигнатуры базовых типов представляют собой одиночные символы, такие
как i
, u
, s
и т. д. Сигнатуры составных типов представляют собой строки типа (iibs)
, ai
или a{s(ii)}
.
Полное описание всех типов и сигнатур выходит за рамки этой статьи, но в зависимости от используемого вами языка и/или
библиотеки D-Bus вам потребуются по крайней мере некоторые знания о том, как указать тип значений, передаваемых или
получаемых. Дополнительную информацию см.
в спецификации D-Bus.
Собираем все вместе (пример на Python)
Теперь, когда мы знаем основные понятия D-Bus, мы готовы отправлять сообщения D-Bus. Давайте посмотрим полный пример Python.
|
|
Обратите внимание, что нам не нужно было указывать тип аргумента метода. Это связано с тем, что dbus-python делает все
возможное, чтобы преобразовать значения Python в эквивалентные значения D-Bus (т. е. str
в STRING
, list
в ARRAY
, dict
в DICT_ENTRY
и т. д.). Однако, поскольку D-Bus имеет строгую систему типов, вам нужно будет указать
тип в случае неоднозначности. Например, для целочисленных типов вам часто потребуется использовать
dbus.UInt16(значение)
, dbus.Int64(значение)
и т. д.
Дополнительные возможности D-Bus
Еще одной важной и широко используемой функцией D-Bus являются сигнальные сообщения. Приложения могут подписываться на сигналы других приложений, указывая, какие из них им интересны. Приложение-продюсер отправляет сигнальные сообщения при возникновении определенных событий, а подписчик получает их асинхронно. Это позволяет избежать необходимости постоянного опроса.
В D-Bus есть много других полезных функций, таких как активация служб, аутентификация, самоанализ и т. д., которые выходят далеко за рамки этой статьи.
Полезные инструменты
Для командной строки busctl - наиболее интуитивно понятный и полный инструмент с такими замечательными функциями, как мониторинг или захват трафика D-Bus в виде файла pcap. Однако типы значений должны быть указаны как сигнатуры D-Bus, что сложно, если вы их не очень хорошо знаете. В этом случае проще использовать dbus-send из пакета dbus-tools или qdbus из Qt.
Когда вы начинаете экспериментировать с D-Bus, изучение иерархии объектов различных приложений становится намного проще с помощью инструмента с графическим интерфейсом, такого как Qt DBUS Viewer (QDbusViewer).
Рекомендованные статьи
- https://www.freedesktop.org/wiki/Software/dbus/
- https://dbus.freedesktop.org/doc/dbus-tutorial.html
- https://dbus.freedesktop.org/doc/dbus-specification.html
- https://develop.kde.org/docs/features/d-bus/introduction_to_dbus/
- https://en.wikipedia.org/wiki/D-Bus
- https://networkmanager.dev/docs/api/latest/spec.html
- https://dbus.freedesktop.org/doc/dbus-python/index.html