Dbus

2 篇文章 0 订阅
1 篇文章 0 订阅

DBus (Desktop Bus) 是一种 IPC 机制, 由 freedesktop.org 项目提供,用于进程间通信进程与内核的通信
DBus支持单对单和多对多的对等通信,在多对多的通讯是,作为后台进程角色去分转消息:当一个进程发送消息给另一个进程时,先发送消息到后台进程(Bus Daemon Process是运行在linux系统中的一个后台守护进程,dbus-daemon运行时会调用libdus的库),在通过后台进程(dbus-daemon)将消息转发到目的进程,DBus后台进程充当一个路由的角色

tips: IPC机制还有 fifo管道, share memory, semaphore, message queue, socket…
官网

DBus中主要概念为总线,连接到总线的进程可通过总线接受或传递消息,总线接受到消息时,根据不同的消息类型进行不同的处理。DBus消息分为四类:

  1. Method Call 消息: 将出发一个函数调用
  2. Method Retrun 消息: 触发函数调用返回的结果
  3. Error 消息: 触发的函数调用返回一个异常
  4. Signal 消息: 通知,可以看作为事件消息

1. 介绍

1.1. DBus应用场景

根据DBUS消息类型可知,DBUS提供一种高效的进程间通信机制,主要用于进程间函数调用以及进程间信号广播:

  1. 函数调用
    DBUS可以实现进程间函数调用,进程A发送函数调用的请求(Methodcall消息),经过总线转发至进程B。进程B将应答函数返回值(Method return消息)或者错误消息(Error消息)
  2. 消息广播
    进程间消息广播(Signal消息)不需要响应,接收方需要向总线注册感兴趣的消息类型,当总线接收到“Signal消息”类型的消息时,会将消息转发至希望接收的进程

1.2. DBus通信特点

低延迟:

DBus一开始就是用来设计成避免来回传递允许异步操作的。因此虽然在Application和Daemon之间是通过socket实现的,但是又去掉了socket的循环等待,保证了操作的实时高效。

低开销:

DBus使用一个二进制的协议,不需要转化成像XML这样的文本格式。因为DBus是主要用来进行机器内部的IPC,而不是为了网络上的IPC机制而准备的.所以它才能够在本机内部达到最优效果。

高可用性:

DBus是基于消息机制而不是字节流机制。它能自动管理一大堆困难的IPC问题。同样的,DBus库被设计来让程序员能够使用他们已经写好的代码。而不会让他们放弃已经写好的代码,被迫通过学习新的IPC机制来根据新的IPC特性重写这些代码

2. 技术实现

2.1 原理

DBUS是一种高级的IPC通信机制,通信流程下图所示。在DBUS通信过程中,存在一个后台进程(BUS Daemon Process)。后台进程和普通进程间信息交互是通过域套接字进行通信

在这里插入图片描述

  1. Process1 需先连接到总线(dbus_bus_get),其次构造消息(dbus_message_new_signal),然后发送消息(dbus_connection_send)到后台进程。后台进程接收消息,然后根据消息类型对消息进行不同处理(bus_dispatch_matches)
  2. Process2 接收消息前需要连接到总线,并告知总线自己希望得到的消息类型(dbus_bus_add_match),然后等待接收消息(dbus_connection_pop_message)。进程2(Process2)收到总线转发的消息时会根据消息类型,做不同的处理(若是信号类型则不需要发送返回值给总线)

2.2 连接到总线

进程间通信前,需要连接到总线。调用dbus_bus_get函数连接进程到总线,建立进程和总线之间的连接(DBusConnection)。建立连接后,需要为这个连接注册名称,方便后面对这个连接进行操作,调用dbus_bus_request_name函数对连接进行注册名称。
建立连接和注册名称是在程序开始时执行,程序结束时,调用dbus_connection_close函数关闭一个连接。函数接口声明如下所示。

注册名称和关闭连接:

/*  建立和总线的连接  */  
DBusConnection  *dbus_bus_get  (DBusBusType  type,  DBusError   *error)             
 
 /*  注册连接名称      */    
int  dbus_bus_request_name  (DBusConnection   *connection,  
                             const char         *name,  
                             unsigned int        flags,  
                             DBusError        *error)                                  

 /*  关闭连接          */   
