Get on the D-BUS

附脑图


from:https://www.linuxjournal.com/article/7744

D-BUS' initial goal is to be a replacement for CORBA and DCOP, the remote object systems used in GNOME and KDE, respectively. 

D-BUS, as a full-featured IPC and object system, has several intended uses. First, D-BUS can perform basic application IPC, allowing one process to shuttle data to another—think UNIX domain sockets on steroids. Second, D-BUS can facilitate sending events, or signals, through the system, allowing different components in the system to communicate and ultimately to integrate better. For example, a Bluetooth daemon can send an incoming call signal that your music player can intercept, muting the volume until the call ends. Finally, D-BUS implements a remote object system, letting one application request services and invoke methods from a different object—think CORBA without the complications.

注:以上这段精辟的论述,从3个层面描述了D-Bus的功能 / 作用。这也正是 D-Bus 的目标。

Why D-BUS Is Unique

D-BUS is unique from other IPC mechanisms in several ways. First, the basic unit of IPC in D-BUS is a message, not a byte stream. In this manner, D-BUS breaks up IPC into discrete messages, complete with headers (metadata) and a payload (the data). The message format is binary, typed, fully aligned and simple. It is an inherent part of the wire protocol. This approach contrasts with other IPC mechanisms where the lingua franca is a random stream of bytes, not a discrete message. (上述为通讯协议上的区别)

Second, D-BUS is bus-based. The simplest form of communication is process to process. D-BUS, however, provides a daemon  known as the message bus daemon, that routes messages between processes on a specific bus. In this fashion, a bus topology is formed, allowing processes to speak to one or more applications at the same time. Applications can send to or listen for various events on the bus. (关于BUS类型的拓扑结构,应该是D-Bus与其他IPC最大的区别)

A final unique feature is the creation of not one but two of these buses, the system bus and the session bus. The system bus is global, system-wide and runs at the system level. All users of the system can communicate over this bus with the proper permissions, allowing the concept of system-wide events. The session bus, however, is created during user login and runs at the user, or session, level. This bus is used solely by a particular user, in a particular login session, as an IPC and remote object system for the user's applications.

D-BUS Concepts

消息由消息头和消息体组成。消息头由消息的固有字段信息组成。消息体由一串字符串值组成。消息体的每个字符串值的意义由消息头中的描述指定,消息头的长度必须是8的倍数,相应的,消息体由8的倍数处开始。 Messages are sent to objects. 消息类型包括:

  • Methodcall消息:将触发一个函数调用 ;
  • Methodreturn消息:触发函数调用返回的结果;
  • Error消息:触发的函数调用返回一个异常 ;
  • Signal消息:通知,可以看作为事件消息。

Objects are addressed using path names, such as /org/cups/printers/queue. Processes on the message bus are associated with objects and implemented interfaces on that object.

D-BUS supports multiple message types, such as signals, method calls, method returns and error messages. Signals are notification that a specific event has occurred. They are simple, asynchronous, one-way heads-up messages. Method call messages allow an application to request the invocation of a method on a remote object. Method return messages provide the return value resulting from a method invocation. Error messages provide exceptions in response to a method invocation.

Big Conceptual Picture : Address -> [Bus Name] -> Path -> Interface -> Method

bus name是可选的,除非是希望把消息送到特定的应用中才需要。interface也是可选的,有一些历史原因,DCOP不需要指定接口,因为DCOP在同一个对象中禁止同名的方法。

D-BUS is fully typed and type-safe. Both a message's header and payload are fully typed. Valid types include byte, Boolean, 32-bit integer, 32-bit unsigned integer, 64-bit integer, 64-bit unsigned integer, double-precision floating point and string. A special array type allows for the grouping of types. A DICT type allows for dictionary-style key/value pairs.

D-BUS is secure. It implements a simple protocol based on SASL profiles for authenticating one-to-one connections. On a bus-wide level, the reading of and the writing to messages from a specific interface are controlled by a security system. An administrator can control access to any interface on the bus. The D-BUS daemon was written from the ground up with security in mind.

注:以上是一些精简的概念说明,但非常到位。如果想得到更详细的说明,参考官网:https://www.freedesktop.org/wiki/IntroductionToDBus/

