dbus的如何使用教程以及相关概念

发布时间:2026/7/6 3:48:41

dbus的如何使用教程以及相关概念 从零到精通D-Bus 进程间通信完全指南与 Python 实战另外的文章:https://blog.csdn.net/qq_46506007/article/details/162607617很多 Linux 开发者在接触桌面应用或系统底层开发时都会遇到一个绕不开的名词——D-Bus。它的概念繁多且官方文档晦涩难懂。本文将摒弃那些复杂的官方术语带你从宏观架构到代码实战彻底打通 D-Bus 的任督二脉模块一初识 D-Bus1. 什么是 D-Bus简单来说D-Bus 是 Linux 系统上的**“快递公司”专门负责进程间通信 (IPC)**。当程序 A 想调用程序 B 的函数或者程序 C 想监听系统如电量过低、U盘插入的广播时就需要通过 D-Bus 来传递消息。2. 两个平行的“宇宙” (总线类型)D-Bus 的总线分为两类互不干扰System Bus (系统总线)全局唯一。负责系统级事件比如网络状态变化、硬件热插拔。权限控制极严。Session Bus (会话总线)跟随用户的桌面登录而启动。专门用于当前用户的各种图形软件之间互相通信如音乐播放器控制、系统托盘提示。模块二D-Bus 世界的 5 大命名铁律在编写代码前必须先搞懂 D-Bus 的坐标系。D-Bus 里一共只有 5 种东西需要命名它们有着极其死板的底层校验规则Bus Name (服务名/总线名)如com.example.MultiService作用程序在 D-Bus 上注册的“商标”或者叫“门牌号”。规则必须有至少一个.(如 a.b)只能由字母、数字、下划线和-组成段落不能以数字开头。Object Path (对象路径)如/com/example/Manager作用服务内部实体的“URL 地址”。规则必须以/开头只能包含字母、数字和_绝不能包含点或连字符不允许连续的//除非根目录否则不能以/结尾。Interface Name (接口名)如com.example.Manager.Data作用对象身上挂载的功能集合的名称类似于 Java 的 Interface。规则必须有至少一个.只能由字母、数字和_组成不能包含-段落不能以数字开头。Member Name (成员名)如StartSystem或OnStatusChanged作用接口里具体的方法名、属性名或信号名。规则绝对不能包含.不能以数字开头。Error Name (错误名)如org.freedesktop.DBus.Error.InvalidArgs作用方法调用失败时抛出的标准异常名称。规则与接口名 (Interface Name) 的规则完全一致。注:真正关注的内容是Interface Name 接口名字。因为接口下面可以存放真正工作的内容比如1. 方法(函数),2. 属性名(存放值就是变量的意思)3. 信号 (和函数一样调用函数的形式但是无返回值连接了该信号的的地方信号触发之后。连接的地方也会收到信息就好像被调用函数了一样)。那么总线名字和路径对象有什么用个人给的建议。就是一个字符串xxxx 的作用。但是在字符串的基础之上限制你字符串要有一定的格式不能乱写。不然busctl tree 命令行 无法遍历出来东西。然后一层套一层。比如总线名字只能一个表示你的进程。总线下面可以有很多的路径对象(看起来像文件夹一样)。你可以/com/example/a 直接这样写。不强制/ 和 “/com” 和 “/com/example” 都一定存在才能有 “/com/example/a” 。 所以我才说就是个字符串的作用。在基础之上对字符串的内容在代码里面进行限制。总结总线名字下面有多个路径对象。 路径对象下面有多个接口。接口下面有很多的方法属性值和信号。模块三3 大核心交互动作 (报文类型)在 D-Bus 的网络里所有数据的流动都跑不出以下三种常规业务行为和一种异常处理Method Call (方法调用)客户端请求服务端执行某个函数并等待返回结果或Error错误报文。Property (属性读写)通过底层的org.freedesktop.DBus.Properties接口去 Get(读取) 或 Set(修改) 服务端的内部状态变量。Signal (信号广播)服务端主动向总线上发送事件所有connect订阅了该信号的客户端会瞬间触发回调函数。模块四实战演练 (上) —— 编写全功能服务端在 Linux 下最优雅的入门方式是使用 Python 的pydbus库。我们来编写一个包含两个不同对象路径Manager 和 Worker的服务端。#!/usr/bin/env python3# server.pyfrompydbusimportSessionBusfrompydbus.genericimportsignalfromgi.repositoryimportGLibclassWorkerObject: 通过编写这段 XML 字符串 (Introspection XML) 我们向外界宣告这个对象拥有的接口、方法(method)、属性(property)和信号(signal)。 dbus node interface namecom.example.Worker.Job method nameCancelJob arg types namejob_name directionin/ arg typeb nameresult directionout/ /method property nameActiveJobs typei accessread/ signal nameJobFailedarg types namejob_name//signal /interface /node # 定义信号发射器JobFailedsignal()def__init__(self):self._active_jobs0# 实现 XML 中声明的方法defCancelJob(self,name):ifself._active_jobs0:# 【高阶技巧】主动抛出异常底层会自动将其打包为 D-Bus Error 弹给客户端raiseValueError(com.example.Worker.Error.NoJobs: 没有任务可以取消)self._active_jobs-1# 【高阶技巧】像调用普通函数一样直接发射信号广播self.JobFailed(name)returnTrue# 实现 XML 中声明的属性propertydefActiveJobs(self):returnself._active_jobsif__name____main__:busSessionBus()# 1. 注册服务名 (Bus Name)bus.request_name(com.example.MultiService)# 2. 挂载对象到指定的对象路径 (Object Path)bus.register_object(/com/example/Worker,WorkerObject(),None)print(D-Bus Server 正在运行...)loopGLib.MainLoop()loop.run()模块五实战演练 (下) —— 客户端调用与测试服务端跑起来后我们可以使用不同的姿势来调用它。姿势 1使用 Python 客户端极其优雅一切都被pydbus包装成了原生的对象操作#!/usr/bin/env python3# client.pyfrompydbusimportSessionBusfromgi.repositoryimportGLib busSessionBus()# 获取服务及对象路径workerbus.get(com.example.MultiService,/com/example/Worker)# 1. 读取属性print(当前任务数:,worker.ActiveJobs)# 2. 订阅信号defon_failed(job_name):print(f收到广播任务{job_name}失败)worker.JobFailed.connect(on_failed)# 3. 触发异常测试try:worker.CancelJob(Task1)exceptExceptionase:print(捕获到了 D-Bus Error:,e)姿势 2使用 Linux 神器busctl进行命令行排错在没有写客户端代码时busctl是你最好的朋友。查看你的服务长什么样 (Tree)busctl--usertree com.example.MultiService命令行强行调用方法 (遇到异常会被红字砸脸)busctl--usercall com.example.MultiService /com/example/Worker com.example.Worker.Job CancelJob sTask1开启全局监听抓取网线上的每一条 D-Bus 报文busctl--usermonitor com.example.MultiService模块六硬核原理解密 —— D-Spy 是怎么工作的如果你用过 GNOME 官方的 D-Bus 调试软件D-Spy你会惊叹于它是怎么把你的接口像文件夹一样一层一层列出来的。秘密在于 D-Bus 两个内置的系统接口服务发现 (org.freedesktop.DBus)D-Spy 刚启动时会去请求操作系统的总线管家调用其ListNames方法从而获取当前注册的所有服务名如com.example.MultiService。树形爬虫 (org.freedesktop.DBus.Introspectable)D-Spy 会从根目录/开始一层一层向下调用Introspect()方法。即使你只写了/com/example/Worker的代码底层的 C 库GLib也会非常聪明地为你补齐父目录的 XML 返回值比如返回node nameexample/D-Spy 根据这些 XML 就能自动画出整个树状图附注为什么不用 C 语言手写服务端在 Python 中只要 50 行就能搞定的服务端在 C 语言中如果纯手写各种路由、序列化、VTable 回调将会多达千行代码且极其折磨。工业界的标准做法是编写 XML 接口描述文件然后使用gdbus-codegen工具一键生成底层的 C 语言胶水代码开发者只负责填入核心业务逻辑。本文基于对 pydbus 和 GLib 的底层源码深度实战测试总结而成希望能帮助你彻底征服 D-Bus

相关新闻