Power management (简体中文)

From ArchWiki

Tango-preferences-desktop-locale.png本文或本节需要翻译。要贡献翻译,请访问简体中文翻译团队Tango-preferences-desktop-locale.png

附注: No major updates to content since 2017(在 Talk:Power management (简体中文)# 中讨论)
翻译状态:本文是 Power management翻译。上次翻译日期:2017-11-15。如果英文版本有所更改,则您可以帮助同步翻译。

电源管理这个功能可以在系统组件不工作时切断其电源或将其切换到低耗电模式。

Arch 中的电源管理包含两个主要部分:

  1. 配置与硬件交互的内核
  2. 配置用户空间工具,这些工具与内核交互,很多用户空间的工具,配置界面更友好。

用户空间工具

使用这些工具可以简化配置,因为它们的工作方式基本类似,所以请勿同时运行多个程序,以避免冲突。更多电源管理选型请参考 power management category。下面是一些比较流行的省电工具

命令行工具

  • acpid — 一个支持 netlink 的 ACPI 电源管理事件分发进程。
https://sourceforge.net/projects/acpid2/ || acpid
  • Laptop Mode Tools — 配置笔记本电源设置的工具,很多人将其视为省电标准工具,需要的配置比较多。
https://github.com/rickysarraf/laptop-mode-tools || laptop-mode-toolsAUR
  • libsmbios — 和 Dell SMBIOS tables 交互的库和工具.
https://github.com/dell/libsmbios || libsmbios
  • powertop — 检查电源消耗和电源管理的工具,可以协助省电模式的配置。
https://01.org/powertop/ || powertop
  • systemd — 一个系统和服务管理器。
https://freedesktop.org/wiki/Software/systemd/ || systemd
  • TLP — Linux 高级电源管理
https://linrunner.de/tlp || tlp

图形化界面

  • batterymon-clone — 简单的电池监视器。
https://github.com/jareksed/batterymon-clone || batterymon-cloneAUR[损坏的链接:package not found]
  • batsignal — 轻量级电池监视器,使用libnotify来警告电池电量不足。
https://github.com/electrickite/batsignal || batsignalAUR
  • cbatticon — 一款在系统托盘中的轻量且快速的电池图标。
https://github.com/valr/cbatticon || cbatticon
  • GNOME Power Statistics — GNOME的电源信息统计工具。
https://gitlab.gnome.org/GNOME/gnome-power-manager || gnome-power-manager
  • KDE Power Devil — Plasma的电源管理模块。
https://invent.kde.org/plasma/powerdevil || powerdevil
  • LXQt Power Management — LXQt的电源管理模块。
https://github.com/lxqt/lxqt-powermanagement || lxqt-powermanagement
  • MATE Power Management — MATE的电源管理工具。
https://github.com/mate-desktop/mate-power-manager || mate-power-manager
  • MATE Power Statistics — MATE的电源信息统计工具。
https://github.com/mate-desktop/mate-power-manager || mate-power-manager
  • powerkit — 一个独立于桌面的轻量级全功能电源管理器。
https://github.com/rodlie/powerkit || powerkitAUR
  • Xfce Power Manager — Xfce的电源管理工具。
https://docs.xfce.org/xfce/xfce4-power-manager/start || xfce4-power-manager
  • vattery — 用Vala编写的电池监控应用程序,它将在系统托盘中显示笔记本电脑电池的状态。
https://www.jezra.net/projects/vattery.html || vatteryAUR

用 systemd 进行电源管理

ACPI 事件

systemd 能够处理某些电源相关的 ACPI 事件,通过 /etc/systemd/logind.conf/etc/systemd/logind.conf.d/*.conf 进行配置,请参考 logind.conf(5). 如果系统没有专门的电源管理程序,systemd 可以替换掉原本用来响应这些 ACPI 事件的 acpid

事件的动作可以是 ignore, poweroff, reboot, halt, suspend, hibernate, hybrid-sleep, lockkexec. 休眠或挂起动作需要被正确 设置. 如果没有配置事件动作,systemd 会使用默认动作。

事件处理程序 描述 默认动作
HandlePowerKey 按下电源键后的动作 poweroff
HandleSuspendKey 按下挂起键后的动作 suspend
HandleHibernateKey 按下休眠键后触发的动作 hibernate
HandleLidSwitch 笔记本翻盖后触发的动作,除了下面的情况 suspend
HandleLidSwitchDocked 如果笔记本放到了扩展坞或连接了多个显示器时,笔记本翻盖合上时触发的动作 ignore

修改后需要运行 systemctl restart systemd-logind 使上述更改生效。

注意: systemd 无法处理交流电源和电池 ACPI 事件,所以还得使用 Laptop Mode Toolsacpid 工具。

电源管理器

有些 桌面环境 包含的电源管理器会 禁用(临时关闭) 某些或全部 systemd ACPI 设置。这些电源管理器运行时,请在它们的设置中配置 ACPI 事件的动作,只有不被禁用的事件才能在 /etc/systemd/logind.conf/etc/systemd/logind.conf.d/*.conf 中配置。

如果电源管理器没有禁用 systemd 的事件动作,可能出现 systemd 挂起了系统,然后当系统被唤醒之后,电源管理器又再次将系统挂起的情况。截止到 2016 年 12 月,KDE, GNOME, XfceMATE 的电源管理器会执行需要的禁用命令。在使用 acpid 或其它程序处理 ACPI 事件时,响应的 systemd 动作没有被禁用,可以将 Handle 设置为 ignore. 请参考 systemd-inhibit(1).

xss-lock

xss-lock-gitAUR 订阅 systemd 的 suspend, hibernate, lock-sessionunlock-session 事件,并执行对应的动作(运行屏幕锁定并等待用户解锁或停止锁定). xss-lock 还会响应 DPMS 事件并执行屏幕锁定和解锁动作。

下面命令可以 自动启动 xss-lock:

xss-lock -- i3lock -n -i background_image.png &

休眠和挂起

systemd 提供了挂起到内存,挂起到硬盘和混合休眠命令,使用内核的原生挂起和休眠功能执行响应的动作。systemd 还提供了在休眠前和唤醒后执行自定义动作的机制。除了内核默认的待机支持后端,systemd 也可以使用其他后端(比如 Uswsusp)。

systemctl suspend 应该不许要额外的配置,要使用 systemctl hibernate,需要先按照 Suspend and hibernate#Hibernation 进行设置。

混合休眠

systemctl hybrid-sleep 同时执行休眠和挂起,这样如果计算机突然断电(交流电源被拔掉或电池耗尽),将会从硬盘休眠中恢复。如果没有断电,将从内存挂起中恢复,速度比从硬盘恢复快。因为 "hybrid-sleep" 需要将内存转存到交换空间,所以进入睡眠的时间比 systemctl suspend 慢。另外一个选择是使用延迟休眠功能。

休眠钩子

systemd 提供了两种类似的待机/休眠时执行脚本的机制。

使用服务文件

可以将服务文件附在 suspend.target、hibernate.target 或 sleep.target 中,这样就能在待机/休眠前后执行某些操作。用户级操作和root/系统级操作应该使用不同的服务文件。要启用用户级服务文件,使用 # systemctl enable suspend@<用户名> && systemctl enable resume@<用户名>。例如:

/etc/systemd/system/[email protected]
[Unit]
Description=User suspend actions
Before=sleep.target

[Service]
User=%I
Type=forking
Environment=DISPLAY=:0
ExecStartPre= -/usr/bin/pkill -u %u unison ; /usr/local/bin/music.sh stop ; /usr/bin/mysql -e 'slave stop'
ExecStart=/usr/bin/sflock
ExecStartPost=/usr/bin/sleep 1

[Install]
WantedBy=sleep.target
/etc/systemd/system/[email protected]
[Unit]
Description=User resume actions
After=suspend.target

[Service]
User=%I
Type=simple
ExecStartPre=/usr/local/bin/ssh-connect.sh
ExecStart=/usr/bin/mysql -e 'slave start'

[Install]
WantedBy=suspend.target
注意: As screen lockers may return before the screen is "locked", the screen may flash on resuming from suspend. Adding a small delay via ExecStartPost=/usr/bin/sleep 1 helps prevent this.

root/系统级服,使用 root-suspendroot-resume 激活:

/etc/systemd/system/root-resume.service
[Unit]
Description=Local system resume actions
After=suspend.target

[Service]
Type=simple
ExecStart=/usr/bin/systemctl restart mnt-media.automount

[Install]
WantedBy=suspend.target
/etc/systemd/system/root-suspend.service
[Unit]
Description=Local system suspend actions
Before=sleep.target

[Service]
Type=simple
ExecStart=-/usr/bin/pkill sshfs

[Install]
WantedBy=sleep.target

上述服务文件的一些解释(详见 systemd.service(5)):

  • 如果设置 Type=OneShot,那么可以使用多个 ExecStart= 参数。否则只能写一个,替代方案是在 ExecStartPre 中添加命令,或使用分号分隔不同命令(见第一个例子,分号前后的空格都是必须的)。
  • 若命令前加上一个“-”(半角减号),则命令返回非零值时会被忽略、当作正常执行处理。
  • 有关调试,最好的方法是用 journalctl 查看日志。

合并待机和唤醒服务文件

利用下面这种自定义的一体化待机和唤醒服务,使用单一的钩子扩展即可对不同操作(待机/休眠/混合休眠)的不同阶段(进入/唤醒)进行控制。

例子和解释:

/etc/systemd/system/wicd-sleep.service
[Unit]
Description=Wicd sleep hook
Before=sleep.target
StopWhenUnneeded=yes

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=-/usr/share/wicd/daemon/suspend.py
ExecStop=-/usr/share/wicd/daemon/autoconnect.py

[Install]
WantedBy=sleep.target
  • RemainAfterExit=yes:服务启动后,除非显式地停止,否则就认为是活动的。
  • StopWhenUnneeded=yes:服务活动时,如果无其他服务依赖该服务,就停止它。在本例中,该服务会在 sleep.target 停止后停止活动。
  • 由于 sleep.target 会被 suspend.target、hibernate.target、hybrid-sleep.target 调用,且 sleep.target 本身设置了 StopWhenUnneeded,该服务文件对以上各种操作都是有效的。

延迟休眠服务文件

An alternative approach is delayed hibernation. This makes use of sleep hooks to suspend as usual but sets a timer to wake up later to perform hibernation. Here, entering sleep is faster than systemctl hybrid-sleep since no hibernation is performed initially. However, unlike "hybrid-sleep", at this point there is no protection against power loss via hibernation while in suspension. This caveat makes this approach more suitable for laptops than desktops. Since hibernation is delayed, the laptop battery is only used during suspension and to trigger the eventual hibernation. This uses less power over the long-term than a "hybrid-sleep" which will remain suspended until the battery is drained. Note that if your laptop has a spinning hard disk, when it wakes up from suspend in order to hibernate, you may not want to be moving or carrying the laptop for these few seconds. Delayed hibernation may be desirable both to reduce power use as well as for security reasons (e.g. when using full disk encryption). An example script is located here. See also this post for an updated systemd sleep hook.

A slightly updated version of the service is:

/etc/systemd/system/suspend-to-hibernate.service
[Unit]
Description=Delayed hibernation trigger
Documentation=https://bbs.archlinux.org/viewtopic.php?pid=1420279#p1420279
Documentation=https://wiki.archlinux.org/index.php/Power_management
Conflicts=hibernate.target hybrid-sleep.target
Before=sleep.target
StopWhenUnneeded=true

[Service]
Type=oneshot
RemainAfterExit=yes
Environment="WAKEALARM=/sys/class/rtc/rtc0/wakealarm"
Environment="SLEEPLENGTH=+2hour"
ExecStart=-/usr/bin/sh -c 'echo -n "alarm set for "; date +%%s -d$SLEEPLENGTH | tee $WAKEALARM'
ExecStop=-/usr/bin/sh -c '\
  alarm=$(cat $WAKEALARM); \
  now=$(date +%%s); \
  if [ -z "$alarm" ] || [ "$now" -ge "$alarm" ]; then \
     echo "hibernate triggered"; \
     systemctl hibernate; \
  else \
     echo "normal wakeup"; \
  fi; \
  echo 0 > $WAKEALARM; \
'

[Install]
WantedBy=sleep.target

The Before and Conflicts options ensure it only is run for suspension and not hibernation--otherwise the service will run twice if delayed hibernation is triggered. The WantedBy and StopWhenUnneeded options are so it is started before sleep and stops upon resume. (Note that the suspend.target and hibernate.target targets do not stop when unneeded, but sleep.target does). Enable the service.

使用 /usr/lib/systemd/system-sleep 钩子

systemd 在待机/休眠时执行 /usr/lib/systemd/system-sleep/ 里的所有脚本,传递下面两个参数:

  • 参数1:若是准备进入待机/休眠状态,则为 pre;唤醒时为 post
  • 参数2:事件名称,suspendhibernatehybrid-sleep

systemd 会同时执行所有脚本。

脚本输出会记录在相关服务(systemd-suspend.servicesystemd-hibernate.servicesystemd-hybrid-sleep.service)中。通过日志查看:

# journalctl -b -u systemd-suspend
注意: 除了使用自定义脚本,还可以利用 sleep.targetsuspend.targethibernate.targethybrid-sleep.target 来为单元(unit)添加睡眠状态策略。

脚本示范:

/usr/lib/systemd/system-sleep/example.sh
#!/bin/sh
case $1/$2 in
  pre/*)
    echo "进入 $2 状态..."
    ;;
  post/*)
    echo "从 $2 状态唤醒..."
    ;;
esac

记得添加可执行权限:

  1. chmod a+x /usr/lib/systemd/system-sleep/example.sh

详情参见 systemd.special(7)systemd-sleep(8)