D-Bus 完整教程 / 02 - D-Bus 架构详解
第 02 章:D-Bus 架构详解
2.1 整体架构
D-Bus 的架构可以分为四层:
┌─────────────────────────────────────────────────────┐
│ 应用程序(使用 D-Bus 绑定库) │ ← 第 4 层:应用层
├─────────────────────────────────────────────────────┤
│ D-Bus 绑定库(GLib / Qt / sd-bus / python) │ ← 第 3 层:绑定层
├─────────────────────────────────────────────────────┤
│ D-Bus 守护进程 / Broker │ ← 第 2 层:路由层
│ (dbus-daemon / dbus-broker) │
├─────────────────────────────────────────────────────┤
│ Unix Domain Socket / TCP Socket │ ← 第 1 层:传输层
└─────────────────────────────────────────────────────┘
| 层级 | 组件 | 职责 |
|---|---|---|
| 传输层 | Unix Socket / TCP | 原始字节传输 |
| 路由层 | dbus-daemon / dbus-broker | 消息路由、策略检查、服务激活 |
| 绑定层 | GLib、Qt、sd-bus、dbus-python | 提供高级语言 API |
| 应用层 | 用户程序 | 定义接口、发送/接收消息 |
2.2 两种总线
2.2.1 System Bus(系统总线)
System Bus 是系统范围的总线,通常只有一个实例,以 root 或专门的 dbus 用户运行。
# 查看 System Bus 地址
echo $DBUS_SYSTEM_BUS_ADDRESS
# 通常为 unix:path=/var/run/dbus/system_bus_socket
# 查看进程
ps aux | grep 'dbus-daemon.*system'
特点:
- 需要 root 权限或策略允许才能连接
- 承载系统级服务:
systemd、NetworkManager、udisks2、bluetooth - 策略配置文件:
/etc/dbus-1/system.d/或/usr/share/dbus-1/system.d/
2.2.2 Session Bus(会话总线)
Session Bus 是每个用户会话的总线,每个登录用户(甚至每个桌面会话)拥有独立的实例。
# 查看 Session Bus 地址
echo $DBUS_SESSION_BUS_ADDRESS
# 通常为 unix:path=/run/user/1000/bus
# 当 systemd 管理时
echo $DBUS_SESSION_BUS_ADDRESS
# unix:path=/run/user/1000/bus (由 systemd --user 管理)
特点:
- 任何用户进程都可连接(同一 UID 下)
- 承载桌面服务:
gnome-shell、dolphin、notification-daemon - 用户注销后总线关闭
2.2.3 对比
| 维度 | System Bus | Session Bus |
|---|---|---|
| 实例数 | 全局唯一 | 每用户会话一个 |
| 运行用户 | root / dbus | 当前登录用户 |
| 地址 | /var/run/dbus/system_bus_socket | /run/user/<uid>/bus |
| 策略文件 | /etc/dbus-1/system.d/ | 通常不需要 |
| 典型服务 | systemd, NetworkManager | gnome-shell, pipewire |
| 生命周期 | 随系统启动/关闭 | 随用户会话 |
2.3 四种消息类型
D-Bus 协议定义了四种消息类型:
| 类型 | 方向 | 用途 | 必须回复? |
|---|---|---|---|
| Method Call | 调用方 → 目标 | 请求执行操作 | 是 |
| Method Return | 目标 → 调用方 | 返回结果 | — |
| Error | 目标 → 调用方 | 返回错误信息 | — |
| Signal | 发送方 → 总线 → 订阅者 | 广播事件 | 否 |
消息内部结构
每条 D-Bus 消息由 Header + Body 组成:
┌──────────────────────────────────────┐
│ Header Fields │
│ ┌─────────────────────────────────┐ │
│ │ Message Type (1 byte) │ │
│ │ Flags (1 byte) │ │
│ │ Protocol Version (1 byte) │ │
│ │ Body Length (4 bytes) │ │
│ │ Serial Number (4 bytes) │ │
│ │ Header Fields (variable) │ │
│ │ - PATH (object path) │ │
│ │ - INTERFACE (string) │ │
│ │ - MEMBER (method/signal)│ │
│ │ - DESTINATION (bus name) │ │
│ │ - SENDER (bus name) │ │
│ │ - SIGNATURE (type sig) │ │
│ └─────────────────────────────────┘ │
├──────────────────────────────────────┤
│ Body (参数数据) │
│ ┌─────────────────────────────────┐ │
│ │ arg0: int32 │ │
│ │ arg1: string │ │
│ │ arg2: array of struct │ │
│ └─────────────────────────────────┘ │
└──────────────────────────────────────┘
示例:捕获原始消息
# 使用 busctl monitor 查看原始消息流
busctl monitor --user
在另一个终端发送一条消息:
dbus-send --session --dest=org.freedesktop.DBus \
--type=method_call --print-reply \
/org/freedesktop/DBus \
org.freedesktop.DBus.ListNames
busctl monitor 的输出:
‣ Type=method_call Endian=l Flags=0 Version=1
Path=/org/freedesktop/DBus Interface=org.freedesktop.DBus Member=ListNames
ARGS: (no args)
‣ Type=method_return Endian=l Flags=1 Version=1 ReplySerial=2
ARGS: array [string "org.freedesktop.DBus", string ":1.42", ...]
2.4 D-Bus 地址格式
D-Bus 使用 地址字符串 来定位总线。地址格式为:
<transport>:<key1>=<value1>,<key2>=<value2>,...
| 传输类型 | 地址示例 | 说明 |
|---|---|---|
| Unix Socket (path) | unix:path=/var/run/dbus/system_bus_socket | 按文件路径连接 |
| Unix Socket (abstract) | unix:abstract=/tmp/dbus-XXXXXX | Linux 抽象命名空间 |
| TCP | tcp:host=localhost,port=12345 | TCP 连接(跨机器) |
| systemd | systemd: | 由 systemd 传递的 socket |
| launchd | launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET | macOS |
# 查看 System Bus 地址
dbus-send --system --dest=org.freedesktop.DBus \
--type=method_call --print-reply \
/org/freedesktop/DBus \
org.freedesktop.DBus.GetId
# 在配置文件中查看地址
cat /usr/share/dbus-1/system.conf | grep -A2 '<listen>'
2.5 匹配规则(Match Rules)
匹配规则用于控制信号的接收范围。客户端通过 org.freedesktop.DBus.AddMatch 方法向守护进程注册匹配规则。
语法
"type='signal',sender='org.example.Foo',interface='org.example.Bar',member='Baz',path='/com/example',arg0='hello'"
可用字段
| 字段 | 说明 | 示例 |
|---|---|---|
type | 消息类型 | signal、method_call |
sender | 发送方总线名称 | org.freedesktop.NetworkManager |
interface | 接口名称 | org.freedesktop.DBus.Properties |
member | 方法/信号名称 | StateChanged |
path | 对象路径 | /org/freedesktop/NetworkManager |
path_namespace | 路径前缀匹配 | /org/freedesktop |
arg0 ~ arg63 | 参数值匹配 | arg0='Connected' |
arg0namespace | 参数为总线名称时按前缀匹配 | arg0namespace='org.freedesktop' |
eavesdrop | 监听所有消息(需特权) | eavesdrop='true' |
实际操作
# 只接收 NetworkManager 的信号
dbus-send --session --dest=org.freedesktop.DBus \
--type=method_call \
/org/freedesktop/DBus \
org.freedesktop.DBus.AddMatch \
string:"type='signal',sender='org.freedesktop.NetworkManager'"
# 使用 busctl 设置匹配规则
busctl monitor --user \
--match="type='signal',interface='org.freedesktop.DBus.Properties'"
# 删除匹配规则
dbus-send --session --dest=org.freedesktop.DBus \
--type=method_call \
/org/freedesktop/DBus \
org.freedesktop.DBus.RemoveMatch \
string:"type='signal',sender='org.freedesktop.NetworkManager'"
2.6 总线策略(Bus Policy)
策略定义了哪些进程可以连接总线、发送/接收哪些消息。配置文件位于:
| 目录 | 说明 |
|---|---|
/etc/dbus-1/system.d/ | 系统总线策略(系统管理员) |
/usr/share/dbus-1/system.d/ | 系统总线策略(包提供的默认) |
/etc/dbus-1/session.d/ | 会话总线策略(少见) |
/usr/share/dbus-1/session.d/ | 会话总线策略 |
策略文件示例
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- 只允许 root 用户拥有此名称 -->
<policy user="root">
<allow own="org.example.SystemService"/>
<allow send_destination="org.example.SystemService"/>
</policy>
<!-- 默认策略:允许任何人调用此服务 -->
<policy context="default">
<allow send_destination="org.example.SystemService"
send_interface="org.example.SystemService"/>
<deny send_destination="org.example.SystemService"
send_interface="org.example.SystemService.SecretMethod"/>
</policy>
</busconfig>
策略标签
| 标签 | 说明 |
|---|---|
<policy user="..."> | 按用户匹配 |
<policy group="..."> | 按组匹配 |
<policy context="default"> | 默认策略 |
<allow ...> | 允许 |
<deny ...> | 拒绝 |
权限属性
| 属性 | 说明 |
|---|---|
own | 允许拥有指定总线名称 |
send_destination | 允许发送到指定目标 |
send_interface | 限制到特定接口 |
send_member | 限制到特定方法/信号 |
send_type | 限制消息类型(method_call / signal) |
receive_* | 控制接收权限 |
2.7 连接的生命周期
客户端 dbus-daemon
│ │
│──── TCP/Unix 连接 ──────────────────────→│
│ │
│──── AUTH EXTERNAL <uid-hex> ────────────→│ 认证
│←─── OK <guid> ──────────────────────────│
│ │
│──── BEGIN ──────────────────────────────→│ 开始消息传输
│ │
│──── Hello (Method Call) ────────────────→│ 获取唯一名称
│←─── :1.42 (Method Return) ──────────────│
│ │
│──── RequestName("org.example.Foo") ────→│ 请求公共名称
│←─── DBUS_REQUEST_NAME_REPLY_PRIMARY ───│
│ │
│ ... 正常消息收发 ... │
│ │
│──── 连接关闭 ───────────────────────────→│ 释放名称
注意:
Hello方法是连接后调用的第一个方法,返回分配的唯一名称(如:1.42)。在此之前不允许发送其他消息。
2.8 字节序与序列化
D-Bus 支持两种字节序:
| 标识 | 字节序 |
|---|---|
l (0x6C) | Little-endian(小端) |
B (0x42) | Big-endian(大端) |
现代 Linux 系统上,几乎所有通信都使用 little-endian。
类型签名(Type Signature)
D-Bus 使用单字符编码的数据类型:
| 符号 | 类型 | 示例值 |
|---|---|---|
y | BYTE | 0xFF |
b | BOOLEAN | true |
n | INT16 | -123 |
q | UINT16 | 65535 |
i | INT32 | -100000 |
u | UINT32 | 100000 |
x | INT64 | -9999999 |
t | UINT64 | 9999999 |
d | DOUBLE | 3.14 |
s | STRING | “hello” |
o | OBJECT_PATH | “/org/example” |
v | VARIANT | 任意类型 |
a | ARRAY | as = string array |
(...) | STRUCT | (si) = string + int32 |
组合示例:
as→ 字符串数组a{sv}→ 键值字典(string → variant),极为常见(si)→ 结构体,包含字符串和 int32a(si)→ 结构体数组
# 发送复杂类型
dbus-send --session --dest=org.freedesktop.DBus \
--type=method_call --print-reply \
/org/freedesktop/DBus \
org.freedesktop.DBus.ListNames
# 输出中可以看到类型信息
2.9 架构全景图
┌──────────────────────────────────────────────────────────────┐
│ Linux System │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ systemd │ │ Network │ │ Bluez │ │ udisks2 │ │
│ │ │ │ Manager │ │ │ │ │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │ │
│ ═════╪══════════════╪══════════════╪══════════════╪════════ │
│ │ System Bus (/var/run/dbus/system_bus_socket) │
│ ═════╪══════════════╪══════════════╪══════════════╪════════ │
│ │ │ │ │ │
│ ┌────┴─────┐ ┌────┴─────┐ ┌────┴─────┐ ┌────┴─────┐ │
│ │ dbus- │ │ Policy │ │ Activation│ │ Monitoring│ │
│ │ daemon/ │ │ Engine │ │ Engine │ │ Engine │ │
│ │ broker │ └──────────┘ └──────────┘ └──────────┘ │
│ └──────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ GNOME │ │ Nautilus │ │ Terminal │ ← Session Bus │
│ │ Shell │ │ │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└──────────────────────────────────────────────────────────────┘
本章小结
| 概念 | 说明 |
|---|---|
| System Bus | 系统级服务通信,受策略保护 |
| Session Bus | 用户会话级服务通信,自动随会话启停 |
| 四种消息 | Method Call、Method Return、Error、Signal |
| 匹配规则 | 控制信号接收范围的过滤表达式 |
| 总线策略 | XML 配置文件定义权限矩阵 |
| 类型签名 | D-Bus 数据类型的单字符编码 |