Development/Tutorials/D-Bus/Introduction (de)

From KDE TechBase
Revision as of 18:53, 29 June 2011 by Neverendingo (talk | contribs) (Text replace - "<code>" to "<syntaxhighlight lang="bash">")


Development/Tutorials


Übersicht

D-Bus ist ein freier, Quelloffener Software - Inter-Process-Communication (IPC)- Mechanismus, der zur Zeit (2010) weitverbreitet in Open-Source-Desktop Software ist. Er ist Teil des freedesktop.org Projekts und wird von den meisten aktuellen Anwendungen eingesetzt. Beispiele hierfür sind die Hotplug-Notifications unter Linux, Desktop-Such-Anfragen mit Strigi und in primärer Bedeutung die IPC in KDE4. Dieses Tutorial stellt die D-Bus Terminologie vor und wirft einen Blick aus Sicht des Anwendungs-Entwicklers auf die zentralen Konzepte des D-Bus.


Was ist IPC?

Der Begriff "IPC" beschreibt eine beliebige Anzahl von Methoden, um Informationen von einem Prozess zu einem anderen zu übermitteln. Die übertragenen Informationen können in Form von Applikationsdaten, Methodenaufrufen oder spontane Signalen aus z.B. einem DSP, vorliegen. Die eigentliche Kommunikation erfolgt dann über (Unix-)Sockets oder Pipes und manchmal, wie im Fall vom D-Bus, übernimmt ein Prozess das Message-Routing, Service-Aktivierung und Zugriffskontrolle.

Der normale Fall von IPC auf dem Desktop, erstreckt sich von Skripten ( die es dem User erlauben, in einer gemeinsamen Umgebung mit einer Vielzahl von anderen Anwendungen zu interagieren, bzw. sie zu kontrollieren), über das Bereitstellen zentraler Dienste, bis hin zur Koordination zwischen mehrfachen Instanzen kooperativer Anwendungen.

Als konkretes Beispiel kann der Dateizugriff in KDE dienen, der üblicherweise über das KIO Virtual File System (VFS) erfolgt und dabei KIOSlaves nutzt. Diese KIOSlaves sind kleine Programme, die Lese-, Schreib-, usw. -Zugriffe auf die gewünschten Daten, über gegebene Protokolle, bzw. Spezifikationen (wie z.B. http oder local Filesystem API), zur Verfügung stellen. Die Slaves laufen als unabhängige Prozesse und tauschen ihre Daten mit der Anwendung - die der User gerade verwendet - asynchron über IPC. Dies erlaubt nicht nur, dass der selbe Slave von verschiedenen Anwendungen angesprochen werden kann, sondern auch, dass der Slave selbst so einfach wie möglich gehalten werden kann. Es sind z.B. blockierende Operationen möglich, ohne dass die grafische Anwendung selbst blockiert wird. Die grafische Anwendung "sieht" niemals die aktuelle IPC, die im Hintergrund der KIO API erfolgt.

Ein anderes Beispiel für IPC ist der Gebrauch in KDE's "Unique Application Support". Wenn eine UniqueApplication gestartet wird, erfolgt eine Prüfung, ob bereits eine Instanz der selben Anwendung läuft und diese dann gegebenenfalls in den Vordergrund geholt, indem eine entsprechende Nachricht an sie gesendet wird, sich selbst zu zeigen.

Der primäre IPC Mechanismus für Anwendungen unter KDE4, ist D-Bus, ein populäres System, das von verschiedenster Software genutzt wird. QT4 stellt einen Satz intuitiver, stabiler Zugriffsmethoden zum D-Bus in Form von C++ Klassen bereit. Diese Tutorial-Serie stellt den D-Bus, sowie den Gebrauch der QT4 D-Bus API, vor. Dieser Artikel beschreibt die übergeordneten Aspekte des D-Bus und überlässt die Details, wie man diese Möglichkeiten nutzt, dem folgenden Kapitel.


Busse

D-Bus bietet mehrere Nachrichten-"Busse" zur Kommunikation der Anwendungen untereinander. Jeder Bus verfügt über seinen eigenen Satz an Eigenschaften, was es erlaubt verschiedene Kategorien von Nachrichten zu unterscheiden: Nachrichten auf einem Bus können nicht von einem anderen Bus aus erreicht und manipuliert werden, aber die Anwendungen, die an einem gemeinsamen Bus hängen, können jederzeit miteinander kommunizieren. Verschiedene Anwendungen können jederzeit mit jedem gegebenen Bus verbunden sein. Und eine Anwendung kann gleichzeitig mit mehreren Bussen verbunden sein. Dies erlaubt verschiedene Sicherheitsstufen auf verschiedenen Bussen während gleichzeitig eine effiziente Verteilung globaler und lokaler Nachrichten gewährleistet ist.

