Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Разбираемся с D-BUS

Оригинал: Get on the D-BUS
Автор: Robert Love
Дата публикации: 5 Января 2005 г.
Перевод: А.Панин
Дата публикации перевода: 18 октября 2012 г.

Прикладные программы, ядро операционной системы и даже ваш мобильный телефон могут информировать вас о наступивших событиях, что позволит вам использовать ваш компьютер с максимальным удобством. В этой статье рассказывается о том, как работает D-BUS и как приложения могут использовать эту систему.

D-BUS представляет собой систему межпроцессного взаимодействия (IPC), предоставляющую простой и мощный механизм, с помощью которого приложения могут обмениваться друг с другом информацией и инициировать запросы служб. Система D-BUS была спроектирована с нуля в условиях необходимости удовлетворения запросов современных Linux-систем. Главной задачей D-BUS является замена таких систем удаленных вызовов объектов, как CORBA и DCOP, используемых в GNOME и KDE соответственно. В идеальном случае, система D-BUS может стать унифицированной и гибкой системой межпроцессного взаимодействия для обоих вышеупомянутых окружений рабочего стола, удовлетворяющей их нуждам и используемой для реализации новых возможностей.

D-BUS, как полнофункциональная система межпроцессного взаимодействия и удаленного вызова объектов, имеет ряд типичных применений. Во-первых, D-BUS может использоваться для классического межпроцессного взаимодействия, позволяющего одному процессу передать данные другому - как улучшенная реализация сокетов домена UNIX. Во-вторых, D-BUS может упростить отправку сообщений о событиях или сигналов в рамках системы, позволяя различным компонентам системы реагировать на события и в конечном итоге, улучшая интеграцию этих компонентов системы. Например, служба Bluetooth может отправить сигнал о входящем звонке, позволяя музыкальному проигрывателю приглушить звук до окончания разговора. Наконец, D-BUS позволяет производить запросы к службам и вызовы методов из других объектов - как CORBA, только без лишних сложностей.

Почему система D-BUS уникальна

Система D-BUS отлична от других систем межпроцессного взаимодействия по ряду причин. Во-первых, основной единицей межпроцессного взаимодействия в D-BUS является сообщение, а не поток данных. Таким образом, D-BUS разделяет данные взаимодействующих приложений на отдельные сообщения, состоящие из заголовков (метаданных) и полезной нагрузки (данных). Сообщений находятся в бинарном, типизированном, полностью выровненном и простом формате. Эта часть протокола унаследована из сетевых протоколов обмена данными. Этот подход резко отличается от подхода, используемого в других системах межпроцессного взаимодействия, где в подавляющем большинстве используются различные байтовые потоки, а не отдельные сообщения.

Во-вторых, в основе D-BUS лежит понятие шины. Простейшей формой взаимодействия процессов является передача данных от процесса к процессу. Тем не менее, D-BUS предоставляет системную службу, известную как служба системной шины сообщений, которая перенаправляет сообщения между процессами, работающими с конкретной шиной. Таким образом реализуется топология шины, позволяющая процессам передавать сообщения одному или нескольким приложениям одновременно. Приложения могут отправлять или ожидать приема различных событий на шине.

Последней уникальной возможностью является создание не одной, а сразу двух таких шин: шины системы и шины сессии. Шина системы является глобальной шиной, работающей на уровне системы. Все пользователи системы, имеющие соответствующие права, могут использовать эту шину, что вводит концепцию общесистемных событий. Шина сессии создается во время входа пользователя в систему и работает на уровне пользователя или сессии. Эта шина используется исключительно определенным пользователем в рамках определенной сессии как система межпроцессного взаимодействия и удаленного вызова объектов для пользовательских приложений.

Концепции D-BUS

Сообщения отправляются объектам. Адресация объектов производится при помощи путей, таких как /org/cups/printers/queue. Процессы, работающие с шинами, ассоциируются с объектами и реализуют интерфейсы этих объектов.

D-BUS поддерживает множество типов сообщений, таких как: сигналы, вызовы методов, возврат результатов вызовов методов и сообщения об ошибках. Под сигналами понимаются уведомления о наступлении определенных событий. Они являются простыми, асинхронными односторонними сообщениями. Сообщения с вызовами методов позволяют приложению отправить запрос на вызов метода удаленного объекта. Сообщения, возвращающие результат вызова методов, предоставляют значение, которое было возвращено в результате вызова метода. Сообщения об ошибках позволяют обрабатывать исключения в результате вызова методов.

В системе D-BUS используется полная типизация и возможно безопасное использование типов. И заголовок сообщения и полезные данные полностью типизированы. Поддерживаемые типы данных: байты (byte), булевые значения (Boolean), 32-битные целочисленные значения (32-bit integer), 32-битные беззнаковые целочисленные значения (32-bit unsigned integer), 64-битные целочисленные значения (64-bit integer), 64-битные беззнаковые целочисленные значения (64-bit unsigned integer), числа с плавающей точкой двойной точности (double-precision floating point) и строки(string). Специальный тип массива (array) позволяет группировать эти значения. Тип словаря (DICT) позволяет создавать пары ключ/значение.