void  dbus_connection_close  (DBusConnection  *connection)                          

2.3 信号发送与接收

2.3.1 信号发送

DBUS中信号是一种广播的消息,当发出一个信号,所有连接到 DBUS 总线上并注册了接受对应信号的进程,都会收到该信号。
进程发出一个信号前,需要创建一个 DBusMessage 对象来代表信号,然后追加上一些需要发出的参数,就可以发向总线了。发完之后需要释放消息对象。信号发送的函数声明如下所示。
信号发送接口:

 /*  创建信号类型消息      */  
DBusMessage  *dbus_message_new_signal  (const  char  *path,  
                                       const  char  *iface,  
                                       const  char  *name)                      

/*  加入参数到信号        */  
void  dbus_message_iter_init_append  ( DBusMessage     *message,  
                           DBusMessageIter  *iter)                

/*  发送信号到总线        */  
dbus_bool_t  dbus_connection_send  ( DBusConnection  *connection,  
                                     DBusMessage    *message,  
                                     dbus_uint32_t    *serial)                       
 
/*  释放消息              */    
void  dbus_message_unref  (DBusMessage *message)                                 

2.3.2 信号接收

进程接收信号时,需先告知总线进程感兴趣的消息,然后等待接收消息。信号接收函数声明如下所示。
信号接收接口

/*  告知总线感兴趣的消息   */  
void  dbus_bus_add_match  ( DBusConnection  *connection,  
                            const char        *rule,  
                            DBusError       *error)                                 
  
/*  接收消息               */    
DBusMessage  *dbus_connection_pop_message  ( DBusConnection  *connection)         
 
/*  判断消息是否为信号     */  
dbus_bool_t  dbus_message_is_signal  (DBusMessage  *message,  
                                      const char      *iface,  
                                      const char     *signal_name)                      

2.4 函数调用和接收函数调用

2.4.1 函数调用

调用一个远程函数与发送一个信号原理类似,需要先创建一个消息(DBusMessage),然后通过注册在 DBUS上的名称指定发送的对象。然后追加相应的参数,调用方法分为两种,一种是阻塞式的,另一种为异步调用。异步调用的时候会得到一个“DBusMessage *” 类型的返回消息,从这个返回消息中可以获取一些返回的参数
函数调用接口:

/*  创建一个函数调用消息    */ 
DBusMessage  *dbus_message_new_method_call  (const char  *destination,  
                                             const char  *path,  
                                             const char  *iface,  
                                             const char  *method)                     
  
/*  为消息添加参数           */    
void  dbus_message_iter_init_append  (
							DBusMessage     *message,  
                         	DBusMessageIter  *iter)                     
  
/*  发送消息                */  
dbus_bool_t  dbus_connection_send_with_reply  (
							DBusConnection   *connection,  
                            DBusMessage      *message,   
                            DBusPendingCall  **pending_return,  
                            int            timeout_milliseconds)       
  
/*  阻塞等待返回值           */ 
void  dbus_pending_call_block  (DBusPendingCall  *pending)                            
  
/*  获得返回消息            */ 
DBusMessage  *dbus_pending_call_steal_reply  (DBusPendingCall  *pending)              
    
/*  获取参数                */  
dbus_bool_t  dbus_message_iter_init  (
					DBusMessage     *message,  
                    DBusMessageIter  *iter)                     

2.4.2 接收函数调用

提供远程函数调用,首先需告知总线进程感兴趣的消息,其次从总线获取消息并判定消息是方法调用。然后从消息中获取参数进行函数执行,最后创建返回消息,并发送消息至总线,由总线转发至调用的进程。
接收函数调用接口:

/*  请求获取调用消息       */  
void  dbus_bus_add_match  ( DBusConnection  *connection,  
                            const char        *rule,  
                            DBusError       *error)                                   
   
/*  从总线获取消息         */  
DBusMessage  *dbus_connection_pop_message  ( DBusConnection  *connection)           
  
/*  判定消息是方法调用     */ 
dbus_bool_t  dbus_message_is_method_call (DBusMessage  *message,  
                                          const char     *iface,  
                                          const char     *method)                        
    