对比前后两张d-feet截图,可以看到 address 可以共用(一个 UNIX Domain Socket 连接中传递了多个Bus Object的信息),Bus Name 可以重复,Unique Name 则不能重复。因此对象是由(BusName + ObjectPath)唯一确定的。注意:通信双方是Object,不是application。

在一个Object中,可以包括多个Interface——可以理解为对功能的分组(或者说是Namespace),是多个方法和信号的集合。

 

桌面应用程序通信
典型的桌面都会有多个应用程序在运行,而且,它们经常需要彼此进行通信。DCOP 是一个用于 KDE 的 解决方案,但是它依赖于 Qt,所以不能用于其他桌面环境之中。类似的,Bonobo 是一个用于 GNOME 的 解决方案,但是非常笨重,因为它是基于 CORBA 的。它还依赖于 GObject,所以也不能用于 GNOME 之外。 D-BUS 的目标是将 DCOP 和 Bonobo 替换为简单的 IPC,并集成这两种桌面环境。由于尽可能地减少了 D-BUS 所需的依赖,所以其他可能会使用 D-BUS 的应用程序不用担心引入过多依赖。
 
桌面/操作系统通信
术语“操作系统”在这里不仅包括内核,还包括系统后台进程。例如,通过使用 D-BUS 的 udev (Linux 2.6 中取代 devfs 的, 提供动态 /dev 目录),当设备(比如一个 USB 照相机)插入时会发放出一个信号。 这样可以更紧密地将硬件集成到桌面中,从而改善用户体验。
 
Tools
  • 要查看Dbus总线上的服务和对象可以借助d-feet 和qdbusviewer
  • 要发送信号可以使用dbus-send
  • 要查看Dbus上的消息流可以使用dbus-monitor
  • 环境变量DBUS_SESSION_BUS_ADDRESS的值即为dbus-daemon的总线地址
DBUS可以提供一些更高层的功能
  • 结构化的名字空间;
  • 独立于架构的数据格式;
  • 支持消息中的大部分通用数据元素;
  • 带有异常处理的通用远程调用接口;
  • 支持广播类型的通信。

Signature Strings

D-Bus uses a string-based type encoding mechanism called Signatures to describe the number and types of arguments requried by methods and signals. Signatures are used for interface declaration/documentation, data marshalling, and validity checking. Their string encoding uses a simple, though expressive, format and a basic understanding of it is required for effective D-Bus use. The table below lists the fundamental types and their encoding characters.

Container TypesNo spacing is allowed within signature strings. When defining signatures for multi-argument methods and signatures, the types of each argument are concatenated into a single string. For example, the signature for a method that accepts two integers followed by a string would be "iis".

There are four container types: Structs, Arrays, Variants, and Dictionaries.

Structs

Structures are enclosed by parentheses and may contain any valid D-Bus signature. For example, (ii) defines a structure containing two integers and ((ii)s) defines a structure containing a structure of two integers followed by a string. Empty structures are not permitted.

Arrays

Arrays define a list consisting of members with a fixed type. The array charater a must be immediately followed by the type of data in the array. This must be a single, complete type.

Examples

  • ai    - Array of 32-bit integers
  • a(ii)  - Array of structures
  • aai    - Array of array of integers

Variants

Variants may contain a value of any type. The marshalled value of the variant includes the D-Bus signature defining the type of data it contains.

Dictionaries

Dictionaries work in a manner similar to that of structures but are restricted to arrays of key = value pairs. The key must be a basic, non-container type and the value may be any single, complete type. Dictionaries are defined in terms of arrays using {} to surround the key and value types. Examples:

  • a{ss}    - string ⇒ string
  • a{is}    - 32-bit signed integer ⇒ string
  • a{s(ii)}   - string ⇒ structure containing two integers
  • a{sa{ss}}  - string ⇒ dictionary of string to string

Message Routing

Messages are routed to client connections by destination address and match rules. Destination address routing is used when a message’s destination parameter contains a unique or well-known bus name. This is typically the case with method call and return messages which inherently require 1-to-1 communication. Signals, on the other hand, are broadcast messages with no specific destination. For these, client applications must register match rules in order to receive the signals they are interested in.