Система D-BUS безопасна. Она реализует простой протокол, основанный на профилях SASL для аутентификации соединений приложений один-на-один. На уровне шин чтение и запись сообщений со стороны определенного интерфейса контролируется системой безопасности. Администратор может контролировать доступ любого интерфейса к шине. Служба D-BUS изначально проектировалась с учетом системы безопасности.

Почему следует использовать D-BUS

Все вышесказанное здорово звучит, но в чем же преимущества? Во-первых, концепция системной шины является нововведением. Одна шина, доступная всем компонентам системы, позволяет распространять сообщения начиная от событий ядра (смотрите раздел "Уровень событий ядра") и заканчивая высокоуровневыми приложениями в рамках системы. Linux со своими хорошо спроектированными интерфейсами и четко разделенными уровнями систем, не имел хорошей системы интеграции. Шина системы D-BUS улучшает интеграцию системных компонентов без нарушения хорошо зарекомендовавших себя правил разработки. Теперь такие события, как заполнение жесткого диска или опустошение очереди печати или даже разрядка батареи ноутбука могут сопровождаться всплывающей подсказкой в системном трее и событием, доступным заинтересованным в нем приложениям, позволяющим системе ответить и отреагировать на него соответствующим образом. Сигналы отправляются асинхронно без необходимости постоянной проверки доступности новых событий.

Уровень событий ядра

Под уровнем событий ядра понимается механизм обмена сообщениями между пространствами ядра и пользователя, реализуемый при помощи высокоскоростного netlink-сокета. Этот механизм связан с системой D-BUS и позволяет ядру генерировать сигналы D-BUS!

Уровень событий ядра связан с файловой системой sysfs, являющейся отображением внутренних объектов ядра (kobjects) и расположенной в /sysfs в современных дистрибутивах Linux. Каждая директория sysfs связана с объектом ядра (kobject), являющимся структурой ядра, предназначенной для внутреннего представления объектов; sysfs является иерархией объектов, экспортируемой из ядра в виде файловой системы.

Каждое событие уровня событий ядра моделируется таким образом, что в качестве инициатора приводится путь в файловой системе sysfs. Таким образом, события выглядят как инициированные объектами ядра. Пути из файловой системы sysfs легко приводятся к путям объектов D-BUS, связывая уровень событий ядра и систему D-BUS естественным образом. Уровень событий ядра был впервые представлен в ядре версии 2.6.10-rc1.

Во-вторых, шина сессий предоставляет механизмы межпроцессного взаимодействия и удаленного вызова процедур, предоставляя в перспективе унифицированную систему для GNOME и KDE. Целью D-BUS является создание системы с функциями CORBA, лучшей чем CORBA и системы с функциями DCOP, лучшей чем DCOP, удовлетворяя требованиям обоих проектов и в то же время, предоставляя дополнительные возможности.

К тому же, D-BUS обладает этими возможностями, оставаясь простой и эффективной системой.

Добавление поддержки D-BUS в ваши приложения

Основа системы D-BUS разработана на языке программирования C и ее API является обширным и довольно низкоуровневым. Существуют высокоуровневые API, используемые поверх этого API для различных языков программирования и компонентов, включая Glib, Python, Qt и Mono. Наряду с предоставлением API для различных языков программирования, высокоуровневые API предоставляют и поддержку функций, специфических для отдельных системных компонентов. Например, API для Glib представляют соединения D-BUS как объекты GObject и позволяют отправку сообщений для интеграции в цикл приема событий (Glib mainloop). Предпочтительным методом использования D-BUS в программе является использование высокоуровневых API для выбранного языка программирования и программного компонента для достижения удобства использования и повышения функциональности.

Давайте рассмотрим простейшие примеры использования D-BUS в ваших приложениях. Сначала рассмотрим API для языка C, затем перейдем к рассмотрению кода с использованием интерфейса Glib.

Использование API D-BUS для языка C

Использование системы D-BUS начинается с подключения заголовочного файла:
#include <dbus/dbus.h>
Первое, что вам наверняка захочется сделать - это подключиться к существующей шине. Повторим сказанное в предыдущих разделах: D-BUS предоставляет две шины, шину сессии и шину системы. Давайте подключимся к системной шине:
DBusError error;
DBusConnection *conn;

dbus_error_init (&error);
conn = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
if (!conn) {
    fprintf (stderr, "%s: %s\n",
             err.name, err.message);
    return 1;
}
Соединение с системной шиной является неплохим первым шагом, но нам хочется получить возможность отправлять сообщения с известного адреса. Давайте назначим имя службы:
dbus_bus_acquire_service (conn, "org.pirate.parrot",
                          0, &err);
if (dbus_error_is_set (&err)) {
    fprintf (stderr, "%s: %s\n",
             err.name, err.message);
    dbus_connection_disconnect (conn);
    return;
}
Теперь наша служба с именем org.pirate.parrot работает с системной шиной и мы можем отправлять сообщения с этого адреса. Давайте отправим сигнал:
DBusMessage *msg;
DBusMessageIter iter;

