linux电源管理

十月 18, 2025 更新: 八月 16, 2026 [Linux 与系统] #pm

查看suspend休眠配置模式

cat /sys/power/mem_sleep
echo deep | sudo tee /sys/power/mem_sleep

配置hibernate休眠

cat /etc/fstab
swapon --show
sudo swapoff -a
sudo dd if=/dev/zero of=/swapfile bs=1G count=17
sudo swapon /swapfile

# 获取swap文件的offset
sudo filefrag -v /swapfile | awk '{if($1=="0:") print $4}'
# 查看根分区 UUID
findmnt -no UUID -T /

sudo vim /etc/default/grub
# GRUB_CMDLINE_LINUX_DEFAULT="quiet splash mem_sleep_default=deep resume=UUID=<磁盘分区UUID> resume_offset=<swap文件偏移量>"
sudo update-grub

配置电源管理

cat <<EOF > /etc/systemd/sleep.conf.d/enable-sleep.conf
AllowSuspend=yes
AllowHibernation=yes
HibernateMode=platform shutdown
HibernateState=disk
AllowSuspendThenHibernate=yes
AllowHybridSleep=yes
SuspendState=mem standby freeze
EOF

sudo mkdir /etc/systemd/logind.conf.d
cat <<EOF | sudo tee /etc/systemd/logind.conf.d/override.conf
[Login]
HandlePowerKey=suspend-then-hibernate
HandleRebootKey=reboot
HandleRebootKeyLongPress=poweroff
HandleSuspendKey=suspend
HandleSuspendKeyLongPress=hibernate
IdleAction=suspend-then-hibernate
IdleActionSec=30min
EOF

systemd-analyze cat-config systemd/sleep.conf
systemd-analyze cat-config systemd/logind.conf

防休眠(让机器随时可连)

场景:远程机(笔记本、服务器)需要 7x24 可 SSH 连接,不能自动睡。

方案一:mask 休眠目标(简单粗暴)

# 屏蔽所有休眠途径:空闲挂起、合盖、电源键、手动 suspend 全部失效
sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target
# 验证(显示 masked)
systemctl list-units --type=target --all | grep -Ei "suspend|sleep|hibernate"
# 恢复
sudo systemctl unmask sleep.target suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target

缺点(太重):手动 systemctl suspend、按电源键、合盖全部静默失败;电池模式下也无法省电;无法区分 AC/电池策略。

方案二:GNOME 电源管理(推荐,粒度细)

原理:gsd-power 持有 sleep block 抑制剂(systemd-inhibit --list 可查),logind 执行 IdleAction 时被抑制剂挡住,机器不会因空闲挂起,但手动挂起/电源键仍然可用。

# 空闲自动挂起:AC/电池 均设为 nothing(不挂起)
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
gsettings get org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type   # 验证

合盖行为:logind drop-in 必须带 [Login] 段头(易踩坑)

症状:/etc/systemd/logind.conf.d/override.conf 写了 HandleLidSwitch=ignore,systemd-analyze cat-config 也显示已合并,但 logind 生效值仍是 suspend,合盖照样挂起。

根因:drop-in 文件漏了 [Login] 段头——systemd 配置解析要求键必须属于某个 section,没有段头整行被忽略。日志可见(需 root 读 journal):

systemd-logind: /etc/systemd/logind.conf.d/override.conf:1: Assignment outside of section. Ignoring.

systemd-analyze cat-config 只拼接文件内容、不做 section 校验,所以看起来一切正常——别被它骗了。

(旧版曾误判为"gsd-power 在会话启动时用 SetLidSwitchBehavior 覆盖 logind 配置":实际 systemd ≥257 已移除该方法,logind 合盖行为完全由配置决定,不存在运行时覆盖;D-Bus 监听也证实无任何调用。误判源于对 HandleLidSwitch 属性值的错误解读——它如实反映配置,配置只是没生效而已。)

排查/验证:

# 看 logind 当前生效的合盖行为
busctl get-property org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager HandleLidSwitch

# drop-in 未生效时,root 查看被忽略的原因
sudo journalctl -u systemd-logind -n 20 | grep -i "outside of section"

修复(改完 systemctl restart systemd-logind 立即生效;会登出图形会话,对纯服务器无影响):

sudo tee /etc/systemd/logind.conf.d/override.conf <<'EOF'
[Login]
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
EOF
sudo systemctl restart systemd-logind

合盖 ignore 之后:屏幕不会自动息屏(省电确认方法)

合盖不挂起后,OS 层面对 lid 事件不再做任何动作,屏幕也不会自动熄灭:背光保持原亮度、GPU 继续渲染、eDP 供电不断。是否真省电取决于笔记本 EC 固件是否在硬件层切断面板供电(系统内无法直接确认,多数笔记本 EC 会断,但别默认)。

确认屏幕当前是否在省电状态:

# 背光设定值(0 = 软件层已熄屏;非 0 = 还亮着)
cat /sys/class/backlight/*/actual_brightness /sys/class/backlight/*/max_brightness

# GPU 渲染管道是否还活动(有 "1920x1080" 这样的帧缓冲 = 还在渲染)
sudo cat /sys/kernel/debug/dri/0/state | grep "crtc-pos"

# eDP 数据通道供电(PORT_DDI_LANES_A = 1 说明还在供电)
sudo cat /sys/kernel/debug/dri/0/i915_power_domain_info | grep "PORT_DDI_LANES_A"

# 功耗对比(最可靠;需拔电源用电池,合盖前后各读一次)
awk '{print "放电功率(W):", $1*$2/1e12}' \
  /sys/class/power_supply/BAT1/current_now /sys/class/power_supply/BAT1/voltage_now
