Archive:Development/Tutorials/D-Bus/Introduction (zh CN)

From KDE TechBase


D-BUS介绍
Tutorial Series   D-Bus
Previous   None
What's Next   [[../Accessing Interfaces|访问D-Bus接口]]
Further Reading   D-Bus文档页面

摘要

D-Bus 是一套开源的进程通讯机制,它如今被广泛应用于各种开源桌面软件之中。他已经是freedesktop.org项目的一部分了而且一大批软件程序已经采用了这种机制。包括Linux上的热插拔提醒,Strigi的桌面搜索以及KDE4中的进程通讯。本教程从开发员的角度介绍了D-Bus的术语以及一些基本概念。

什么是进程间通讯(IPC)?

进程间通讯表示通过一些方法把信息在系列进程间进行传递共享。信息传递的路径可以是通过应用程序数据,方法调用,以及信号传递。这些通讯通常通过套接字,管道,有时甚至就是D-BUS来实现。D-Bus有专门一个进程来执行信息路由,服务启动和安全控制。

进程间通讯在桌面环境的运用从脚本执行(允许用户执行一个脚本在共享环境进行交互或控制各种正在运行的应用程序) ,到提供集中服务以协调的多个相互合作的应用程序实例之间的互动。

举个实例,在KDE的文件访问通常是通过使用KIOSlaves实现的KIO虚拟文件系统(虚拟文件系统)。这些KIOSlaves是小型应用程序,遵循某一协议或规范(如HTTP或本地文件系统的API)来执行读,写等对数据的访问。KIOSlaves是作为独立的运行程序和数据交换的应用-这是用户互动-异步进程间通讯。这不仅可同KIOSlaves被多个应用程序使用,但它也使KIOSlaves变得简单,他们做的事情,如执行拦截行动,是不会造成图形应用程序的中断的。图形应用程序从未真正“看到”进程间通讯的发生,因为那些都是发生在幕后KIO的应用程序接口。

另一个例子是进程通讯对KDE应用的特有支持。当KUniqueApplication启动它首先检查是否有其他正在运行的实例,如果有它就发个信息,通过进程通讯向运行中的实例标识自己。

KDE4中主要的进程通讯机制就是D-Bus,这是在各种软件都流行的应用。Qt4提供了一套直观和稳定C++类库,由此绑定到D-Bus。这一系列教程介绍了D-Bus以及包括如何使用Qt4 D-Bus接口。本文特别在整体上介绍了D-Bus,至于关于如何使用这些特性和功能的条款内容细节将在后面异议介绍。

总线

D-Bus提供了多种信息“总线”以供不同的应用程序来互相沟通。每个总线提供自己的连接设施,使分离的不同类别的邮件:一个总线发送的邮件在另一条总线上是无法访问的,但连到相同总线的应用程序都可互相沟通。多个应用程序可以在任何特定时间连接到任何特定的总线,而且应用程序可以同时连接到多个总线。这使不同的总线可以采用不同的安全策略,同时这也有效地共享享全局和本地的信息。

发送系统范围内消息的总线,从操作系统内核本身和其他系统的全局性服务提供了默认调用,这类总线称为系统总线。这是总线的作用,如当系统发现有新硬件时,当一个插入闪存时或DVD插入驱动器时,总有信号发出。

每个桌面会话(对每个登录用户)都有一个或多个总线与之关联。他们被称为会话总线。当前对话的缺省总线称为会话总线,而且这事桌面程序最常用的一个。

另外,应用程序在端对端的情况下可以独立创建自己和其它应用的总线。

实际上,应用程序可用的总线数量没有什么特别的限制。然而,它是能够分享总线-共享信息-在系统,一如D-Bus真正服务。

消息

总线通讯的单元是消息。所有的信息传递的总线是在消息的形式,类似的,TCP/IP传播信息的方式是通过数据包。不同于网络数据包,每条发送或接收D-Bus消息都包含一整套数据。除了数据传送,消息体还记录谁是发送者或接收者以及允许使用的中间路由。

该系统很好的反映了桌面应用的内部工作流程:方法调用(信息)产生将会得到一些运行后可能的返回值。D-Bus消息的一个主要附加功能就是,他们异步的:一个信息发出后,响应可能不知道什么时候才会到达(内建超时机制防止'永远等待'的死锁) 。当然,便利的Qt4 D-Bus编程接口调用看上去似乎是同步的。

应用程序自身就可以发送消息。These are "short-circuited" and kept local to the application, so it is not necessary for code in an application to worry about whether or not it might actually be calling a remote or local application. This is often useful in highly componentized apps and prevents possible deadlock situations.

命名空间和地址

Since multiple applications can be on the same bus, and one application may provide multiple objects to which messages can be sent, it is necessary to have a means to effectively and unambiguously address any given object on any given bus, similar to the way a street address uniquely identifies any given residence or office. There are 3 pieces of information which, when taken together, create a unique address for any given object on a bus: interface, service and object name.

接口