/*  获取参数               */  
dbus_bool_t  dbus_message_iter_init  (
						DBusMessage     *message,  
                        DBusMessageIter  *iter)                    
  
/*  创建返回消息           */ 
DBusMessage  *dbus_message_new_method_return  (DBusMessage *method_call)             
  
/*  在消息中填入参数       */  
void  dbus_message_iter_init_append  ( 
						DBusMessage     *message,  
                        DBusMessageIter  *iter)                   
  
/*  发送返回消息          */ 
dbus_bool_t  dbus_connection_send  ( DBusConnection   *connection,  
                                     DBusMessage     *message,  
                                     dbus_uint32_t     *serial)                        

2.5 DBus常见:

native object

所有使用D-BUS的应用程序都包含一些对象, 当经由一个D-BUS连接收到一条消息时,该消息是被发往一个对象而不是整个应用程序。在开发中程序框架定义着这样的对象,例如JAVA,GObject,QObject等等,在D-Bus中成为native object

object path

对于底层的D-Bus协议,即libdbus API,并不理会这些native object,它们使用的是一个叫做object path的概念。通过object path,高层编程可以为对象实例进行命名,并允许远程应用引用它们。这些名字看起来像是文件系统路径,例如一个对象可能叫做“/org/kde/kspread/sheets/3/cells/4/5”。易读的路径名是受鼓励的做法,但也允许使用诸如“/com/mycompany/c5yo817y0c1y1c5b”等,只要它可以为你的应用程序所用。Namespacing的对象路径以开发者所有的域名开始(如 /org/kde)以避免系统不同代码模块互相干扰

DBus Name

一个应用创建对象实例进行D-Bus的通信,这些对象实例都有一个名字,命名方式类似于路径,例如/com/mycompany,这个名字在全局(session或者system)是唯一的,用于消息的路由,相当于进程的 ID

Interface

每一个对象支持一个或者多个接口,接口是一组方法和信号,接口定义一个对象实体的类型。D-Bus对接口的命名方式,类似org.freedesktop.Introspectable。开发人员通常将使用编程语言类的的名字作为接口名字

主要是用来为更高一层的框架使用方面而设定的。在 C API 这一层,你几乎可以无视这个概念,只需要知道这个一个“字符串”,并在消息匹配是被 DBus 使用到,会随着消息在不同的进程之前传递,从进程 A 发送一个消息或是数据到进程 B 时,其中必定会带有一个部分就是这个字符串,至于 B 进程怎么用(或是无视它)都可以。它的命名规则与 DBus Name 几乎是一样的,只有一点要注意,interface name 中不能带有“-”字符

member name

相当于 Message type. Member 包含两种类型,一种是 Signal,一种是 Method。在大多数方面,他们几乎是一样的,除了两点:

  1. Signal是在总线中进行广播的,而Method是指定发给某个进程的
  2. Signal 不会有返回,而 Method 一定会有返回(同步的或是异步的)
    Member name的命名规则是这样的:
    只能包含"[A-Z][a-z][0-9]_"这些字符,且不能以数字开头。不能包含“.”。
    不能超过255个字符
    以 C API 的层面来看,Member name最大的作用就是在两个进程间共享“发出的消息的类型信息”。DBus 只能以 Signal / Method 来进行消息通信,这两种方式都允许在消息发出之前,在消息中 append 各种类型的数据,当通信的对方收到消息后,它就可以通过 Signal / Method 的名称知道如何把各种数据再解析出来

3. 其他

底层接口层

主要通过 libdbus 库给与系统使用DBus 的能力

总线层

Message bus daemon 这个总线守护进程提供, 在 Linux 系统启动时运行, 负责进程间的消息路由和传递, 其中包括 Linux 内核和 Linux 桌面环境的消息传递. 总线守护进程可同时与多个引用程序相连, 并能把来自一个引用程序的消息路由到多个其他程序

应用封装层

通过一些列基于特定应用程序框架将 DBus 的底层接口封装成有好的 Wrapper 库, 供不同的人软使用
DBus官方主页,提供了大部分编程语言的DBus库版本, 比如libdbus-glib, libdbus-python

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值