Перевод статьи написанной Íñ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