Ein Systemweiter Bus für Nachrichten des OS-Kernel selbst und andere Systemglobale Dienste steht standardmäßig zur Verfügung und wird sinnigerweise der system bus genannt. Dieser Bus wird zur Signalisierung genutzt, wenn neue Hardware am System verfügbar ist, wie z.B ein angesteckter USB Stick oder eine neu eingelegte DVD.

Jede Desktop Sitzung (z.B. jeder eingeloggte User) ist mit einem oder mehreren Bussen verbunden. Diese werden die session busse genannt. Der Standard Bus für die aktuelle Sitzung (Session) wird als der Session Bus bezeichnet und ist derjenige, den Desktopanwendungen am häufigsten zu benutzen versuchen.

Außerdem kann eine Anwendung ihren eigenen privaten Bus zwischen sich und einer anderen Anwendung, wie eine peer to peer Verbindung, aufbauen.

Es gibt praktisch keine Grenze der Anzahl an Bussen, die von Anwendungen von einem spezifischen User genutzt werden können. Wie auch immer, es ist die Fähigkeit, Busse, und damit die darüber geschickten Nachrichten, gemeinsam nutzen zu können, durch die Systeme wie der D-Bus glänzen.

Nachrichten

Die grundlegende Einheit der Kommunikation auf einem Bus ist die Nachricht. Jede Information, die über den Bus geschickt wird, geschieht in Form einer Nachricht, ähnlich, wie jede Information in TCP/IP Netzen in Paketen verschickt wird. Anders als in Netzwerk-Paketen, garantiert der D-Bus die vollständige Übermittlung des gesamten Datensatzes. Zusätzlich zu den Nutzdaten werden der Sender und Empfänger protokolliert, um ein verlässliches (Daten-)Routing bieten zu können.

Dieses System bildet die interne Arbeitsweise von Desktop-Anwendungen sehr gut ab: Methodenaufrufe resultieren in ausgeführten Aktionen mit der Möglichkeit, Werte zurückzuliefern. Eine der primären zusätzlichen Eigenschaften der D-Bus Nachrichten, ist ihre Asynchronität: eine Nachricht kann gesendet werden, während die Antwort nicht innerhalb einer bestimmten, erwarteten Zeit erfolgt (Timeouts werden angeboten, um ein "Deadlock" zu verhindern). Natürlich gibt es bequeme Möglichkeiten in der Qt4-D-Bus-API, um Aufrufe zu tätigen, die scheinbar synchron mit der aufrufenden Anwendung laufen.

Eine Anwendung kann Nachrichten an sich selbst schicken. Diese werden sozusagen "kurzgeschlossen" und verbleiben lokal zu der Anwendung. Dadurch ist es nich nötig, innerhalb der Anwendung Code bereitzustellen, der zwischen internen und externen Nachrichtenaufrufen unterscheidet. Für die Anwendung ist es egal woher die Nachricht stammt, solange die Reaktion darauf festgelegt ist. Das ist in hoch integrierten Anwendungssuiten oft sehr nützlich und verhindert schon viele Möglichkeiten eines Deadlocks, in dem mehrere Prozesse wechselseitig auf ihre Ergebnisse warten.

Namensräume und Adressen

Da mehrere Anwendungen auf demselben Bus operieren können und eine Anwendung mehrere Objekte besitzen kann, die Nachrichten empfangen und verarbeiten können, ist es notwendig ein Mittel zur eindeutigen Identifizierung einer gegebenen Nachricht zu haben. Ähnlich wie eine Wohnortsangabe durch Stadt, Strasse und Hausnummer gibt es 3 Informationen, die zusammengenommen eine eindeutige Adressierung für jedes Objekt auf dem Bus ermöglichen: die Schnittstelle, der Dienst und der Objektname.

Schnittstellen (Interface)

Eine Schnittstelle ist ein Satz aufrufbarer Methoden und Signale, die an den Bus gebunden sind. Eine Schnittstelle ist ein "Vertrag" zwischen den Anwendungen, der den Namen, die erforderlichen Parameter (wenn vorhanden) und Rückgabewerte (wenn vorhanden), definiert. Diese Methoden müssen nicht 1:1 auf Methoden oder das API der Anwendung abgebildet sein, wenn sie es auch häufig sind. Dies erlaubt es, dass verschiedenen Anwendungen, den gleichen Satz an Steuerungsmöglichkeiten anzubieten, selbst wenn die darunterliegende Funktionalität gänzlich anders implementiert wurde.

