Endlich ist Schluss mit Corba! Gnome verabschiedet sich von dem Standardisierungsmonster und setzt auf das Prozess-Messaging-System D-Bus. Auch Kollege KDE ist dabei, umzustellen.
Keiner mag Programme, die ihren Arbeitstag als Einzelgänger in einer Ecke des Desktops verbringen, wenn sie eigentlich mit anderen zusammenarbeiten sollten. Das mindeste ist noch, dass sie mit anderen über Drag&Drop ihre Daten austauschen. Viele Anwender verlangen von ihren Programmen aber noch umfassendere Kommunikationsfähigkeiten: Natürlich soll die eben angesteckte USB-Festplatte allen laufenden Programmen zur Verfügung stehen. Das VoIP-Softphone soll beim Einstecken eines Headsets einfach von der neuen Hardware Gebrauch machen, und zwar am besten ohne Neustart.
Sprich mit ihr
Damit das alles und noch viel mehr funktioniert, braucht ein Linux-System ein Kommunikationssystem, das Desktop-Anwendungen untereinander und mit den darunter liegenden Schichten bis hin zu Kernel und Hardware verbindet. Für künftige Linux-Generationen soll dies nach dem Willen der Freedesktop-Entwickler D-Bus [1] leisten, das sich zur Hardware-Anbindung auf den Hardware Abstraction Layer (HAL) stützt [2].
D-Bus ist ein System für die Interprozesskommunikation (Inter Process Communication, IPC), es stellt die Infrastruktur bereit, damit sich Anwendungen untereinander und mit Teilen des Betriebssystems unterhalten können. Zwar gibt es bereits die Unix-IPC-Mechanismen, sie beschränken sich aber auf Signale, Pipes und so fort.
Das D-Bus-Konzept klingt bekannt, denn schon seit langem gibt es konkurrierende Ansätze, zum Beispiel Corba, Microsofts DCOM oder noch hundert weitere Projekte. Sowohl KDE als auch Gnome haben zu Beginn mit eigenen Corba-Implementierungen experimentiert. KDE setzt seit längerem auf das Eigengewächs DCOP, bei Gnome wirken die Corba-Altlasten noch in dem Komponentensystem Bonobo nach.
Nun kann man zu Corba stehen, wie man will, die meisten Entwickler, die nur eben mal eine Desktop-Anwendung programmieren möchten, sind damit überfordert. Selbst einfache Gnome-Applets setzen die eingehende Beschäftigung mit der komplizierten Komponenten-Architektur voraus. Entsprechend fristet Bonobo in Gnome schon seit einiger Zeit ein Schattendasein.
D-Bus sollte einfacher werden und beginnt entsprechend klein: Die grundlegende Bibliothek Libdbus stellt nur Funktionen für die Kommunikation zweier Applikationen zur Verfügung. Anwendungsentwickler machen von ihr normalerweise keinen Gebrauch. Für sie gibt es die auf dem Glib-API basierende Libdbus-Glib, die ein objektorientiertes C-API bereitstellt. Auf dieser Ebene erweitern sich die Fähigkeiten von D-Bus hin zu einem Bus-System – wie der Name bereits andeutet.
Der Serverprozess »dbus-daemon« läuft im Hintergrund und wartet auf Verbindungsanfragen von Anwendungen, die sich für bestimmte Ereignistypen registrieren, zum Beispiel das Ein- und Ausstecken von Hardware. Tritt das Ereignis ein, schickt der D-Bus-Daemon eine Nachricht über den Bus, und die Anwendungen reagieren entsprechend.
Systemweit oder pro Session
Prinzipiell gibt es auf Systemen, die D-Bus verwenden, zwei von je einem Serverprozess realisierte Busse: den System-Bus und den Session-Bus. Der System-Bus startet beim Booten und ist auch aktiv, wenn kein Benutzer eingeloggt ist. Erst beim grafischen Login einer Desktop-Session startet auch ein Serverprozess für den Session-Bus. Das Binary »dbus-daemon« kennt für die beiden Modi die Kommandozeilenparameter »–system« respektive »–session«.
Zum Starten des Daemon enthält das D-Bus-Paket das Programm »dbus-launch«, das unter anderem die nötigen Umgebungsvariablen setzt. Die meisten Distributionen starten damit den D-Bus-Daemon im Session-Modus zusammen mit der X-Session.
Abbildung 1 zeigt die Stellung beider Busse in der Kommunikation zwischen den Betriebssystem-Komponenten. Der Session-Bus ermöglicht Anwendungen, die zu einer Desktop-Sitzung gehören, miteinander zu sprechen. Das können durchaus auch Dienste sein, die zum Beispiel die Desktop-Umgebung zur Verfügung stellt. Der System-Bus ist in erster Linie dafür gedacht, dass Desktop-Programme mit den darunter liegenden Schichten kommunizieren. So kann eine Anwendung sich über den System-Bus für eine Hardwareklasse registrieren, zum Beispiel digitale Kameras.