/* создание нового сообщения, являющегося сигналом */
msg = dbus_message_new_signal(
          "org/pirate/parrot/attr",
          "org.pirate.parrot.attr", "Feathers");

/* добавление полезной нагрузки в сообщение */
dbus_message_iter_init (msg, &iter);
dbus_message_iter_append_string (&iter, "Shiny");
dbus_message_iter_append_string (&iter,
                                 "Well Groomed");

/* отправка сообщения */
if (!dbus_connection_send (conn, msg, NULL))
        fprintf (stderr, "error sending message\n");

/* уменьшение количества ссылок на сообщение */
dbus_message_unref (msg);

/* отправка сообщения с опустошением буфера */
dbus_connection_flush (conn);

Этот код отправляет сигнал "Feathers" с адреса org.pirate.parrot.attr с полезной нагрузкой в виде двух полей, каждое из которых является строкой: "Shiny" и "Well Groomed". Любое приложение, работающее с системной шиной сообщений, с достаточными правами может выбрать эту службу и принять сигнал.

Отсоединение от системной шины сообщений осуществляется с помощью единственной функции:
if (conn)
        dbus_connection_disconnect (conn);

Интерфейс Glib

Glib (произносится как гии-либ) является базовой библиотекой GNOME. Gtk+ (API для создания графических интерфейсов приложений GNOME) и остальные компоненты GNOME используют функции Glib. Glib содержит ряд функций, повышающих удобство использования языка C, функции для поддержки кроссплатформенности, ряд функций для работы со строками и полную реализацию системы работы с объектами и типами - все это на чистом C.

Библиотека Glib представляет систему работы с объектами и цикл ожидания событий (mainloop), позволяющие работать с объектами и событиями даже на C. В API Glib предусмотрено использование этих возможностей. Для начала нам нужно подключить необходимые заголовочные файлы:
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
Соединение с определенной шиной сообщений в Glib осуществляется довольно просто:
DBusGConnection *conn;
GError *err = NULL;

conn = dbus_g_bus_get (DBUS_BUS_SESSION, &err);
if (!conn) {
        g_printerr ("Error: %s\n", error->message);
        g_error_free (error);
}

В этом примере мы присоединились к шине пользователя. Этот вызов связывает соединение с циклом событий Glib, позволяя совершать операции ввода/вывода вместе с работой с сообщениями D-BUS.

Glib использует понятие прокси-объектов для представления различных реализаций соединений D-BUS, связанных с определенными службами. Прокси-объект создается при помощи единственного вызова:
DBusGProxy *proxy;

proxy = dbus_g_proxy_new_for_service (conn,
                                  "org.fruit.apple",
                                  "org/fruit/apple",
                                  "org.fruit.apple");

В этот раз вместо отправки сигнала, давайте выполним вызов удаленного метода. Эта операция осуществляется при помощи двух функций. Первая функция вызывает удаленный метод, вторая - получает возвращенное значение.

Для начала, давайте вызовем удаленный метод "Peel":
DBusGPendingCall *call;

call = dbus_g_proxy_begin_call (proxy,
                    "Peel", DBUS_TYPE_INVALID);
Теперь давайте проверим наличие ошибок и, в случае их отсутствия, получим возвращенное в результате вызова метода значение:
GError *err = NULL;
int ret;

if (!dbus_g_proxy_end_call (proxy, call,
                          &err, DBUS_TYPE_INT32,
                          &ret, DBUS_TYPE_INVALID)) {
        g_printerr ("Error: %s\n", err->message);
        g_error_free (err);
}

Функция Peel принимает дин параметр в виде целого числа. Если метод возвратит значение, отличное от нуля, он корректно завершился и в переменной ret содержится значение, возвращенное этой функцией. Типы данных, которые принимает определенный метод, описываются при реализации метода. Например, мы не передавали тип DBUS_TYPE_STRING вместо типа DBUS_TYPE_INT32.

Главным преимуществом использования высокоуровневых API является интеграция в цикл приема событий, позволяющая разработчикам обрабатывать различные сообщения D-BUS вместе с операциями ввода-вывода и событиями пользовательского интерфейса. Заголовочный файл <dbus/dbus-glib.h> описывает ряд функций для интеграции D-BUS в цикл приема событий Glib.

Заключение

D-BUS является мощной и простой системой межпроцессного взаимодействия, которая с успехом улучшит интеграцию и расширит функциональность Linux-систем. Пользователи рано или поздно столкнутся со все большим количеством приложений, использующих D-BUS. Имея на руках эту статью, есть все основания рассматривать D-BUS не как ужасную зависимость, а как блестящую особенность системы. На интернет-ресурсах можно найти ряд примеров программ, использующих D-BUS. Разработчики со временем будут вынуждены добавлять поддержку D-BUS в свои приложения. Также существует ряд сайтов с описанием возможностей и примерами использования D-BUS. Конечно же, лучшим справочным материалом является исходный код, и, к счастью, его сейчас очень много.