Schnittstellen können durch Anwendung von XML, für die Dokumentation und laufenden Code gleichzeitig beschrieben werden. Nicht nur, dass User und Programmierer dies als Referenz benutzen, es ist auch möglich (Objekt-) Klassen automatisch aus XML Beschreibungen heraus generieren zu lassen, was das Benutzen des D-Bus vereinfacht und die Fehlerträchtigkeit senkt (z.B. kann der Compiler die Syntax von Nachrichten schon zur Kompilierzeit überprüfen).

Dienste (Services)

Ein Dienst stellt eine Verbindung der Anwendung zu einem Bus dar.

Dienst bedeutet hier einer der "well known (wohlbekannter)" Bus Namen in der D-Bus Spezifikation Terminologie. Der Begriff "Bus Name" ist ein wenig verwirrend, ungeachtet seines Klangs. Bus Namen sind die Namen der Verbindungen(!) auf dem Bus, NICHT die Namen von Bussen. Der Begriff "Dienst" bzw. seine englische Bezeichnung "service", wird hier in der gleichen Weise benutzt, wie die QT Dokumentation es handhabt.

Dienste werden eindeutig identifizier- und ansprechbar gehalten, indem ein "Reverse Domain Name" Ansatz angewendet wird, wie es sich in vielen anderen Systemen bewährt hat, die Namensräume für viele Komponenten benötigen. Die meisten Dienste die von Anwendungen innerhalb des KDE Projekts bereitgestellt werden, benutzen das Prefix "org.kde". Also kann man höchstwahrscheinlich den Namen "org.kde.screensaver" auf dem session Bus wiederfinden, da er dort, wie oben beschrieben, proklamiert wird.

Man sollte den Domainnamen der eigenen Organisation und den Namen der Anwendung für einen guten Servicenamen zusammenfassen. Zum Beispiel, der Domainname lautet "fantastischsoft.de" und der Anwendungsname "unglaublichgut", dann könnte man seine Anwendung unter dem Dienstnamen (servicename) "de.fantastischsoft.unglaublichgut" auf dem D-Bus proklamieren.

Wenn eine Anwendung mehr als eine Verbindung zum Bus hat, oder wenn mehrere Instanzen der selben Anwendung zeitgleich aktiv sind, wird ein eindeutiger Dienstname für jede Verbindung benötigt. Der Einfachkeit halber wird dann meist die Prozess-ID an den Dienstnamen angehängt.

Objekte

Natürlich kann eine Anwendung wünschen, auf mehr als ein Objekt auf dem Bus zuzugreifen. Diese Viele-zu-Eins Beziehung zwischen Objekten und Diensten wird durch das Angebot einer Pfad-Komponente zur entsprechenden Adresse erreicht. Jeder Pfad zu einem Dienst stellt ein eindeutiges, klar unterscheidbares Objekt dar. Ein Beispiel könnte

/MainInterface

oder

/Documents/Doc1

sein. Die aktuelle Pfadstruktur ist völlig beliebig und es liegt an der Anwendung, den Dienst anzubieten wie diese Pfadstruktur zu interpretieren ist. Diese Pfade stellen einen einfachen Weg dar, Objekte, die Nachrichten an die Anwendung senden, zu identifizieren und logisch zusammenzufassen.

Einige (Software-)Bibliotheken exportieren Objekte mit vorangestellter "reverse Domain", um ihre Objekte in einem definierten eigenem Namensraum benutzen zu können.Das ist üblich für Bibliotheken und "Plugins", die sich einen beliebigen Dienst teilen und so eine Kollision der Objekt-Namen vermeiden, die ja auch von anderen Komponenten exportiert werden könnten. Allerdings wird diese Praxis nicht innerhalb von KDE Anwendungen und Bibliotheken angewendet.

Objekte bieten Zugriff auf Schnittstellen. Tatsächlich kann ein bestimmtes Objekt Zugriff auf mehrere Schnittstellen gleichzeitig bieten.

Zusammenfassung

Eine D-Bus-Nachricht enthält eine Adresse aus allen oben genannten Komponenten, so dass sie auf die richtige Anwendung, Objekt und Methode geroutet werden kann. Eine solche Adresse kann wie folgt aussehen:

org.kde.krunner /ScreenSaver org.kde.screensaver.setBlankOnly