Although signal registration is the most common use for message matching rules, DBus message matching rules can be used to request delivery of any messages transmitted over the bus; including the method call and return messages between arbitrary bus clients. The messages delivered via match rules are always copies so it is not possible to use this mechanism to redirect messages away from their intended targets.

Message match rules are set via the org.freedesktop.DBus.AddMatch method and are formatted as a series of comma-separated, key=value paris contained within a single string. Excluded keys indicate wildcard matches that match every message. If all components of the match rule match a message, it will be delivered to the requesting application. The following table provides a terse description of the keys and values that may be specified in match rules. For full details, please refer to the DBus specification.

Standard Interfaces

For example:


Why D-BUS?

These concepts make nice talk, but what is the benefit? First, the system-wide message bus is a new concept. A single bus shared by the entire system allows for propagation of events, from the kernel (see The Kernel Event Layer sidebar) to the uppermost applications on the system. Linux, with its well-defined interfaces and clear separation of layers, is not very integrated. D-BUS' system message bus improves integration without compromising fine engineering practices. Now, events such as disk full and printer queue empty or even battery power low can bubble up the system stack, available for whatever application cares, allowing the system to respond and react. The events are sent asynchronously, and without polling.

The Kernel Event Layer

The Kernel Event Layer is a kernel-to-user communication mechanism that uses a high-speed netlink socket(是否就是 UNIX Domain Socket?) to communicate asynchronously with user space. This mechanism can be tied into D-BUS, allowing the kernel to send D-BUS signals!

The Kernel Event Layer is tied to sysfs, the tree of kobjects that lives at /sys on modern Linux systems. Each directory in sysfs is tied to a kobject, which is a structure in the kernel used to represent objects; sysfs is an object hierarchy exported as a filesystem.

Each Kernel Event Layer event is modeled as though it originated from a sysfs path. Thus, the events appear as if they emit from kobjects. The sysfs paths are easily translatable to D-BUS paths, making the Kernel Event Layer and D-BUS a natural fit. This Kernel Event Layer was merged into the 2.6.10-rc1 kernel.

Second, the session bus provides a mechanism for IPC and remote method invocation, possibly providing a unified system between GNOME and KDE. D-BUS aims to be a better CORBA than CORBA and a better DCOP than DCOP, satisfying the needs of both projects while providing additional features.

And, D-BUS does all this while remaining simple and efficient.

How to use it

from: https://dbus.freedesktop.org/doc/dbus-tutorial.html

Message

D-Bus works by sending messages between processes. If you're using a sufficiently high-level binding, you may never work with messages directly.There are 4 message types:

  • Method call messages ask to invoke a method on an object.
  • Method return messages return the results of invoking a method.
  • Error messages return an exception caused by invoking a method.
  • Signal messages are notifications that a given signal has been emitted (that an event has occurred). You could also think of these as "event" messages.

A method call maps very simply to messages: you send a method call message, and receive either a method return message or an error message in reply.Each message has a header, including fields, and a body, including arguments. You can think of the header as the routing information for the message, and the body as the payload. Header fields might include the sender bus name, destination bus name, method or signal name, and so forth. One of the header fields is a type signature describing the values found in the body. For example, the letter "i" means "32-bit integer" so the signature "ii" means the payload has two 32-bit integers.

Calling a Method

A method call in DBus consists of two messages; a method call message sent from process A to process B, and a matching method reply message sent from process B to process A. Both the call and the reply messages are routed through the bus daemon. The caller includes a different serial number in each call message, and the reply message includes this number to allow the caller to match replies to calls.

The call message will contain any arguments to the method. The reply message may indicate an error, or may contain data returned by the method.

