深入浅出DBUS

DBUS简介

D-Bus 是 Freedesktop.org 项目开发的一种系统间消息传递机制,用于系统中不同进程间进行通信问题。

在 Linux 桌面系统中,应用程序通常需要与其他应用程序进行通信,例如播放器需要与音频设备进行通信,桌面环境需要与屏幕保护程序进行通信。传统上,应用程序间通信通常使用共享内存、信号量、管道、socket等方式进行。这些方式存在一些问题,例如:

  • 通信方式不统一,需要应用程序自行实现通信接口,增加了开发复杂度和维护成本。D-Bus基于unix socket,提供了统一的通信开发框架。该框架支持多种开发语言,可以更加便捷地实现进程间的通信。
  • 通信效率较低,影响系统性能。D-Bus 采用高效的消息传递机制,传统的RPC方案,免不了需要对数据进行序列化和反序列化操作,而D-Bus没有这一操作,减少了数据解析成本,以及序列化操作所带来的数据负载,可以有效提升系统性能。
  • 动态注册注销机制:D-Bus 采用动态注册注销机制,可以动态注册注销服务,实现服务的动态加载或卸载。

D-Bus 通信模型

  • D-Bus 通信模型
  • Method Call过程

D-Bus 基本概念

消息总线

  • 系统总线:系统级的通信通道,用于操作系统的核心部分和系统级服务。随系统启动而启动,持久的。它主要用于系统服务之间的交流,如硬件状态的变更、系统设置的更新等。
  • 会话总线:用户会话级的通信通道,用于同一用户会话中运行的应用程序。

总线名(Bus Name)

总线名是服务注册到总线的名称,总线名又分为两类:

Unique name 以:开始,Unique name是dbus-daemon分配的,类似于:1.30,技术规范明确要求其永不重复

Well Known name 为了方便人脑记忆引入的名字。为了命名不冲突,技术规范推荐使用逆转域名方式命名,例如org.freedesktop.DBus

对象(Object Path)

一个进程可以包含多个对象,D-Bus用Object path来标识对象,一般以/org/freedesktop/DBus这种形式命名。

接口(Interface)

Interface包含方法(Method)、属性(Property)和信号(Signal),以org.freedesktop.DBus这种形式命名。

方法(Method)

方法,程序可以通过D-Bus提供的机制,调用另外一个进程提供的Method,即RPC的概念

信号(Signal)

信号,D-Bus中Pub-Sub通信模型的具体实现。

属性(Property)

属性,类似于C++类中成员变量的Getter-Setter,可以定义为只读、只写、读写类型。

签名字符串(Signature)

D-Bus技术规范规定使用String类型来编码方法、信号的签名(参数)。例如i代表int32_t类型,a代表数组等等。

消息类型

连接到总线的进程可通过总线接收或传递消息,总线收到消息时,根据不同的消息类型进行不同的处理。D-Bus中消息分为四类:

  • Methodcall消息:基于D-Bus的函数调用,类似于RPC过程
  • Methodreturn消息:触发函数调用返回的结果
  • Error消息:触发的函数调用返回异常
  • Signal消息:进程间消息广播不需要响应,接收方需要向总线注册感兴趣的消息类型,当总线接收到Signal消息类型的消息时,会将消息转发至希望接收的进程。该过程类似于消息的发布订阅

D-Bus 组件

  • libdbus: 让两个应用程序可以互相链接并交换消息的函式库
  • dbus-daemon: 消息总线进程,基于libdbus,可连接到多个进程。当进程(客户端或服务)向发送 D-Bus 消息时,消息总线进程首先接收该消息并将其传递给适当的接收者。消息总线守护进程可以被视为集线器或路由器,负责通过 D-Bus 连接将每条消息重复到接收进程,从而将其发送到目的地。
  • 基于特定应用程序框架的封装函式库

D-Bus 开发

GDbus库

C应用程序通过GDbus库接入D-Bus

推荐的 D-Bus GLib API 是 GDBus,它从 2.26 版本开始随 GLib 一起分发。这里没有记录。有关如何使用 GDBus 的详细信息, 请参阅GLib 文档。
还存在一个较旧的 API dbus-glib。它已被弃用,不应在新代码中使用。只要有可能,还建议将现有代码从 dbus-glib 移植到 GDBus

gdbus是D-Bus的C语言绑定库,提供了C语言接口来访问D-Bus。gdbus可以用于开发D-Bus服务端和客户端。

gdbus的主要功能包括:

  • 创建D-Bus服务端和客户端
  • 设置和获取属性
  • 调用方法
  • 发送和接收信号

D-Bus演示

获取dbus-daemon连接信息

debug@debug-virtual-machine:~$ busctl --user
NAME                                          PID PROCESS         USER     CONNECTION    UNIT              SESSION DESCRIPTION
:1.0                                         1596 systemd         debug:1.0          user@1000.service -       -
:1.10                                        1768 tracker-miner-f debug:1.10         user@1000.service -       -
:1.11                                        1825 gvfs-udisks2-vo debug:1.11         user@1000.service -       -
:1.12                                        1830 gvfs-mtp-volume debug:1.12         user@1000.service -       -
....
....

列出某个总线的详细信息