In diesem Fall ist "org.kde.krunner" der Dienst. "/ScreenSaver" ist der Pfad zum Objekt. "org.kde.screensaver" ist die Schnittstelle, die vom Objekt exportiert wird und "setBlankOnly" ist eine Methode, die in der Schnittstelle definiert ist. Wenn das /ScreenSaver Objekt nur die org.kde.screensaver Schnittstelle anbietet (oder die setBlankOnly Methode einzigartig unter den eingebundenen Diensten ist), dann ist der folgende Aufruf gleichbedeutend mit dem obigen:

org.kde.krunner /ScreenSaver setBlankOnly

Auf diese Weise wird jedes mögliche Ziel eindeutig und zuverlässig adressierbar.

Aufrufen und Aufgerufen werden

Jetzt, da wir einen Weg haben, jeden beliebigen Endpunkt auf dem Bus zu adressieren, können wir die Möglichkeiten prüfen, wenn es um das tatsächlich Senden oder Empfangen von Nachrichten geht.

Methoden

Methoden sind Nachrichten, die gesendet werden, um Programmcode in der empfangenden Anwendung ausführen zu lassen. Wenn die Methode nicht verfügbar ist, weil z.B. die Adresse falsch ist, oder die angeforderte Anwendung nicht ausgeführt wird, wird ein Fehler an die aufrufende Anwendung zurückgegeben. Wenn die Methode erfolgreich aufgerufen wird, wird ein optionaler Rückgabewert an die aufrufende Anwendung zurückgegeben. Selbst wenn kein Rückgabewert vorgesehen ist, wird eine entsprechende Erfolgsmeldung zurückgegeben. Diese Vorgehensweise erzeugt eine Menge "Overhead" und es ist wichtig, dies bei Performance-kritischem Code im Auge zu behalten.

Solche Methodenaufrufe erfolgen immer durch die rufende Anwendung und die resultierenden Nachrichten haben genau einen Ursprung und eine Zieladresse.

Signale

Signale sind wie Methodenaufrufe, außer, dass sie in die "umgekehrte" Richtung passieren und sind nicht auf ein einzelnes Ziel gebunden sind. Ein Signal wird von der Anwendung emittiert (ausgegeben), die die Schnittstelle bereitstellt und ist für jede Anwendung auf dem gleichen Bus verfügbar. Dies ermöglicht es einer Anwendung, spontane Veränderungen im Status, oder andere Ereignisse, an jede Anwendung, die daran interessiert sein könnte, zu propagieren.

Wenn das jetzt alles wie der Signal-Slott Mechanismus von QT klingt, dann liegt das daran, dass es letztendlich dieser ist. Für alle Absichten und Zwecke ist es eine nicht-lokale Version der gleichen Funktionalität.

Nützliche Werkzeuge

Es gibt mehrere nützliche Werkzeuge für die Erkundung des D-Bus sowie die Entwicklung von Applikationen, die D-Bus verwenden. Werfen wir hier einen kurzen Blick auf die Hilfsmittel, die in den folgenden Kapiteln detaillierter behandelt werden:

qdbus

qdbus ist ein Kommandozeilen Werkzeug, das dazu benutzt werden kann, die Dienste, Objekte und Schnittstellen auf einem gegebenen Bus aufzulisten. Man kann damit das System den "default session bus" erforschen. Wenn der [pre]--system[/pre] Parameter mitgegeben wird, verbindet sich qdbus mit dem System-Bus, ansonsten mit dem Session-Bus.

qdbus interpretiert weitere Argumente als Adress und, soweit vorhanden, als an das Objekt zu übergebende Parameter. Wenn keine vollständige Adresse angegeben wird, dann werden alle verfügbaren Objekte dieses Busendpunktes aufgelistet. Wenn z.B. gar keine Adresseb übergeben wurde, werden alle verfügbaren Dienste aufgezeigt. Wenn ein Dienst angegeben wurde, dann werden alle dazugehörigen Objektpfade gelistet. Usw. Auf diese Weise kann man ganz einfach mit Objekten interagieren und diese erforschen. Das macht qdbus zum idealen Werkzeug zum Testen, Skripten und einfach nur zum Untersuchen.

qdbusviewer

qdbusviewer ist eine QT Anwendung mit grafischer Oberfläche, die die gleiche Funktionalität wie qdbus auf der Kommandozeile bietet. Nur eben grafisch. Dies ist eine etwas benutzerfreundlichere Art, mit dem Bus herumzuspielen und ihn zu untersuchen. qdbusviewer wird zusammen mit QT4 ausgeliefert und ist für jemanden der die Grundkonzepte des D-Bus kennt, intuitiv zu bedienen.