理论基础: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 bussystem bus
典型范围一个用户的应用与用户级服务整台机器上的系统服务
常见身份登录用户启动的进程root 或专用系统用户运行的守护进程
常见例子通知、MPRIS、桌面 portalNetworkManager、login1、BlueZ
权限重点同 UID 内的协作;沙箱需额外过滤跨身份请求、系统资源和服务级授权
生命周期取决于总线实现、用户管理器、登录与 lingering总线通常随系统启动;具体服务可按需启动、退出或重启

这是一张经验地图,不是协议强制分类。服务作者选择在哪条总线上导出接口;一个应用也可以同时连接两条总线。判断时应以本机服务列表和官方 API 文档为准。

⚠️ 不要把 system bus 等同于 root

普通用户通常可以连接 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 的某个连接,不一定属于当前终端或当前显示器;
  • 依赖 DISPLAYWAYLAND_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_ADDRESSbusctl --system 会使用系统默认地址,因此也不要用 user bus 的环境变量推测 system bus 是否可达。

4. 生命周期属于总线、用户管理器和服务三个层次

“system bus 上的服务生命周期更长”不够准确,因为它混合了三件事:

  1. 总线进程或 broker 的生命周期:system bus 通常随系统运行;user bus 与用户运行时环境相关。
  2. systemd 用户管理器的生命周期:在常见 systemd-logind 集成中,用户首次登录时启动 user@<uid>.service,最后一个并发会话结束后通常回收;配置与 lingering 会改变这一点。
  3. 具体服务进程的生命周期:无论在哪条总线,服务都可能已常驻、按需激活、空闲退出、崩溃重启或由别的管理器接管。

用只读命令查看当前用户状态:

loginctl show-user "$USER" \
  --property=State \
  --property=Sessions \
  --property=Linger \
  --property=RuntimePath

常见输出形状:

RuntimePath=/run/user/1000
State=active
Sessions=3 7
Linger=no
  • Sessions 可能列出同一用户的多个登录会话;
  • 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.Notificationsorg.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.Desktop
busctl --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

              └─ 规则判断;必要时由会话中的认证代理与用户交互

需要分清四点:

  1. 总线策略管消息能否通过这一层,不理解“连 Wi-Fi”之类业务语义。
  2. 服务自身授权才是最终执行动作的守门人;不同 method 可以有不同规则。
  3. Polkit 是服务可选用的授权框架,不是 system bus 每个调用自动经过的万能弹窗。服务把未受信任调用者的请求交给 authority 判断,桌面认证代理可能提示认证。
  4. 只读不等于永远无需授权,写操作也不等于一定弹窗;活动会话、本地/远程身份、系统策略和具体 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 只发生在 --userSSH/容器没有完整用户会话、运行时目录或地址异常查看 XDG_RUNTIME_DIRDBUS_SESSION_BUS_ADDRESS,再运行 loginctl show-user "$USER"确认是否本来就没有 user manager;不要伪造别人的 /run/user/<uid> 地址
--system 可用,但目标名称不存在服务未安装、未运行、不可激活或名称写错busctl --system list;查服务官方文档如果名称只在 user bus,就切换总线;否则查包和服务状态
--user 找不到桌面服务当前是 TTY/SSH、自定义 session bus,或激活环境没有图形变量比较两条总线列表;检查 DISPLAYWAYLAND_DISPLAY 是否存在回到实际图形会话验证;不要把显示变量硬编码进全局配置
同一 UID 的另一个登录能看到“我的”服务现代环境共享 per-user busloginctl show-user "$USER" -p Sessions -p RuntimePath把它视为 user-scoped,而不是 display-session-scoped;需要隔离时使用专门沙箱或独立总线
system bus 返回 AccessDenied / NotAuthorizedbus 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. 安全、可观察的练习

练习 1 建立两份清单 分别保存或截图 busctl --user --no-pager list 与 busctl --system --no-pager list 的输出。各标出三个 well-known name;若不足三个,就如实记录精简环境。验收结果是名称、总线、用途三列表。
练习 2 证明当前地址 保存 UID、XDG_RUNTIME_DIR、DBUS_SESSION_BUS_ADDRESS 的探测结果,再用 busctl --user list 验证能否实际连接。说明“环境变量值”和“连接成功”哪一项是更直接证据。
练习 3 对比同名核心对象 分别 introspect 两条总线上的 org.freedesktop.DBus 和 /org/freedesktop/DBus。记录它们都有哪些核心接口,并解释“名字相同但不是同一总线”的原因。
练习 4 找一个真实系统服务 从 system bus 列表中选择本机确实存在的 org.freedesktop.login1、org.freedesktop.NetworkManager 或其他有官方文档的名称,只做 tree、introspect 或只读 property 查询。验收结果包含发现命令、真实 object path 和成功输出中的一个成员。
练习 5 画出授权路径 任选“查询网络版本”和“修改网络连接”做对比,画出客户端、system bus policy、系统服务、服务授权与 Polkit 的关系。不要实际修改网络;验收标准是能指出 Polkit 不是每个 D-Bus 调用的必经层。

11. 总结与下一站

现在你应该能准确区分:user/session bus 主要承载用户范围内的协作,system bus 主要连接系统服务,但具体服务位置必须以发现结果为准;现代 systemd 桌面常把 user bus 做成 per-UID 资源,不能把它机械理解成“每个图形会话一条”;总线、用户管理器和具体服务各有自己的生命周期;一次有影响的调用还要经过 bus policy、服务规则,以及服务选择使用的 Polkit 授权。

至此,第 2 章的系统通信基础已经闭环。下一篇建议进入 终端初体验:别害怕黑框框,把只读探测习惯带到后续安装准备。需要回看消息本身的结构时,返回 D-Bus 基础与对象模型

官方依据

修订时间线

2026-03-29 初版 介绍 session bus、system bus、NetworkManager、login1 与 Polkit 的基础分工。
2026-07-21 生命周期与权限边界修订 补充实际地址探测、传统 session 与现代 per-user bus 的区别、多登录和 lingering;拆分 bus policy、服务授权、Polkit 与沙箱 portal 边界,并加入成功证据、分层排障和只读练习。
Navigation