Abbildung 1: D-Bus und HAL im Zusammenspiel mit den restlichen Komponenten eines Linux-Systems. Über den Session-Bus kommunizieren Anwendungen untereinander. Den System-Bus nutzen Programme, um sich via HAL über Hardware zu informieren.
HAL hilft
Die Hardwareverwaltung übernimmt D-Bus nicht selbst, sondern greift dafür auf die Hardware-Abstraktionsschicht (HAL) mit dem Daemon »hald« zurück. Obwohl es sich dabei um eine von D-Bus getrennte Komponente handelt, arbeiten beide aufs Engste zusammen.
Neben dem Kernel selbst kümmert sich in modernen Distributionen vor allem das Udev-Subsystem im Userspace um die Verwaltung der Hardware. Ab Version 0.59 braucht Udev das gerade erst etablierte Hotplug-System schon nicht mehr, das auf das Ein- und Ausstecken von Geräten mit der Ausführung von »/sbin/hotplug« reagierte.
Zusätzlich zu den Kernel- und Udev-Informationen führt HAL nun zusätzliche Details über Geräte, die als FDI-Dateien (Device Information Files) in einem XML-Format abgelegt sind. Listing 1 zeigt einen Ausschnitt aus einer FDI-Datei für eine digitale Kamera.
Ein gutes Beispiel für das Zusammenspiel der Komponenten ist der Gnome Network Manager [3], der das Netzwerksubsystem vom HAL-Daemon überwachen lässt. Der benachrichtigt den Network Manager bei Veränderungen über den D-Bus, zum Beispiel beim Ein- oder Ausstecken von Wireless-USB-Sticks. Neben echten Geräten kann HAL auch mit Dateisystemen umgehen und deren Typ feststellen, sogar mit LUKS-verschlüsselten Partitionen [4].
Unter Gnome übernimmt HAL mittlerweile einen großen Teil der Hardwareverwaltung, vor allem von Hot-Plug-Geräten. Dazu läuft im Hintergrund der Prozess »gnome-volume-manager«, den der Gnome-Anwender mit dem Frontend »gnome-volume-properties« konfiguriert (Abbildung 2). Auch für HAL selbst gibt es ein Frontend, das alle angeschlossenen Geräte in einem Baum anzeigt (Abbildung 3). Der »hal-device-manager« verbirgt sich beispielsweise bei Fedora im Paket »hal-gnome«.
|
Listing 1: |
|---|
01 <deviceinfo version="0.2"> 02 <device> 03 <match key="info.bus" string="usb"> 04 <match key="usb.interface.class" int="0x06"> 05 <match key="usb.interface.subclass" int="0x01"> 06 <match key="usb.interface.protocol" int="0x01"> 07 <merge key="info.category" type="string">camera</merge> 08 <append key="info.capabilities" type="strlist">camera</append> 09 <merge key="camera.access_method" type="string">ptp</merge> 10 </match> 11 </match> 12 </match> 13 </match> 14 </device> 15 </deviceinfo> |

Abbildung 2: Arbeitet mit HAL und D-Bus: Der Gnome Volume Manager, den »gnome-volume-properties« einstellt. Hier die neueste Version in Gnome 2.14.