A method invocation in DBus happens as follows:

  1. The language binding may provide a proxy, such that invoking a method on an in-process object invokes a method on a remote object in another process. If so, the application calls a method on the proxy, and the proxy constructs a method call message to send to the remote process.
  2. For more low-level APIs, the application may construct a method call message itself, without using a proxy.
  3. In either case, the method call message contains: a bus name belonging to the remote process; the name of the method; the arguments to the method; an object path inside the remote process; and optionally the name of the interface that specifies the method.
  4. The method call message is sent to the bus daemon.
  5. The bus daemon looks at the destination bus name. If a process owns that name, the bus daemon forwards the method call to that process. Otherwise, the bus daemon creates an error message and sends it back as the reply to the method call message.
  6. The receiving process unpacks the method call message. In a simple low-level API situation, it may immediately run the method and send a method reply message to the bus daemon. When using a high-level binding API, the binding might examine the object path, interface, and method name, and convert the method call message into an invocation of a method on a native object (GObject, java.lang.Object, QObject, etc.), then convert the return value from the native method into a method reply message.
  7. The bus daemon receives the method reply message and sends it to the process that made the method call.
  8. The process that made the method call looks at the method reply and makes use of any return values included in the reply. The reply may also indicate that an error occurred. When using a binding, the method reply message may be converted into the return value of of a proxy method, or into an exception.

The bus daemon never reorders messages. That is, if you send two method call messages to the same recipient, they will be received in the order they were sent. The recipient is not required to reply to the calls in order, however; for example, it may process each method call in a separate thread, and return reply messages in an undefined order depending on when the threads complete. Method calls have a unique serial number used by the method caller to match reply messages to call messages.

Emitting a Signal

A signal in DBus consists of a single message, sent by one process to any number of other processes. That is, a signal is a unidirectional broadcast. The signal may contain arguments (a data payload), but because it is a broadcast, it never has a "return value." Contrast this with a method call (see the section called “Calling a Method - Behind the Scenes”) where the method call message has a matching method reply message.

The emitter (aka sender) of a signal has no knowledge of the signal recipients. Recipients register with the bus daemon to receive signals based on "match rules" - these rules would typically include the sender and the signal name. The bus daemon sends each signal only to recipients who have expressed interest in that signal.

A signal in DBus happens as follows:

  1. A signal message is created and sent to the bus daemon. When using the low-level API this may be done manually, with certain bindings it may be done for you by the binding when a native object emits a native signal or event.
  2. The signal message contains the name of the interface that specifies the signal; the name of the signal; the bus name of the process sending the signal; and any arguments
  3. Any process on the message bus can register "match rules" indicating which signals it is interested in. The bus has a list of registered match rules.
  4. The bus daemon examines the signal and determines which processes are interested in it. It sends the signal message to these processes.
  5. Each process receiving the signal decides what to do with it; if using a binding, the binding may choose to emit a native signal on a proxy object. If using the low-level API, the process may just look at the signal sender and name and decide what to do based on that. 

思考

  1. Dbus几乎集成在了Linux所有发行版中,作为系统级的IPC形式(甚至你没有安装X11,并且以init3模式启动,见附图),在于其作为一个管理工具,为系统间的程序提供了交互模式,且已经被各类App所接受。正如前面例子里讲的,当插入耳机,音频程序会默认根据dbus信号切换输出模式。
  2. 文中提到了通过bus daemon搭建起总线拓扑结构(a bus topology),那么,总线拓扑结构是什么样的呢?与MQ的broker结构(星型拓扑)有何区别?
    答:DBus在内部实现的依然是星型结构——daemon为所有消息的调度中心。但在外部,daemon是透明的。也就是外在表现上,DBus系统呈现的是一种总线拓扑,跟System-Signal、Event相似。
  3. 在Why DBus中,最后提到了without polling?真的吗?那为什么客户端 / 服务端编程都必须依赖一个 MainLoop 循环呢?难道不是为了同步操作吗?
  4. 深入研究“内核事件”,Linux将内核封装成了怎么样的一种对象? 
  5. SessionBus的Proxy模式分析,以及远程对象调用——是否等同于RPC?
  6. 在How to use一节讲的并不详细——对于Method Calling,服务端该怎样提供服务接口?对于Signal Emiiting,Publisher该如何发布服务,如何推送消息?Subscriber如何注册match rules?由于Signal是异步模式的,那怎样连接Signal及其相应函数?
  7. 另外,文章的目录,为何没有完整显示 How to use it 的子标题?出了什么鬼?

原文地址:https://www.cnblogs.com/brt3/p/9613346.html