systemctl restart gdm3 卡住排查:gpu-manager 阻塞在 QXL 的 drm_master_open

九月 12, 2026 [Linux 与系统] #gdm #systemd #ubuntu

现象

本机(Ubuntu 24.04 虚拟机,QEMU/KVM + QXL 虚拟显卡)执行:

sudo systemctl restart gdm3

命令长时间不返回,终端像卡死;Ctrl-C 之后重跑还是卡。

时间线:卡在 gpu-manager,不是 gdm3

journalctl -u gdm3 把两次 restart 的时间线拉出来:

22:14:13 systemd[1]: Stopping gdm3.service - LSB: GNOME Display Manager...
22:14:33 systemd[1]: Stopped gdm3.service - LSB: GNOME Display Manager.    ← stop 花了 20s
22:14:33 systemd[1]: Starting gpu-manager.service - Detect the available GPUs...
22:16:36 systemd[1]: Finished gpu-manager.service - Detect the available GPUs...
22:16:36 systemd[1]: Starting gdm3.service - LSB: GNOME Display Manager...  ← 等 gpu-manager 结束才开始
22:16:36 systemd[1]: Started gdm3.service - LSB: GNOME Display Manager.     ← gdm3 本身瞬间完成

第二次同样:

22:20:58 Stopped gdm3.service
22:22:30 Finished gpu-manager.service    ← 1m32s
22:22:30 Started gdm3.service

耗时几乎全部花在 gpu-manager.service 上:gdm3 的启动在等它跑完。

顺带一个容易误判的点:Ctrl-C 只会结束 systemctl 客户端进程,systemd 里的 job 仍在继续。本机第一次 restart 的客户端进程在 22:16:22 就退出了,而 job 到 22:16:36 才真正完成——所以"Ctrl-C 没反应"只是假象,实际是 job 还没跑完。

依赖链:restart gdm3 为什么会拉起 gpu-manager

gpu-manager.service 由 Ubuntu 的 ubuntu-drivers-common 提供(检测 GPU 变化、处理显卡驱动切换),它的单元声明:

# /usr/lib/systemd/system/gpu-manager.service
[Unit]
Before=display-manager.service
...
[Install]
WantedBy=display-manager.service

而 display-manager.service 在本机是指向 gdm3 的别名,安装时生成的 wants 链接把它绑进了 display-manager 的启动事务:

$ ls /etc/systemd/system/display-manager.service.wants/
gpu-manager.service -> /lib/systemd/system/gpu-manager.service

$ systemctl show gdm3 -p Wants,After
Wants=gpu-manager.service
After=... gpu-manager.service

于是每次 start/restart gdm3:systemd 先按依赖拉起 gpu-manager,又因为 Before=display-manager.service,gdm3 的启动必须排在它后面——gpu-manager 不结束,gdm3 就不会开始。

实锤:单跑 gpu-manager 也卡,卡在内核 drm_master_open

单独跑 gpu-manager,同样不返回:

$ time timeout 40 /usr/bin/gpu-manager --log /tmp/gm-test.log
real    0m48.337s
user    0m0.042s      ← 几乎不耗 CPU
sys     0m0.033s
rc=124                ← 被 timeout 杀掉

CPU 只有 0.04 秒,说明它在等而不是在算。抓卡住时的进程状态与内核栈:

$ grep State /proc/<pid>/status
State:  D (disk sleep)      ← 不可中断睡眠

$ cat /proc/<pid>/wchan
drm_master_open

$ cat /proc/<pid>/stack
[<0>] drm_master_open+0x2b/0xc0
[<0>] drm_open_helper+0x109/0x130
[<0>] drm_open+0x76/0x130
[<0>] drm_stub_open+0xb3/0xf0
[<0>] chrdev_open+0xcf/0x250
[<0>] do_dentry_open+0x21d/0x570
[<0>] vfs_open+0x33/0x50

gpu-manager 在打开 /dev/dri/card1(QXL 显卡的 DRM 设备)时阻塞在内核 drm_master_open:进程处于 D 状态(不可中断),信号也打不断。它的业务逻辑其实已经跑完(日志里 Skipping /dev/dri/card1, driven by qxl → Single card detected),卡的只是这次 open 系统调用。约 1.5~2 分钟后自行解除(两次实测 2m03s / 1m32s),具体是谁持有内核锁没有继续深挖——定位到这一步,修复方向已经清楚。

本机特例:gdm3 其实已经不存在