# 或者最直接:开一条缝看屏幕

真正省电靠 idle 息屏:GNOME org.gnome.desktop.session idle-delay(默认 300s)闲置后屏幕熄灭(背光归 0),与合盖无关;0 = 永不自动息屏。注意登录页(gdm greeter)状态下的息屏由 greeter 决定,未必生效——要确保息屏先登录回图形会话。合盖 ignore 不影响手动 systemctl suspend(需要时仍可手动挂起省电)。

SSH 会话里 gsettings get/set 直接可用(dconf 写用户库,无需会话 DBus);但 gdbus/busctl 调图形会话接口(息屏/锁屏,见下文)需借用图形会话的 DBus(DBUS_SESSION_BUS_ADDRESS 从 gnome-shell 进程 environ 取)。

排查:远程机 SSH 断连

场景症状 ssh: connect to host ... No route to host,先分清是「机器挂了」还是「换 IP 了」:

ping -c2 <host>                    # 不通:机器挂起/关机,或 IP 已变
uptime -p; last reboot -2          # suspend 不重置 uptime;last reboot 区分挂起 vs 重启
cat /proc/acpi/button/lid/*/state  # 合盖挂起的机器:开盖自动恢复(SSH 又通了就是合盖挂起)
nmap -sn -T4 192.168.2.0/24 | grep report   # IP 变了:扫存活主机,用 hostname 对上目标

/etc/hosts 静态条目过期是常见原因(机器在线但换 DHCP IP)。

排查方法论(合盖挂起实战教训)

  1. 认错机器:主机离线时 nmap 扫到活机,先核对 hostname、cat /sys/class/dmi/id/product_name、系统版本再当目标——局域网可能有多台相似机器(本案例把 .147 的 H3C MegaBook 当成 .63 的华为 MateBook 配置了一下午,改错了对象)。
  2. journal 组权限坑:非 root 且不在 adm/systemd-journal 组时,journalctl 只显示本人会话的消息,系统消息(logind 等)全被隐藏——grep 不到 ≠ 没有日志。排查 systemd 行为一律 sudo journalctl -u <unit> -n 20。本案例 drop-in 被忽略的 Assignment outside of section 警告就是加了 sudo 才看到的。
  3. 抓 D-Bus 调用方:dbus-monitor --system "type=method_call,member=<方法名>"(非 root 自动降级 eavesdropping,仍能抓到),配合重启服务/登录会话重现。本案例用监听证实没有任何会话进程调用 SetLidSwitchBehavior,排除"运行时覆盖"理论,把排查收敛到配置解析本身。
  4. systemd ≥257 已移除 SetLidSwitchBehavior:logind 合盖行为完全由配置决定,不存在运行时覆盖——配置"没生效"优先怀疑配置本身(段头/文件路径/优先级),而不是被谁覆盖。

Wi-Fi 不断线

笔记本掉线头号元凶是 Wi-Fi 省电(NIC 间歇性休眠),SSH 空闲断连则是 NAT 超时掐线:

# 关闭 Wi-Fi 省电(全局 + 单个连接,重启后仍生效)
sudo nmcli g s wifi.powersave 2
sudo nmcli con mod "SSID" 802-11-wireless.powersave disable
sudo nmcli con down "SSID" && sudo nmcli con up "SSID"   # 重连使连接级配置生效
sudo iw dev wlan0 get power_save                          # 验证:Power save: off

# sshd 保活:/etc/ssh/sshd_config.d/keepalive.conf
ClientAliveInterval 60
ClientAliveCountMax 3
sudo systemctl restart ssh

息屏与锁屏

息屏(DPMS)与休眠独立:息屏只是屏幕进省电状态,系统照常运行、SSH 不掉线;休眠是整机冻结。

# 检查/关闭自动息屏(GNOME)
gsettings get org.gnome.desktop.session idle-delay     # 秒;0 = 永不自动息屏
gsettings set org.gnome.desktop.session idle-delay 0
gsettings get org.gnome.settings-daemon.plugins.power idle-dim   # 息屏前先变暗

远程控制息屏/亮屏(即时动作;gsettings 只改"空闲多久自动息屏"之类的规则,不是动作,不能替代):

# GNOME Wayland:SSH 会话需借用图形会话的 bus。
# systemd 用户会话有固定路径 /run/user/<uid>/bus,比从 gnome-shell environ 里挖更稳;
# 跨用户会话(如活动会话是 gdm greeter,属 Debian-gdm)用 sudo -u。
busctl --user call org.gnome.ScreenSaver /org/gnome/ScreenSaver org.gnome.ScreenSaver SetActive b true   # true=息屏, false=亮屏
busctl --user call org.gnome.ScreenSaver /org/gnome/ScreenSaver org.gnome.ScreenSaver GetActive          # 查询屏保状态

# GNOME 同时实现了 org.gnome.ScreenSaver 和 org.freedesktop.ScreenSaver 两个名字(实测都可用),
# 用哪个都行;busctl 比 gdbus 稳(本机实测 gdbus 传 b 类型参数报 "expected value" 解析错误)

坑与边界(实测):

# X11 环境有直接工具:xset dpms force off / on
# (旧文曾写 "Wayland 下 xdg-screensaver 已失效"——实测不准:xdg-screensaver 在 Wayland 可用,
#   失效的只是 xset,它只能操作 Xwayland 窗口,点不亮物理屏幕;wlopm 是 wlroots/Sway 系,GNOME 用不了)

# 锁屏:loginctl lock-session <id>(亮屏需密码,不适合远程)
# 锁屏不会断 Wi-Fi(网络栈与显示会话无关);真正断网的是休眠