Qtの基礎 - D-Bus
概要
D-Busは、オープンソースのプロセス間通信(IPC)機構であり、freedesktop.orgプロジェクトの一部である。
幅広いソフトウェアで使用されており、デスクトップの通知、メディアプレーヤー制御、XDGポータル等、多くのfreedesktop.org標準がD-Busをベースに構築されている。
IPCとは、あるプロセスから別のプロセスへ情報を取得する方法を説明するために使用することができる。
これは、データの交換、メソッドの呼び出し、イベントのリスニング等がある。
- デスクトップにおけるIPCの使用例
- スクリプト(ユーザが共通の環境でスクリプトを実行して、実行中の様々なソフトウェアと対話または制御する)
- 集中型サービスへのアクセスの提供
- 協調型ソフトウェアの複数のインスタンス間の調整
- スクリプト(ユーザが共通の環境でスクリプトを実行して、実行中の様々なソフトウェアと対話または制御する)
- D-Busの使用例
- freedesktop.orgのnotification仕様
- これは、ソフトウェアは通知を中央サーバ(Plasma等)に送信して、中央サーバは通知を表示して、通知が閉じられたりアクションが実行されたりといったイベントを送り返すものである。
- freedesktop.orgのnotification仕様
- IPCの他の使用例
- ユニークなソフトウェアのサポート
- これは、ソフトウェアの起動時に、まず、同じソフトウェアの他の実行中のインスタンスを確認して、
- もし存在すれば、IPCを介して実行中のインスタンスにメッセージを送信して、自分自身を表示して終了させる。
- ユニークなソフトウェアのサポート
D-Busは、言語やツールキットに囚われないため、あらゆるプロバイダのソフトウェアやサービスが相互作用することができる。
Qtは、D-Busと対話するためのクラスとツールのセットを提供しており、ここでは、D-Busのハイレベルなコンセプト、および、QtやKDEソフトウェアでの実装を記載する。
バス
D-Busは、ソフトウェアが相互に通信するため、複数のメッセージバスを提供する。
各バスには独自の接続機能があり、異なるカテゴリーのメッセージを分離することができる。
あるバスで送信されたメッセージは他のバスからアクセスできないが、同じバスに接続されたソフトウェアは全て互いに通信することができる。
任意のバスに複数のソフトウェアを同時に接続することができ、また、1つのソフトウェアが複数のバスに同時に接続することも可能である。
これにより、バスごとに異なるセキュリティポリシーを適用できると共に、グローバルメッセージとローカルメッセージの両方を効率的に共有することができる。
D-Busは、2つの定義済みバス(システムバスとセッションバス)を予め用意されており、一般的なD-Busの使用方法をほぼカバーすることができる。
- システムバス
- ハードウェア管理等のシステムグローバルサービスに使用される。
- これはユーザー間で共有され、通常、厳格なセキュリティ・ポリシーが付属しています。
- セッションバス
- 各デスクトップセッション(ログインしているユーザ等)には、セッションバスが存在する。
- これは、GUIソフトウェア等が最も頻繁に使用する傾向がある。
さらに、ソフトウェアは、必要に応じて複数の独自のバスを作成することができる。
メッセージ
メッセージは、バスにおける通信の基本単位である。
TCP/IPのパケットと同様に、バスでやり取りされる情報は、全てメッセージで行われる。
しかし、ネットワークのパケットとは異なり、D-Busの各メッセージには、送受信されるデータ1式が含まれていることが保証されている。
メッセージには、送信されるデータだけでなく、送信者と受信者が誰であるかも記録されており、適切なルーティングを可能にする。
メッセージは、メソッドコール、シグナルエミッション、メソッドの戻り値であり、エラー情報も含むことがある。
名前空間とアドレス
複数のソフトウェアが同一のバス上に存在し、1つのソフトウェアが複数のオブジェクトを提供してメッセージを送信することができるため、
特定の住宅やオフィスを一意に識別するのと同様に、任意のバス上の任意のオブジェクトを効果的かつ一意にアドレス指定する手段を持つことが必要である。
インターフェース、サービス、オブジェクト名の3つの情報を組み合わせることにより、バス上の任意のオブジェクトに一意のアドレスを作成することができる。
インターフェイス
インターフェイスは、バス上に公表される呼び出し可能なメソッドとシグナルのセットである。
インターフェースは、メッセージを渡すソフトウェアの間で、インターフェースの名前、パラメータ(もしあれば)、戻り値(もしあれば)を定義する"契約"を提供する。
これらのメソッドは、インターフェイスを使用しているソフトウェアのメソッドやAPIに1対1で直接マッピングされることもある。(直接マッピングされないこともある)
これにより、複数のソフトウェアが、内部の実装に関係なく、類似または同一のインターフェイスを提供することができ、
一方で、ソフトウェアは、ソフトウェアの内部設計を気にすることなく、これらのインターフェイスを使用できるようになる。
インターフェイスは、文書化やコードの再利用を目的として、XMLで記述する。
ユーザや開発者は、インターフェースのXML記述を参照できるだけでなく、開発者はXMLから自動生成されたクラスを使用することができる。
このため、D-Busの使用は非常に簡単で、エラーが発生しにくくなる。(例. コンパイラがコンパイル時にメッセージの構文を確認することができる)
サービス
サービスとは、ソフトウェアとバスの接続を表すものである。
ここでいうサービスとは、D-Busの用語でいうバス名に相当するものである。
(バス名という用語は、バス上の接続名であり、バスの名前ではない。そのため、Qtのドキュメントで記載されているように、サービスという用語を使用する)
これらは、複数のコンポーネントの名前空間を必要とする他の多くのシステムで見られるように、"逆ドメイン名"アプローチを使用することにより、一意に保たれる。
KDEプロジェクトのソフトウェアが提供する多くのサービスでは、サービス名にorg.kdeというプレフィックスを使用している。
そのため、セッションバスにおいて、org.kde.screensaverが宣伝されているのを見掛けるかもしれない。
サービス名には、開発者の組織やソフトウェアのドメイン名を使用することを推奨する。
例えば、開発者のドメインがawesomeapps.org、ソフトウェアの名前がwickedwidgetの場合、バス上のサービス名はorg.awesomeapps.wickedwidgetとなる。
ソフトウェアが複数のバスに接続している場合、または、同一のソフトウェアの複数のインスタンスが同時にアクティブになっている場合は、接続ごとに一意なサービス名を使用する必要がある。
多くの場合、プロセスIDをサービス名に付加することでこれを実現する。
オブジェクト
ソフトウェアは、バス上の複数のオブジェクトへのアクセスを宣伝する可能性が高い。
オブジェクトとサービスの間のこの多対1の関係は、アドレスにパスコンポーネントを提供することで対応される。
サービスに関連付けられた各パスは、異なる一意のオブジェクトを表す。(例. /MainInterface、/Documents/Doc1)
実際のパス構造は完全に任意であり、どのようなパスにするかは、サービスを提供するソフトウェア次第である。
これらのパスは、他のソフトウェアにメッセージを送信するソフトウェアのために、オブジェクトを識別し、論理的にグループ化する方法を提供する。
一部のライブラリは、オブジェクトを適切に名前空間化するために、オブジェクトパスの先頭に"リバースドメイン"を付けてエクスポートする。
これは、任意のサービスに参加するライブラリやプラグインではよくあることで、そのため、ソフトウェアや他のコンポーネントによりエクスポートされたオブジェクトとの全ての衝突を避けなければならない。
しかし、KDEソフトウェアやライブラリでは、この方法は使用されていない。
オブジェクトはインターフェースへのアクセスを提供しており、オブジェクトは同時に複数のインタフェースへのアクセスを提供することができる。
アドレスの例
D-Busメッセージには、上記で記載したコンポーネントで構成されるアドレスが含まれており、正しいソフトウェア、オブジェクト、メソッドコールにルーティングされる。
# 例. KRunnerのアドレス org.kde.krunner /App org.kde.krunner.App.display org.kde.krunner : サービス /App : オブジェクトへのパス org.kde.krunner.App : オブジェクトがエクスポートするインターフェース display : インターフェース内のメソッド
もし、/Appオブジェクトがorg.kde.krunner.Appインターフェースを提供するのみの場合(または、実装するサービスの中で表示メソッドが一意の場合)、これはアドレスとして同様に機能する。
org.kde.krunner /App display
このようにして、可能性のあるそれぞれの宛先を一意かつ確実にアドレスで指定することができる。
メソッドとシグナル
バス上の任意のエンドポイントをアドレス指定する方法を使用して、メッセージを送受信する場合の可能性を検討する。
メソッド
受信側のソフトウェアにおいて、メソッド(関数)を実行するために送信されるメッセージのことである。
メソッドの呼び出しに失敗した場合(アドレスの間違い、または、要求されたソフトウェアが動作していない等の理由でメソッドが利用できない場合)、呼び出し側のソフトウェアにエラーが返る。
メソッドの呼び出しに成功した場合、呼び出し元のソフトウェアにオプションの戻り値が返る。(戻り値が提供されない場合でも、成功メッセージが返る)
このラウンドトリップにはオーバーヘッドがあり、パフォーマンスが重要なコードではこれを念頭に置くことが重要である。
メソッド呼び出しは、常に呼び出し元のソフトウェアによって開始されて、結果として生じるメッセージは正確に1つの送信元アドレスと1つの送信先アドレスを持つ。
シグナル
シグナルはメソッドコールに似ているが、"逆方向"に発生すること、単一の宛先に縛られないことが特徴である。
シグナルは、インターフェイスをエクスポートしているソフトウェアから発信されて、同一バス上のどのソフトウェアでも利用できる。
これにより、ソフトウェアは、状態の変化やその他のイベントを、それらの変化を追跡するソフトウェアに自発的に知らせることができる。
これは、Qtのシグナルとスロットの仕組みに似ており、これは、同じ機能のシステム版である。
便利なツール
D-Busのバスの検索やD-Busを使用したソフトウェアの開発には、いくつかのエンドユーザ向けの便利なツールが存在する。
qdbus
qdbus
コマンドは、コマンドラインツールであり、与えられたバス上のサービス、オブジェクト、インタフェースを表示したり、バス上の与えられたアドレスにメッセージを送信するために使用できる。
これは、セッションバスおよびシステムバスの両方を検索するために使用することができる。
--system
オプションを付加する場合は、qdbusはシステムバスに接続する。
--system
オプションを付加しない場合は、セッションバスを使用する。
qdbus
コマンドは、与えられた引数を、与えられたオブジェクトに渡すためのアドレス、および、パラメータ(存在する場合)として使用する。
もし、完全なアドレスが与えられなかった場合、バス上のその地点から利用可能な全てのオブジェクトを表示する。
例えば、アドレスが提供されない場合は、利用可能なサービスのリストが表示される。
サービス名を指定する場合、オブジェクトのパスが提供される。
パスを指定する場合、全てのインターフェースの全てのメソッドが表示される。
このように、非常に簡単にバス上のオブジェクトを検索して操作することができる。
qdbusviewer
qdbusviewer
は、Qtで作成されたGUIソフトウェアであり、qdbus
コマンドがCUIから提供する機能と基本的に同一のものをGUIとして提供している。
qdbusviewer
は、Qt本体と同梱されており、オブジェクトアドレス等のD-Busの基本概念に慣れていれば、誰でも簡単に使用できる。