Abbildung 3: Der HAL Device Manager zeigt die Hardware in Baumform an. Die Zusatzinformationen zu jedem Gerät gehen ziemlich ins Detail. Hier die einer an USB angeschlossenen digitalen Kamera.
D-Bus selbst nutzen
Das D-Bus-Protokoll, dessen Spezifikation sich auf [1] findet, legt vier Typen von Nachrichten fest, die sich Anwendungen über die Busse schicken. So kann ein Programm die Methoden aufrufen, die ein anderes anbietet. Entsprechend enthält der zweite Typ die Antwort auf eine solche Anfrage. Im Fehlerfall kommt die dritte Variante zum Einsatz: eine Fehlermeldung der Anwendung, die als Server auftritt. Der vierte Typ von Nachrichten sind Signale, die Anwendungen über den Bus schicken und keine Antwort vorsehen. Programmierer können D-Bus-Methoden synchron oder asynchron aufrufen.
Objekte und Interfaces
Um Sender und Empfänger von D-Bus-Nachrichten zu identifizieren, verwendet D-Bus ein mehrstufiges Namensschema. Jede Anwendung stellt ein oder mehrere Objekte zur Verfügung, die mit Pfaden adressiert werden, die aus umgedrehten Domain-Namen und dem eigentlichen Objektnamen bestehen, zum Beispiel »/org/freedesktop/DBus«.
Objekte bieten Services an, die ähnlich aussehen, aber durch Punkte getrennt sind: »org.freedesktop.DBus«. Die Methoden und Signale eines Objekts fasst das so genannte Interface zusammen, das ebenfalls die Punktnotation nutzt – ähnlich wie ein Java-Interface.
Natürlich darf nicht jeder Benutzer mit D-Bus machen, was er will. Sicherheit ist nach Auskunft der Entwickler stets mitbedacht. Im einfachen Fall läuft die Zugriffsbeschränkung über die User-ID. Gehören der Bus-Daemon und die Clients demselben Benutzer, dürfen sie alles. Für feiner abgestufte Sicherheitsregeln kennt D-Bus Security-Policies, die für jeden Benutzer festlegen, was er darf oder nicht (Listing 2). Wohl weil es vor allem von Red Hat entwickelt wird, lässt sich D-Bus auch mit SE Linux kombinieren.
|
Listing 2: |
|---|
01 <busconfig> 02 <!-- Only root can send this message --> 03 <policy user="root"> 04 <allow send_interface="com.redhat.PrinterSpooler"/> 05 </policy> 06 07 <!-- Allow any connection to receive the message --> 08 <policy context="default"> 09 <allow receive_interface="com.redhat.PrinterSpooler"/> 10 </policy> 11 </busconfig> |
Anwendungen für D-Bus
Neben den erwähnten Gnome-Systemprogrammen machen schon einige Anwendungen von D-Bus Gebrauch. Eine recht aktuelle Liste führt [5]. Wenn auch hier einmal mehr die Warnung vor Änderungen erklingt, lassen sich beispielsweise die Audioplayer BMPx und Banshee in der Entwicklerversion über D-Bus fernsteuern. Für netzwerkfähige Programme ist besonders interessant, dass das Zero-Conf-Paket Avahi in aktuellen Versionen D-Bus-fähig ist. So kann sich eine Anwendung über neu auftauchende Server informieren lassen.
Der D-Bus-Programmierer hat es gleichzeitig leicht und schwer. Leicht hat er es, weil er auf eine Vielzahl von Sprachbindungen zurückgreifen kann [6], vom Glib-C-API über Python bis zu Ruby, C# und Java. Leider hat sich aber das API in der Vergangenheit häufig geändert, sodass viele im Internet auffindbare Beispiele auf aktuellen D-Bus-Versionen gar nicht mehr laufen.
Generell ist sehr wenig Dokumentation über die Interaktion mit dem D-Bus verfügbar. Am aufschlussreichsten ist wahrscheinlich der Quellcode der funktionierenden Programme, zum Beispiel der des Gnome Network Manager, der in Python geschrieben ist.
Programmieren mit Loop
Prinzipiell ist die Vorgehensweise in allen Programmiersprachen gleich: eine Verbindung zum Bus der Wahl herstellen, je eine Referenz auf das Remote-Objekt und das Interface holen und dann Anfragen stellen oder Signal-Handler registrieren. Wenn ein Programm zum Beispiel einen Signal-Handler registriert, läuft es normalerweise in einer Hauptschleife, dem Mainloop, der regelmäßig auf eingehende Signale prüft.
Mit D-Bus sollte der Programmierer dazu das Mainloop-Objekt der Glib benutzen, egal ob in C oder einer Skriptsprache. Mit Version 0.60 führt D-Bus Funktionen ein, die ohne Mainloop das Empfangen von Nachrichten ermöglicht, zum Beispiel »dbus_connect_read_write()«.
Ein kleines Beispiel in Python soll demonstrieren, wie man den D-Bus in eigenen Programmen nutzt. Gegenüber Glib-C-Programmen gestaltet sich der Umgang mit den objektorientierten Konstrukten wie Objekten und Interfaces in Python wesentlich einfacher. Ganz simpel ist zum Beispiel das Importieren des D-Bus-Moduls in Zeile 1 von Listing 3. Ab D-Bus-Versionen 0.41 gilt: Wer den Glib-Mainloop nutzen will, findet die Glib-Objekte und -methoden fortan im Modul »dbus.glib«.
Ebenfalls erst im Lauf der Zeit hat sich der Zugriff über Interfaces entwickelt. In vielen Codebeispielen im Internet findet sich die veraltete Variante über die Methode »get_service()«. Nun ist ein Proxy-Objekt nötig, das den Zugriff über ein Interface kapselt. Dazu dient die Methode »get_object()« des Objekts »bus«, das die Argumente »org.freedesktop.Hal« und »/org/freedesktop/Hal/Manager« erhält (Listing 3, Zeilen 4 und 5). Mit Hilfe dieses Proxy-Objekts legt die statische Methode »dbus.Interface« ein Interface an, das der Programmierer im Folgenden für seine Aufrufe verwendet.
|
Listing 3: |
|---|
01 import dbus
02
03 bus = dbus.SystemBus()
04 proxy_obj = bus.get_object ('org.freedesktop.Hal',
05 '/org/freedesktop/Hal/Manager')
06 hal_manager = dbus.Interface (proxy_obj, 'org.freedesktop.Hal.Manager')
07
08 dev_list = hal_manager.GetAllDevices()
09
10 for dev in dev_list:
11 print dev,"n"
|
Einige Features des D-Bus-API setzen spezielle Ausdrucksmittel der Programmiersprachen ein. So verwendet das Python-API die in Python 2.4 hinzugekommenen Decorators, um Signale und Service-Methoden zu markieren (Listing 4, Zeile 9).
|
Listing 4: |
|---|
01 import gobject
02 import dbus
03 import dbus.service
04 import dbus.glib
05
06 class HelloWorldObject(dbus.service.Object):
07 def __init__(self, bus_name, object_path):
08 dbus.service.Object.__init__(self, bus_name, object_path)
09 @dbus.service.method('org.firstfloor.HelloWorldIFace')
10 def hello(self):
11 return "Ping"
12
13 session_bus = dbus.SessionBus()
14 bus_name = dbus.service.BusName('org.firstfloor.HelloWorld', bus=session_bus)
15 object = HelloWorldObject(bus_name, '/org/firstfloor/HelloWorldObject')
16 mainloop = gobject.MainLoop()
17 mainloop.run()
|
Automatisch starten
Stillschweigend wurde bisher vorausgesetzt, dass eine Anwendung von sich aus den D-Bus kontaktiert und dabei als Client fungiert. Wenn ein Programm als D-Bus-Server arbeiten möchte, muss es entweder schon beim Booten starten oder der D-Bus-Server ruft es erst bei Bedarf ins Leben. Damit er das kann, muss er den Servicenamen und das auszuführende Binary kennen, das er in einem Konfigurations-File mit der Endung ».service« findet. Das folgende Listing zeigt ein Beispiel für den Audioplayer BMPx:
[D-BUS Service] Name=org.beepmediaplayer.bmp Exec=/usr/libexec/beep-media-player-2-bin
Wie die Beispiele zeigen, ist es eigentlich nicht schwer, die eigene Anwendung über D-Bus mit der Welt sprechen zu lassen. Nur die richtigen Funktionen im API-Wust finden, das kann gelegentlich noch Probleme bereiten.
Die Zukunft
D-Bus hat sich erstaunlich schnell auf vielen Distributionen verbreitet. Und das, obwohl es sich immer noch in der Entwicklung befindet und sich die Schnittstellen von einer Release zur nächsten ständig ändern.
Wer jetzt mit D-Bus Erfahrungen sammeln möchte, sollte sich also dessen bewusst sein, dass sich das D-Bus-API jederzeit noch ändern kann. Zum Abschluss sei ergänzend zu den erwähnten Dokumenten noch auf die Tutorials von Raphaël Slinckx hingewiesen, die zumindest bei Redaktionsschluss noch aktuell waren [7].
Die Ironie der Entwicklungsgeschichte: Auch die Väter von D-Bus finden es mittlerweile wichtig, dass ihr System netzwerkweit funktioniert. Es also wohl nur eine Frage der Zeit, bis das System ähnlich monströse Züge annimmt wie das heute verschmähte Corba. Aber dann findet sich sicherlich eine neue Gruppe von Entwicklern bei Novell oder Red Hat, die wieder von vorne anfängt.
Zurzeit setzt Gnome den D-Bus am intensivsten ein, zum Beispiel bei der Verwaltung von Hotplug-Geräten wie beispielsweise Kameras, Harddisks oder Scannern. Doch auch die KDE-Entwickler stellen ihre Anwendungen langsam auf D-Bus um, selbst Qt kann inzwischen direkt mit D-Bus umgehen. Deshalb lohnt sich eine Testfahrt für Anwendungsentwickler auf jeden Fall.
|
Infos |
|---|
|
[1] D-Bus: [http://www.freedesktop.org/wiki/Software/dbus] [2] HAL: [http://www.freedesktop.org/wiki/Software/hal] [3] Gnome Network Manager: [http://www.gnome.org/projects/NetworkManager] [4] LUKS für HAL: [http://www.redhat.com/magazine/012oct05/features/hal] [5] D-Bus-Software: [http://www.freedesktop.org/wiki/Software_2fDbusProjects] [6] Sprachbindungen für D-Bus: [http://www.freedesktop.org/wiki/Software_2fDBusBindings] [7] Blog von Raphaël Slinckx: [http://raphael.slinkcx.net] |






Vielen Dank für diesen informativen Beitrag. Es ist die perfekte Mischung zwischen der Verwendung von Fachbegriffen und verständlichen Erklärungen.