这台机器上 gdm3 包早已被移除(dpkg -l gdm3 状态为 rc,只剩配置文件),/usr/sbin/gdm3 不存在;残留的 /etc/init.d/gdm3 被 systemd 生成成 gdm3.service,而脚本开头是:

DAEMON=/usr/sbin/gdm3
test -x $DAEMON || exit 0     ← 二进制不存在,start 直接退出 0

所以等完 gpu-manager 那 2 分钟,GDM 也不会真的启动——unit 显示 Started 只是 init 脚本空转退出 0。这个 restart 除了等 2 分钟之外什么也没发生。

(这也说明判断"服务起没起"不能只看 systemctl status 的 Active 状态,要看进程是否真的存在。)

2026-09-13 更新:本节记录的是当时的残留状态。桌面栈已重装(起因见文末「背景补记」),gdm3 现在是原生 gdm.service,这个特例已不存在。

修复方案(2026-09-13 实测:方案 1 生效)

按场景选:

  1. 还在用 GDM:屏蔽 gpu-manager 即可——

    sudo systemctl mask gpu-manager.service
    

    QXL 虚拟机上 gpu-manager 没有用途(它服务于 NVIDIA/AMD 专有驱动切换);mask 后 Wants 依赖会立即失败,gdm3 照常启动(Wants 非强制)。需要时 systemctl unmask 恢复。本机 2026-09-13 实测:mask 后安装/启动 gdm3 均无卡顿,2 分钟等待消失。

  2. 不用 GDM:清掉残留,避免再踩——

    sudo apt purge gdm3          # 清 /etc/init.d/gdm3 + /etc/X11/default-display-manager
    sudo systemctl daemon-reload
    
  3. 只想命令别卡:systemctl restart gdm3 --no-block(立即返回,但实际启动仍要等 gpu-manager)。

  4. 也可用 drop-in 摘掉依赖,但不如 mask 直接。

2026-09-13 后续:桌面栈重装、QXL 显存耗尽

背景补记:桌面栈为什么"已经不存在"

07-24 的一次清理是起因:

Commandline: apt purge --autoremove snapd libsnapd-glib-2-1
Purge: ... ubuntu-desktop, ubuntu-desktop-minimal, gnome-shell, gdm3, ubuntu-session, gnome-remote-desktop, gnome-control-center ...

ubuntu-desktop 依赖的 firefox/thunderbird 是 snap 过渡包,过渡包依赖 snapd——--autoremove 顺着依赖链把整个桌面栈连坐了。表现:dpkg -l 里 gnome-shell/gdm3 全是 rc、/usr/share/xsessions 消失、控制台只剩 tty1 文本登录。

重装(在 mask gpu-manager 之后执行):

sudo apt-get update && sudo apt-get install -y gdm3
# 79 新装 + 28 升级,含 gnome-shell/ubuntu-session/gnome-remote-desktop/gnome-control-center

教训:apt purge --autoremove 删基础包(如 snapd)前先 apt-get -s 模拟看 Remove 列表;snap 过渡包会把删除面放大成桌面级事故。

新坑:QXL 显存 BO 耗尽,greeter 起了但画不出来

重装后 gdm.service active、gnome-shell 进程也在(日志已到 Registering session with GDM),但 VM 控制台仍是 tty1 文本。内核日志在刷:

[drm:qxl_alloc_bo_reserved [qxl]] *ERROR* failed to allocate VRAM BO
[TTM] Buffer eviction failed
qxl 0000:00:01.0: object_init failed for (4096, 0x00000001)

每 ~15 秒一次,且从当天 17:55 就开始(远早于重装)——QXL 显存被更早的某次图形操作耗尽后状态粘住,重启 VM 复位后 GDM 登录界面正常渲染。

至此这台机器的 QXL 集齐了三种形态:drm_master_open 阻塞(gpu-manager)、没有 renderD128(纯 2D 卡)、显存 BO 耗尽(本次)。最终按 工具链迁移 Wayland 一文的实测方案把显卡换成 virtio-vga-gl,QXL 相关问题整体消失。

总结

排查命令速查

目的命令
看 restart 卡在哪个阶段journalctl -u gdm3 --since "5 min ago" -o short-precise
看单元依赖(谁拉起谁、谁等谁)systemctl show <unit> -p Wants,After,Before
看依赖安装链接ls /etc/systemd/system/display-manager.service.wants/
单跑复现 + 计时time timeout 40 /usr/bin/gpu-manager --log /tmp/gm.log
卡住进程的内核栈cat /proc/<pid>/wchan; cat /proc/<pid>/stack
看 systemd 当前 jobsystemctl list-jobs