debug@debug-virtual-machine:~/workspace/dbus/gdbus$ gdbus introspect --session --dest org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject
node /org/gtk/GDBus/TestObject {
  interface org.freedesktop.DBus.Properties {
    methods:
      Get(in  s interface_name,
          in  s property_name,
          out v value);
      GetAll(in  s interface_name,
             out a{sv} properties);
      Set(in  s interface_name,
          in  s property_name,
          in  v value);
    signals:
      PropertiesChanged(s interface_name,
                        a{sv} changed_properties,
                        as invalidated_properties);
    properties:
  };
  interface org.freedesktop.DBus.Introspectable {
    methods:
      Introspect(out s xml_data);
    signals:
    properties:
  };
  interface org.freedesktop.DBus.Peer {
    methods:
      Ping();
      GetMachineId(out s machine_uuid);
    signals:
    properties:
  };
  @org.gtk.GDBus.Annotation("OnInterface")
  @org.gtk.GDBus.Annotation("AlsoOnInterface")
  interface org.gtk.GDBus.TestInterface {
    methods:
      @org.gtk.GDBus.Annotation("OnMethod")
      Greet(in  s greeting,
            out s response);
      EmitSignal(@org.gtk.GDBus.Annotation("OnArg")
                 in  d signal);
    signals:
      @org.gtk.GDBus.Annotation("Onsignal")
      VelocityChanged(d signal);
    properties:
      readwrite s Name = 'DeLorean';
  };
};

debug@debug-virtual-machine:~$ busctl --user call org.gtk.GDBus.TestServer /org/gtk/GDBus/TestObject org.freedesktop.DBus.Introspectable Introspect

调用总线方法

debug@debug-virtual-machine:~/workspace/dbus/gdbus$ gdbus call  --dest org.gtk.GDBus.TestServer  --session --object-path /org/gtk/GDBus/TestObject --method org.gtk.GDBus.TestInterface.Greet hello
('Hi',)

查看对象属性

debug@debug-virtual-machine:~/workspace/dbus/gdbus$ gdbus call --session --dest  org.gtk.GDBus.TestServer --object-path /org/gtk/GDBus/TestObject --method org.freedesktop.DBus.Properties.Get "org.gtk.GDBus.TestInterface" "Name"
(<'DeLorean'>,)

debug@debug-virtual-machine:~/workspace/dbus/gdbus$ busctl --user get-property org.gtk.GDBus.TestServer /org/gtk/GDBus/TestObject org.gtk.GDBus.TestInterface Name
s "DeLorean"

设置对象属性

debug@debug-virtual-machine:~/workspace/dbus/gdbus$ busctl --user set-property org.gtk.GDBus.TestServer /org/gtk/GDBus/TestObject "org.gtk.GDBus.TestInterface" "Name" s debug

监听信号

debug@debug-virtual-machine:~/workspace/dbus/gdbus$ dbus-monitor "type='signal',interface='org.gtk.GDBus.TestInterface'"
signal time=1703148559.468084 sender=org.freedesktop.DBus -> destination=:1.129 serial=2 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
   string ":1.129"
signal time=1703148559.468127 sender=org.freedesktop.DBus -> destination=:1.129 serial=4 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameLost
   string ":1.129"
signal time=1703148660.406897 sender=:1.115 -> destination=(null destination) serial=37 path=/org/gtk/GDBus/TestObject; interface=org.gtk.GDBus.TestInterface; member=VelocityChanged
   double 1
signal time=1703148670.606249 sender=:1.115 -> destination=(null destination) serial=40 path=/org/gtk/GDBus/TestObject; interface=org.gtk.GDBus.TestInterface; member=VelocityChanged
   double 1

其他

如何理解总线、总线名等基础等概念之间的关系

通过一个Web做对比

https://github.com/freedesktop/dbus/commits/master?after=13e7b14e195e60b6a068166eb8872fa56c6328de+34&branch=master&qualified_name=refs%2Fheads%2Fmaster

D-BusWeb类比内容
Bus因特网、专网因特网
Bus name (Unique)IP地址20.205.243.166
Bus name (Well known)域名GitHub: Let’s build from here · GitHub
Object pathURL路径/freedesktop/dbus/commits/
InterfaceURLmaster
MethodURLmaster
Signature参数after=13e7b14e195..

dbusremote

DBusRemote

dbus指南

https://web.archive.org/web/20150828095257/http://dbus.freedesktop.org/doc/dbus-tutorial.html#objects

GLib APIs
The recommended GLib API for D-Bus is GDBus, which has been distributed with GLib since version 2.26. It is not documented here. See the GLib documentation for details of how to use GDBus.

An older API, dbus-glib, also exists. It is deprecated and should not be used in new code. Whenever possible, porting existing code from dbus-glib to GDBus is also recommended.

推荐的 D-Bus GLib API 是 GDBus,它从 2.26 版本开始随 GLib 一起分发。这里没有记录。有关如何使用 GDBus 的详细信息, 请参阅GLib 文档。
还存在一个较旧的 API dbus-glib。它已被弃用,不应在新代码中使用。只要有可能,还建议将现有代码从 dbus-glib 移植到 GDBus

wiki

https://en.wikipedia.org/wiki/D-Bus

gdbus开发指南

https://web.archive.org/web/20150905104029/https://developer.gnome.org/gio/stable/gdbus-convenience.html

Glib库源码

GitHub - GNOME/glib: Read-only mirror of https://gitlab.gnome.org/GNOME/glib

gdbus工具使用

gdbus(1) — Arch manual pages

gdbus-codegen

Ubuntu Manpage: gdbus-codegen - D-Bus code and documentation generator

dbus设计指南

D-Bus API设计指南(译文) - CodeAntenna

结束

  • 24
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值