An interface is a set of callable methods and signals that are advertised on the bus. An interface provides a "contract" between the applications passing messages that defines the name, parameters (if any) and return values (if any) of the interface. These methods may not map directly in a one-to-one fashion to methods or API in the application that is advertising the interface, though they often do. This allows multiple applications to provide similar or the same interfaces, regardless of internal implementation, while allowing applications to use these interfaces without worrying about the internal design of the applications.

Interfaces can be described for documentation and code re-use purposes using XML. Not only can users and programmers reference the XML description of the interface, but developers can use classes that are auto-generated from the XML — making using D-Bus much easier and less error-prone (e.g. the compiler can check the syntax of messages at compile time).

服务

服务表示一个应用程序和一条总线连在一起了。

service here corresponds to "well-known" Bus names in the D-Bus specification terminology. A term "Bus name" is a bit confusing. Regardless of how it sounds like, Bus names are the names of connections(!) on the bus, not names of buses(!). So here the term service used, as QT Documentation calls it.

These are kept unique by using a "reverse domain name" approach, as can be seen in many other systems that need to namespace for multiple components. Most services provided by applications from the KDE project itself use the org.kde prefix to their service name. So one may find "org.kde.screensaver" advertised on the session bus.

You should use the domain name for your organization or application for your service names. For example if your domain is awesomeapps.org and the name of your application is wickedwidget you would probably use org.awesomeapps.wickedwidget as the service name on the bus.

If an application has more than one connection to a bus, or if multiple instances of the same application may be active at once, it will need to use a unique service name for each connection. Often this is done by appending the process ID to the service name.

对象

Of course, an application is likely to advertise access to more than one object on the bus. This many-to-one relationship between objects and services is accommodated by providing a path component to the address. Each path associated with a service represents a different, unique object. An example might be /MainInterface or /Documents/Doc1. The actual path structure is completely arbitrary and is up to the application providing the service as to what the paths should be. These paths simply provide a way to identify and logically group objects for applications that send messages to the application.

Some libraries export object paths with their "reverse domain" prepended to it, so as to properly namespace their objects. This is quite common for libraries and plugins that join an arbitrary service and must therefore avoid all clashing with objects exported by the application and other components. However, this practice is not in use in KDE applications and libraries.

Objects provide access to interfaces. In fact, a given object can provide access to multiple interfaces at the same time.

综合集成

A D-Bus message contains an address made up of all the above components so that it can be routed to the correct application, object and method call. Such an address might look like this:

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

In this case org.kde.krunner is the service, /ScreenSaver is the path to the object, org.kde.screensaver is the interface the object exports and setBlankOnly is a method in the interface. If the /ScreenSaver object only provides the org.kde.screensaver interface (or the setBlankOnly method is unique amongst the services it implements) then this would work equally well as an address:

org.kde.krunner /ScreenSaver setBlankOnly

In this way each possible destination is uniquely and reliably addressable.

调用和被调用

Now that we have a way to address any given end point on the bus, we can examine the possibilities when it comes to actually sending or receiving messages.

方法

Methods are messages that are sent to cause code to be executed in the receiving application. If the method is not available because, for instance, the address was wrong or the requested application is not running, an error will be returned to the calling application. If the method is successfully called, an optional return value will be returned to the calling application. Even if there is no return value provided, a success message will be returned. This round trip does have overhead, and it is important to keep this in mind for performance critical code.

Such method calls are always initiated by the calling application and the resulting messages have exactly one source and one destination address.

信号

Signals are like method calls except that they happen in the "reverse" direction and are not tied to a single destination. A signal is emitted by the application which is exporting the interface and is available to any application on the same bus. This allows an application to spontaneously advertise changes in state or other events, to any applications which may be interested in tracking those changes.

If this sounds a lot like the signal and slots mechanism in Qt, that's because it is. For all intents and purposes it is a non-local version of the same functionality.

有用工具

There are several useful tools for exploring the D-Bus busses as well as developing applications that use D-Bus. We will now look briefly at end-user tools as the articles that follow cover the development tools in greater detail and context.

qdbus

qdbus is a command line tool which can be used to list the services, objects and interfaces on a given bus as well as send messages to a given address on the bus. It can be used to explore both the system and the default session bus. If the --system switch is passed, qdbus will connect to the system bus, otherwise it uses the session bus.

qdbus uses the rest of the supplied arguments on the command as an address and, if any, parameters to pass to a given object. If a full address is not supplied, then it lists all the objects available from that point on the bus. For instance, if no addresses are provided a list of available services is listed. If a service name is provided, object paths will be provided. If a path is also provided all methods in all interfaces will be listed. In this way one can quite easily explore and interact with objects on the bus, making qdbus very useful for testing, scripting and even idle exploration.

qdbus浏览器

qdbusviewer is a Qt application that provides a graphical interface to essentially the same set of features that qdbus provides on the command line, thus providing a more user friendly mechanism to interact with the bus. qdbusviewer ships with Qt4 itself and is easy for anyone who is familiar with the basic D-Bus concepts, such as object addresses, to use.