Ubuntu 的「设置 → 电源」里应该有「自动挂起」这一项,可以分别设置用电池时和插电时空闲多久挂起。
我的机器上它整组消失了。从"是不是我改坏了"开始查,一路查到 systemd 的 inhibitor 机制,过程挺有意思,记一笔。
症状
打开 设置 → 电源 →「节电(P)」,只有:
屏幕变暗(D) 关
自动节电(S) 关
自动熄屏(B) 开 延时(E) 5 分钟
没有「自动挂起」那一组。GNOME 46 的面板源码里其实是有这一组的:
<AdwPreferencesGroup id="suspend_on_battery_group">
<property name="title" translatable="yes">Automatic Suspend</property>
<property name="visible">false</property> <!-- 初始隐藏 -->
...
它是代码控制显隐的,条件只有一个:
self->can_suspend = can_power_action (self, connection, "CanSuspend");
排除法
既然条件是 CanSuspend,那就先确认它到底是什么。
1. 是不是硬件不支持? 不是。
cat /sys/power/state # freeze mem ← 只有 s2idle,没有 S3
cat /sys/power/disk # [disabled]
固件确实只有 Modern Standby,但内核照样报告能挂起:
busctl call --system org.freedesktop.login1 /org/freedesktop/login1 \
org.freedesktop.login1.Manager CanSuspend
# → s "yes" ← root 调用是 yes
2. 是不是面板版本问题? 不是。把 gnome-control-center 降到装机时的 50.0-0ubuntu6,依然不显示。
3. 是不是 UPower 的锅? 不是。UPower 1.91 确实删掉了 CanSuspend 属性,但面板调的是 logind 的方法,不是 UPower 的属性。
4. 是不是 polkit 拦了? 不是。pkcheck 显示 polkit 里根本没注册这个动作。
5. 那它到底为什么返回 challenge? 普通用户调用:
busctl call --system org.freedesktop.login1 /org/freedesktop/login1 \
org.freedesktop.login1.Manager CanSuspend
# → s "challenge" ← 普通用户却拿到 challenge
root 是 yes、普通用户是 challenge,这个差异就是线索:说明有一个"当前是否被阻止"的运行时判断。
真凶
systemd 在新版本里,只要存在 sleep 类的 inhibitor,就把 CanSuspend 报成 challenge(意思是"可以做,但需要授权"),而不是 yes。面板只认 yes,于是整组隐藏。
而那个 inhibitor 是我自己加的 —— 为了防止机器半夜自己挂起(挂起就没法回微信了),我给隧道套了一层:
ExecStart=/usr/bin/systemd-inhibit --what=sleep:idle --why="DSH 隧道常驻,禁止挂起" --mode=block %h/.local/bin/dsh-tunnel.sh
一个 block 锁,把整个"能挂起"的能力锁死了。
修法
先把 inhibitor 拆掉:
ExecStart=%h/.local/bin/dsh-tunnel.sh
"不自动挂起"改由这两处保证(都不影响面板显示):
# gsettings:空闲后不挂起
gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type 'nothing'
gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-battery-type 'nothing'
# logind:合盖/空闲也不挂起
[HandleLidSwitch]
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
IdleAction=ignore
拆掉后 CanSuspend 立刻变成 yes,「使用电池时(T)」和「插入电源时(N)」两行就都回来了。
附带经验
- GNOME 面板的显隐常常由一个远程能力查询决定,它坏掉时 UI 会"静默少一项",不报任何错 → 排查要往 D-Bus 方法和 systemd 那边想,而不是盯着 GNOME。
systemd-inhibit是个 blunt instrument —— 它挡的不只是挂起动作本身,还会让"能不能挂起"这个查询结果变味。用来做"防休眠"其实不如直接改 gsettings。- 系统日志是金矿:这次真正定位靠的是
journalctl+ 面板源码 +strings二进制里的"CanSuspend"字符串,而不是猜。 - 顺带修掉的自伤:把 GNOME 自己的锁屏当成"故障",写了个"自愈看门狗"去重启 gdm3,结果把自己登出 4 次。GNOME 锁屏时 GDM 的 greeter 是不运行的,那本来是正常状态。
- 另一个坑:"自动熄屏"(Blank Screen) 不是 "自动挂起"(Automatic Suspend),两个中文翻译长得很像,我一开始就看错了,排查方向整个跑偏。