理论基础:Session Bus 与 System Bus
同一条 D-Bus 调用,换成 --user 或 --system 后,可能一个成功、一个提示服务不存在。问题往往不是接口拼错,而是你站在了不同的总线上。
这篇文章会把“session bus 管桌面、system bus 管系统”这句入门口诀展开成准确模型:总线地址从哪里来、现代 systemd 桌面为何常按用户共享 user bus、多次登录和 lingering 怎样影响生命周期,以及一次系统级操作究竟经过哪些授权层。
- **适合你:**已经理解 bus name、object path、interface 和 method,正在排查“服务找不到”或“权限不足”。
- **学完能做:**探测 user/system bus;判断服务所在总线;解释 user manager、多登录和 lingering;区分总线策略、服务授权与 Polkit。
- **前置知识:**请先读 D-Bus 基础与对象模型;会使用终端即可。
- **适用范围:**截至 2026-07,重点面向采用 systemd-logind、
systemd --user和标准 D-Bus 工具的常见 Linux 桌面。非 systemd 系统、容器、SSH、dbus-run-session与沙箱里的地址和生命周期可能不同。 - **时间与成功证据:**阅读约 20 分钟,练习约 15 分钟。成功时,你能保存两份服务列表、确认实际 user bus 地址、找出一个现有对象,并画出一次请求经过的授权层。
全文只进行环境探测、introspection 和属性读取,不修改网络、蓝牙、电源、用户 lingering 或服务状态,不需要 sudo。
1. 两条总线解决的是不同的隔离问题
同一用户的应用与用户服务
│
▼
user/session bus ─── 通知、媒体控制、桌面门户、用户服务……
普通用户前端 ──请求──> system bus ──路由──> 系统守护进程
│
└─ 网络、登录、电源、蓝牙……| 维度 | user/session bus | system bus |
|---|---|---|
| 典型范围 | 一个用户的应用与用户级服务 | 整台机器上的系统服务 |
| 常见身份 | 登录用户启动的进程 | root 或专用系统用户运行的守护进程 |
| 常见例子 | 通知、MPRIS、桌面 portal | NetworkManager、login1、BlueZ |
| 权限重点 | 同 UID 内的协作;沙箱需额外过滤 | 跨身份请求、系统资源和服务级授权 |
| 生命周期 | 取决于总线实现、用户管理器、登录与 lingering | 总线通常随系统启动;具体服务可按需启动、退出或重启 |
这是一张经验地图,不是协议强制分类。服务作者选择在哪条总线上导出接口;一个应用也可以同时连接两条总线。判断时应以本机服务列表和官方 API 文档为准。
普通用户通常可以连接 system bus,也常能执行公开的只读查询。能否调用某个具体方法,要继续经过总线策略与服务授权。反过来,user bus 也不自动意味着“任何调用都安全”。
2. 名称容易误导:session bus 不一定“每窗口会话一条”
D-Bus 传统术语中的 session bus 是登录会话或用户应用环境使用的总线。单独运行 dbus-run-session 时,确实可以得到一条只属于该命令环境的新总线。
但在许多现代 systemd 桌面发行版里,systemd --user 管理的是每个 UID 一份的用户服务环境,user bus 通常位于该 UID 的运行时目录中。同一用户同时从图形界面、TTY 或 SSH 登录时,这些会话可能共享同一个 user manager 和 user bus,而不是每次登录各有一条总线。
因此本文在谈现代 systemd 环境时优先写 user bus;工具和规范仍会使用 --session 这个历史名称。二者在常规桌面 shell 中通常指向同一条总线,但自定义地址、容器或 dbus-run-session 环境里不能强行画等号。
多登录为什么会影响理解
如果同一 UID 的两个会话共享 user bus:
- 一个会话启动的 well-known name,另一个会话也可能看见;
- “当前 owner”属于该 UID 的某个连接,不一定属于当前终端或当前显示器;
- 依赖
DISPLAY、WAYLAND_DISPLAY等图形环境的激活服务,需要桌面集成正确更新激活环境; - 不能仅凭“我退出了一个窗口会话”断言所有用户服务都已结束。
这些是现代集成方式的常见行为,不是所有 D-Bus 部署的协议保证。
3. 先探测真实地址,不要背固定路径
在当前普通用户的登录终端运行:
printf 'UID=%s\n' "$(id -u)"
printf 'XDG_RUNTIME_DIR=%s\n' "$XDG_RUNTIME_DIR"
printf 'DBUS_SESSION_BUS_ADDRESS=%s\n' "$DBUS_SESSION_BUS_ADDRESS"常见 systemd 桌面会显示类似:
UID=1000
XDG_RUNTIME_DIR=/run/user/1000
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus这只是输出形状,不是应该照抄的值。UID、地址参数和传输方式都以本机为准。
XDG_RUNTIME_DIR 是当前用户的运行时目录;常见 user bus socket 是其中的 bus。但环境变量为空不必然证明 user bus 不存在:不同客户端库可能使用默认地址,当前 shell 也可能没有继承完整登录环境。用一次真实连接来复核:
busctl --user --no-pager list
busctl --system --no-pager list成功证据:两条命令都打印表格,并分别包含 org.freedesktop.DBus。两边出现相同的核心名称并不代表它们是同一条总线;每条总线都有自己的 broker、连接与名称空间。
system bus 通常不依赖 DBUS_SESSION_BUS_ADDRESS。busctl --system 会使用系统默认地址,因此也不要用 user bus 的环境变量推测 system bus 是否可达。
4. 生命周期属于总线、用户管理器和服务三个层次
“system bus 上的服务生命周期更长”不够准确,因为它混合了三件事:
- 总线进程或 broker 的生命周期:system bus 通常随系统运行;user bus 与用户运行时环境相关。
- systemd 用户管理器的生命周期:在常见 systemd-logind 集成中,用户首次登录时启动
user@<uid>.service,最后一个并发会话结束后通常回收;配置与 lingering 会改变这一点。 - 具体服务进程的生命周期:无论在哪条总线,服务都可能已常驻、按需激活、空闲退出、崩溃重启或由别的管理器接管。
用只读命令查看当前用户状态:
loginctl show-user "$USER" \
--property=State \
--property=Sessions \
--property=Linger \
--property=RuntimePath常见输出形状:
RuntimePath=/run/user/1000
State=active
Sessions=3 7
Linger=noSessions可能列出同一用户的多个登录会话;RuntimePath应与探测到的用户运行时目录对应;Linger=yes表示 systemd 可在该用户未登录时仍启动并保留用户管理器,使用户服务有机会跨越注销继续运行。
这里只读取状态。loginctl enable-linger 会持久修改生命周期策略,可能让用户服务从开机起运行并持续占用资源,本文不要求执行它。其准确语义见 systemd loginctl 手册和 pam_systemd 手册。
5. 发现服务之后,再替换占位符
命令文档常写 <name> 和 <path>。它们是占位符,不应原样输入,也不能靠猜。按下面顺序发现真实值。
在 user bus 上发现一个名称
busctl --user --no-pager list从 NAME 列复制一个当前存在的 well-known name。org.freedesktop.DBus 一定适合做基础练习;图形会话还可能有 org.freedesktop.Notifications、org.freedesktop.portal.Desktop 或播放器名称,但不能假定每台机器都有。
接着从根对象开始查看对象树:
busctl --user --no-pager tree org.freedesktop.DBus再检查你确实看到的路径:
busctl --user --no-pager introspect \
org.freedesktop.DBus \
/org/freedesktop/DBus成功证据:tree 输出包含 /org/freedesktop/DBus,introspection 输出能找到接口、method、signal 或 property。
在 system bus 上发现一个名称
busctl --system --no-pager list如果系统采用 systemd-logind,列表通常包含 org.freedesktop.login1。先确认名称存在,再运行:
busctl --system --no-pager introspect \
org.freedesktop.login1 \
/org/freedesktop/login1 \
org.freedesktop.login1.Manager这只读取接口结构。成功时,你会看到 ListSessions 等 method 和多项 property;实际成员取决于 systemd 版本。
如果列表里没有 org.freedesktop.login1,不要强行执行后续命令。容器、非 systemd 系统或精简环境可能本来就不提供它,改用列表中实际存在且有官方文档的服务。
6. 两个案例:位置由服务设计和安装状态决定
桌面通知与 portal:常见于 user bus
桌面通知通常只面向当前用户的图形环境,因此通知服务常在 user/session bus 上导出 org.freedesktop.Notifications。桌面 portal 也在这条总线上以 org.freedesktop.portal.Desktop 暴露受控接口,为沙箱应用提供文件选择、打开 URI、截图等能力。
只有在名称确实存在时才检查:
busctl --user --no-pager list | grep -F org.freedesktop.portal.Desktopbusctl --user --no-pager introspect \
org.freedesktop.portal.Desktop \
/org/freedesktop/portal/desktop第二条仍是只读 introspection。找不到名称可能表示未安装、当前不是图形会话或服务激活环境异常,不代表 D-Bus 整体损坏。
NetworkManager:在 system bus 提供 API
NetworkManager 官方文档明确说明其 D-Bus API 位于 system bus。先检查本机是否安装并导出名称:
busctl --system --no-pager list | grep -F org.freedesktop.NetworkManager存在时,读取版本属性:
busctl --system get-property \
org.freedesktop.NetworkManager \
/org/freedesktop/NetworkManager \
org.freedesktop.NetworkManager \
Version成功输出形如 s "1.xx.x":s 是字符串签名,版本值以本机为准。该调用不会扫描 Wi-Fi、连接网络或修改配置。接口细节以 NetworkManager D-Bus API为准。
7. 一次系统级请求会经过哪些授权层
图形网络面板能请求系统服务,不是因为它自动获得了 root。更准确的链路是:
普通用户客户端
│ 1. 连接并发送 D-Bus 消息
▼
system bus policy
│ 2. 是否允许拥有名称、发送或接收这类消息
▼
系统服务
│ 3. 读取调用者 UID/PID 等凭据,检查业务规则
├─ 直接允许或拒绝
└─ 需要时询问 Polkit authority
│
└─ 规则判断;必要时由会话中的认证代理与用户交互需要分清四点:
- 总线策略管消息能否通过这一层,不理解“连 Wi-Fi”之类业务语义。
- 服务自身授权才是最终执行动作的守门人;不同 method 可以有不同规则。
- Polkit 是服务可选用的授权框架,不是 system bus 每个调用自动经过的万能弹窗。服务把未受信任调用者的请求交给 authority 判断,桌面认证代理可能提示认证。
- 只读不等于永远无需授权,写操作也不等于一定弹窗;活动会话、本地/远程身份、系统策略和具体 action 都可能影响结果。
Polkit 的机制、subject、authority 与认证代理分工见 Polkit 官方手册。排查 AccessDenied 时,先确定拒绝来自 bus policy 还是服务/Polkit,不要第一反应就在命令前加 sudo。
8. user bus 不是同 UID 应用之间的沙箱
普通宿主机应用以同一 UID 运行时,user bus 的目标是协作和消息路由,不应被当作应用隔离边界。能够连接 user bus 不代表应用只能看到或调用“自己的”接口;具体限制依赖总线策略与服务设计。
Flatpak 等沙箱会在应用与宿主总线之间加入代理和过滤规则,默认只允许有限的名称。XDG Desktop Portal 再把文件选择、打开 URI、截图等高风险能力收敛成经过检查的接口:
沙箱应用 ──受过滤的 D-Bus──> portal 前端 ──受控请求──> 桌面/宿主资源因此:
- “接口在 session bus 上”不等于沙箱应用自动可调用;
- 给沙箱完整
--socket=session-bus或--socket=system-bus会移除关键过滤,属于高风险权限; - portal 是能力中介,不是因为 user bus 本身提供了应用沙箱。
可进一步查阅 Flatpak 的 D-Bus 权限说明和 XDG Desktop Portal 官方文档。
9. 常见失败:先区分总线、环境和授权
| 症状 | 常见原因 | 只读诊断 | 下一步决策 |
|---|---|---|---|
Failed to connect to bus 只发生在 --user | SSH/容器没有完整用户会话、运行时目录或地址异常 | 查看 XDG_RUNTIME_DIR、DBUS_SESSION_BUS_ADDRESS,再运行 loginctl show-user "$USER" | 确认是否本来就没有 user manager;不要伪造别人的 /run/user/<uid> 地址 |
--system 可用,但目标名称不存在 | 服务未安装、未运行、不可激活或名称写错 | busctl --system list;查服务官方文档 | 如果名称只在 user bus,就切换总线;否则查包和服务状态 |
--user 找不到桌面服务 | 当前是 TTY/SSH、自定义 session bus,或激活环境没有图形变量 | 比较两条总线列表;检查 DISPLAY、WAYLAND_DISPLAY 是否存在 | 回到实际图形会话验证;不要把显示变量硬编码进全局配置 |
| 同一 UID 的另一个登录能看到“我的”服务 | 现代环境共享 per-user bus | loginctl show-user "$USER" -p Sessions -p RuntimePath | 把它视为 user-scoped,而不是 display-session-scoped;需要隔离时使用专门沙箱或独立总线 |
system bus 返回 AccessDenied / NotAuthorized | bus policy、服务规则或 Polkit 拒绝 | 保存完整错误名;先 introspect 并确认调用目标 | 查服务的 action/policy 文档;不要盲目 sudo 或修改策略 |
| 应该弹认证框却超时 | 当前无图形认证代理、会话不活跃,或服务没有请求交互授权 | loginctl list-sessions;记录服务日志和完整错误 | 在正确会话重试或使用服务官方 CLI;不要绕过授权框架 |
| 注销后用户服务仍在 | 仍有其他会话、Linger=yes,或进程不受该 user manager 管理 | loginctl show-user "$USER" -p Sessions -p State -p Linger | 先识别真实生命周期所有者,再决定是否需要配置变更 |
| portal 名称存在但沙箱调用仍拒绝 | 沙箱 D-Bus 过滤、应用权限或 portal 后端问题 | 查应用权限与 portal 日志;只做 introspection 对比 | 依官方 portal/Flatpak 文档修复,避免开放整条总线 |
10. 安全、可观察的练习
11. 总结与下一站
现在你应该能准确区分:user/session bus 主要承载用户范围内的协作,system bus 主要连接系统服务,但具体服务位置必须以发现结果为准;现代 systemd 桌面常把 user bus 做成 per-UID 资源,不能把它机械理解成“每个图形会话一条”;总线、用户管理器和具体服务各有自己的生命周期;一次有影响的调用还要经过 bus policy、服务规则,以及服务选择使用的 Polkit 授权。
至此,第 2 章的系统通信基础已经闭环。下一篇建议进入 终端初体验:别害怕黑框框,把只读探测习惯带到后续安装准备。需要回看消息本身的结构时,返回 D-Bus 基础与对象模型。
官方依据
- D-Bus Specification:总线地址、名称、路由与策略模型。
pam_systemdmanual:用户运行时目录、登录会话与 per-user manager 生命周期。loginctlmanual:会话、用户状态和 lingering 语义。- systemd Desktop Environment Integration:现代桌面共享 user bus 时的多登录与图形会话边界。
- Polkit reference manual:mechanism、subject、authority 与认证代理架构。
- NetworkManager D-Bus API:system bus 上的真实服务接口。
- Flatpak sandbox permissions与 XDG Desktop Portal:沙箱 D-Bus 过滤与 portal 边界。