Qt/QML应用编程最适合于一些触摸的嵌入式界面设计,那么GUI界面怎么与底层的设备通信,怎么与一个系统内其他模块通信的呢?这就不得不说一个很重要的设计模式:d-bus。
D-BUS是一个系统中消息总线,用于IPC/RPC。消息系统很简单而功能强大,可以在一些命令行实用程序的帮助下进行操作(dbus-send)-以及一些GUI工具(qdbusviewer)。
D-BUS是一个进程间通信(IPC)系统,提供了一种简单而强大的机制允许应用程序彼此交谈、通信信息和请求服务。D-BUS是从头开始设计,以满足现代Linux系统的需求。D-BUS的最初目标是成为分别用于GNOME和KDE的远程对象系统CORBA和DCOP的替代。理想情况下,D-BUS可以成为两台台式机使用的统一且不可知的IPC机制,满足它们需求和引入新功能。D-BUS作为一个功能齐全的工控机和对象系统,有几个预期用途。首先,D-BUS可以执行基本功能应用程序IPC,允许一个进程将数据传送到另一个进程。想想类固醇上的UNIX域套接字。其次,D-BUS可以方便地通过系统发送事件或信号,允许不同的组件在系统中进行通信并最终更好地集成。例如,蓝牙dæmon可以发送音乐播放器可以截获的来电信号,在通话结束前将音量静音。最后D-BUS实现了一个远程对象系统,允许一个应用程序从另一个对象认为CORBA没有复杂性。
D-Bus真正的强大之处在于它丰富的编程接口。D-Bus本身是在C中实现的,因此低级别的C API是D-Bus的“自然”API。然而,事实确实如此使用起来相当麻烦,因此存在用于D总线的各种绑定。
为何D-Bus是独特的?Why D-BUS Is Unique
D-BUS在几个方面与其他IPC机制不同。首先,D-BUS中IPC的基本单元是消息,而不是字节流。通过这种方式,D-BUS将IPC分解为离散的消息,包括报头(元数据)和有效载荷(数据)。消息格式为二进制、类型化、完全对齐且简单。它是有线协议的固有部分。这种方法与其他IPC机制形成对比,其中franca是一个随机的字节流,而不是一个离散的消息。其次,D-BUS是基于总线的。最简单的沟通方式是过程对过程。然而D-BUS,提供了一个dæmon,称为消息总线守护进程,用于在特定的进程之间路由消息总线通过这种方式,形成了总线拓扑,允许进程在同时处理一个或多个应用。应用程序可以向总线发送或侦听总线上的各种事件。最后一个独特的功能是创建两条总线,而不是一条,即系统总线和会话总线。系统总线是全局的、系统范围的,并且在系统级别运行。系统的所有用户都可以进行通信通过具有适当权限的总线,允许系统范围事件的概念。会话总线,但是,它是在用户登录期间创建的,并在用户或会话级别运行。此总线仅由特定用户,在特定登录会话中,作为用户应用程序的IPC和远程对象系统。
D-BUS概念
消息被发送到对象。对象使用路径名进行寻址,例如.org/cups/printers/queue。消息总线上的进程与对象相关联,并在该对象上实现接口。D-BUS支持多种消息类型,如信号、方法调用、方法返回和错误消息。信号是特定事件发生的通知。它们是简单的、异步的、单向的抬头信息。方法调用消息允许应用程序请求调用远程上的方法对象方法返回消息提供方法调用产生的返回值。错误消息提供异常以响应方法调用。D-BUS是完全类型化和类型安全的。消息的标头和有效负载都是完全类型化的。有效类型包括字节,布尔值,32位整数,32位无符号整数,64位整数,64比特无符号整数,双精度浮点和字符串。特殊的数组类型允许对类型进行分组。格言类型允许使用字典样式的键/值对。D-BUS是安全的。它实现了一个基于SASL配置文件的简单协议,用于一对一身份验证连接。在总线范围内,从特定接口读取和写入消息是由安全系统控制。管理员可以控制对总线上任何接口的访问。D总线dæmon从头开始写的时候就考虑到了安全性。
为何D-BUS?
这些概念很有说服力,但有什么好处呢?首先,全系统消息总线是一个新概念。整个系统共享的单个总线允许从内核传播事件到系统上最上面的应用程序。Linux,具有定义良好的接口和层的清晰分离,不是很集成。D-BUS的系统消息总线改进了集成,而无需损害了良好的工程实践。现在,磁盘已满和打印机队列为空等事件甚至电池电量低可能会使系统堆栈膨胀,可用于任何需要的应用程序,从而允许系统进行响应和反应。事件是异步发送的,并且不进行轮询。
内核事件层。
内核事件层是一种使用高速网络链接套接字的内核到用户通信机制以与用户空间异步通信。该机制可以连接到D-BUS,允许内核发送D-BUS信号!内核事件层与sysfs绑定,sysfs是现代Linux系统上位于/sys的kobjects树。每个sysfs中的目录与kobject绑定,kobject是内核中用于表示对象的结构;sysfs是一个导出为文件系统的对象层次结构。每个内核事件层事件都被建模为好像它源自一个sysfs路径。因此,事件出现了就好像它们从kobjects发射一样。sysfs路径很容易转换为D-BUS路径,使内核事件层和D-BUS自然搭配。这个内核事件层被合并到2.6.10-rc1内核中。其次,会话总线为IPC和远程方法调用提供了一种机制,可能会提供GNOME和KDE之间的统一系统。D-BUS旨在成为一个比CORBA更好的CORBADCOP而非DCOP,在提供附加功能的同时满足了两个项目的需求。而且,D-BUS在保持简单高效的同时完成了所有这些。
如何在应用中增加D-Bus?
核心的D-BUS API是用C语言编写的,是相当低级和大型的。在此API之上,绑定与编程语言和环境,包括Glib、Python、Qt和Mono。除了提供语言包装器,绑定提供特定于环境的功能。例如,Glib绑定处理D-BUS连接作为GObjects,并允许消息传递集成到Glib主循环中。首选用途D-BUS肯定在使用特定于语言和环境的绑定,这既是为了方便使用,也是为了改进功能。
Qt中,要使用QtDBus模块,需要#include
当需要多对多通信时,使用Dbus。为了实现这一点,在任何应用程序连接到总线之前,都会启动一个中央服务器:该服务器负责跟踪连接的应用程序,并将消息从源正确路由到目的地。此外,D-Bus定义了两个众所周知的总线,称为系统总线和会话总线。这些总线的特殊之处在于它们具有定义明确的语义:一些服务被定义为在其中一条或两条总线中找到。例如,希望查询连接到计算机的硬件设备列表的应用程序可能会与系统总线上可用的服务通信,而提供打开用户网络浏览器的服务可能会在会话总线上找到。在系统总线上,人们还可以期望发现每个应用程序可以提供哪些服务的限制。因此,可以合理地确定,如果某个服务存在,那么它是由受信任的应用程序提供的。
Messages信息
在底层,应用程序通过Dbus相互发送消息进行通信。消息用于中继远程过程调用以及与之相关的回复和错误。当在bus上使用时,消息有一个目的地,这意味着它们只发送给感兴趣的各方,避免了由于“拥挤”或广播而造成的拥堵。然而,一种被称为“Signal message信号消息”的特殊消息(基于Qt的信号和插槽机制的概念)没有预定义的目的地。由于其目的是在一对多上下文中使用,因此信号消息被设计为通过“选择加入”机制工作。QtD-Bus模块将消息的低级概念完全封装到Qt开发人员熟悉的更简单、面向对象的方法中。在大多数情况下,开发人员不必担心发送或接收消息。
Services Name服务名称
当通过总线进行通信时,应用程序会获得所谓的“服务名称”:即该应用程序如何选择被同一总线上的其他应用程序所知。服务名称由D-Bus总线守护进程代理,用于将消息从一个应用程序路由到另一个应用。与服务名称类似的概念是IP地址和主机名:根据计算机向网络提供的服务,计算机通常有一个IP地址,并且可能有一个或多个与其相关的主机名。另一方面,如果不使用总线,也不会使用服务名称。如果我们再次将其与计算机网络进行比较,这将等同于点对点网络:由于对等端是已知的,因此不需要使用主机名来查找它或其IP地址。D-Bus服务名称的格式实际上与主机名非常相似:它是一个由字母和数字点分隔的序列。通常的做法甚至是根据定义服务的组织的域名来命名服务名称。比如org.freedesktop.DBus
Object Path对象路径
与网络主机一样,应用程序通过导出对象向其他应用程序提供特定服务。这些对象是分层组织的,很像从QObject派生的类所拥有的父子关系。然而,一个不同之处在于,存在“根对象”的概念,即所有对象都有最终的父对象。如果我们继续与Web服务进行类比,对象路径等同于URL的路径部分:
与它们一样,D-Bus中的对象路径的形式类似于文件系统上的路径名:它们是斜杠分隔的标签,每个标签由字母、数字和下划线(“_”)组成。它们必须始终以斜线开头,而不能以斜线结尾。
Interface接口
接口类似于C++抽象类和Java的interface关键字,并声明调用者和被调用者之间建立的“契约”。也就是说,它们建立了可用的方法method、信号signal和属性property的名称,以及建立通信时双方期望的行为。Qt在其插件系统中使用了一种非常类似的机制:C++中的基类通过Q_DECLARE_INTERFACE()宏与一个唯一的标识符相关联。事实上,D-Bus接口名称的命名方式与Qt插件系统所建议的类似:一个通常由定义该接口的实体的域名构建的标识符。
Dbus支持3种复合类型:ARRAY、STRUCT和 maps/dictionaries,两种非原生类型:QStringList和QByteArray,还有BYTE、INY16等原生类型。如果自定义类性须使用Q_DECLARE_METATYPE()声明为Qt元类型,使用qDBusRegisterMetaType()函数注册,流操作符会被注册系统自动找到。
QDBusMessage类:D-Bus总线发送或接收的一个消息,有MethodCallMessage、 SignalMessage、 ReplyMessage、 ErrorMessage等
QDBusConnection类:到D-Bus总线的一个连接,是一个D-Bus会话的起始点。通过QDBusConnection连接对象,可以访问远程对象、接口,连接远程信号到本地槽函数,注册对象等。connectToBus()函数会创建一个到总线服务端的连接,QDBusConnection::send()函数发送消息。QDBusPendingCall asyncCall(const QDBusMessage & message, int timeout = -1)const返回一个打开到system总线的QDBusConnection对象,QDBusMessage call(const QDBusMessage & message, QDBus::CallMode mode = QDBus::Block, int timeout = -1 ) const发送message消息到连接,并立即返回,bool registerService(const QString & serviceName)注册object对象到路径path。
QDBusInterface远程对象接口的代理,连接到远程对象导出的信号,获取/设置远程属性的值。QObject::connect()连接,调用QObject::property()、QObject::setProperty()对属性进行访问。
QDBusReply存储对远程对象的方法调用的应答,是方法调用的应答QDBusMessage对象的一个子集.
QDBusAbstractAdaptor用于使用D-Bus向外部提供接口,使用信号、槽、属性来决定哪些被暴露到bus总线。
QDBusAbstractInterface允许访问远程接口的所有D-Bus接口的基类。
QDBusConnectionInterface访问D-Bus总线服务。
Inter-Process Communication (IPC)就是进程间通信。
How D-bus is working?
system bus有点像system service系统服务,使用的话直接调用就好了。而session bus会话总线也是类似service的方式提供接口服务。
D-Bus/Objects对象:
D-Bus/Interfaces接口:
D-Bus/Properties属性:
D-Bus/Method方法:
D-Bus/signal信号:
D-Bus/Policy策略:
Tools工具:
结论: