电源管理/唤醒触发器
唤醒触发器是可以将系统从任何硬件节能状态唤醒的事件源。显而易见的例子是电源或挂起按钮、网络唤醒功能或笔记本电脑系统中的盖开关。唤醒触发器可以通过下面列出的各种内核接口进行控制。没有涵盖所有可能的触发器的统一接口。
唤醒触发器接口[编辑 | 编辑源代码]
/proc/acpi/wakeup[编辑 | 编辑源代码]
读取 /proc/acpi/wakeup
文件将生成 ACPI 注册的唤醒源列表,其中包含相应的 sysfs
IDs(如果可用)。将条目从设备列写入文件可切换其状态。例如,要禁用打开笔记本电脑盖时的唤醒功能,请运行:
# echo "LID" > /proc/acpi/wakeup
/sys/module/acpi/parameters/ec_no_wakeup[编辑 | 编辑源代码]
该文件表示 ACPI 内核模块选项 ec_no_wakeup
的值,当系统处于挂起到空闲(s2idle
)电源状态时,该选项控制从嵌入式控制器传递唤醒触发器[1]。在现代笔记本电脑上,嵌入式控制器唤醒在某些情况下会导致电池过度消耗。
/sys/devices/[编辑 | 编辑源代码]
每个支持唤醒的 sysfs
设备都在设备的 power
子目录中包含文件 wakeup
。该文件包含唤醒触发器的状态,也可以写入。总线控制器和端点设备能够唤醒系统。例如,若要禁用第一个 USB 控制器(总线)的唤醒,请运行:
# echo "disabled" > /sys/bus/usb/devices/usb1/power/wakeup
如果启用了触发器,端点设备应该能够唤醒设备,而不管控制器的设置如何,但这可能与依赖于硬件。
编写 PowerTOP 与 sysfs
的接口,但它只通过读取 /sys/class/net/
和 /sys/bus/usb/devices/
(包括到 /sys/devices/
的符号链接)来列出网络和 USB 设备的唤醒触发器。
/sys/class/wakeup/*[编辑 | 编辑源代码]
几乎所有的唤醒触发器都可以在 /sys/class/wakeup
目录中找到,该目录包含指向所有相关设备的符号链接。这对于通过子目录查找可能的唤醒触发器非常有用。一些触发器可以对应于虚拟设备,而与硬件相关的唤醒触发器至少包含以下文件之一:
/sys/class/wakeup/*/device/physical_node/power/wakeup /sys/class/wakeup/*/device/power/wakeup
/sys/class/wakeup
中的一些唤醒触发器还提供了一个以下文件指向所在位置隐藏的 /proc/acpi/wakeup
名称的链接:
/sys/class/wakeup/*/device/path
持久设置[编辑 | 编辑源代码]
原先的方法应该足以设置 /proc/acpi/wakeup
状态和 acpi.ec_no_wakeups
内核参数,而 udev 的事件驱动途径是配置 sysfs
设备的可靠方式。
使用 systemd 的原先方法[编辑 | 编辑源代码]
ec_no_wakeups
ACPI 内核模块选项可以在启动时设置,如本文所述。在引导时设置 sysfs
值的标准解决方案是 systemd services,例如在此故障排除中。/proc/acpi/wakeup
的另一个基于 systemd 的管理器是 wakeup-triggersAUR。
一些系统可以在电源状态转换时覆盖一些 ACPI 唤醒触发器,这更像是一个 bug,而不是一个功能。如果硬件能在可预测的时间覆盖触发器,则仍然可以使用适当构建的 systemd units 来解决。sleep.target
是一个通用目标,涵盖所有不同的挂起状态,在这种情况下可能会有所帮助,但没有通用的 wakeup.target
[2]。
此方法仅适用于始终连接的 sysfs
设备。
使用 udev 的事件驱动[编辑 | 编辑源代码]
使用 udev 规则设置唤醒触发器状态是一种事件驱动的方法,在连接具有唤醒触发器的设备时都能可靠地工作。关键是检测规则中是否添加了新设备( ACTION=="add"
),并将唤醒触发器状态设置为 ATTR{power/wakeup}="disabled"
。如果硬件正在重置此设置,udev 可以尝试在每次设备更改时重新应用规则来规避此设置(ACTION=="add|change"
)。通过 udevadm info -q all -a /sys/devices/...
可以获得一个设备树,其中包含用于匹配 sysfs
中找到的特定设备的可能参数。
这里一个典型的常见例子是罗技统一 USB 接收器。它的唤醒触发器应该在默认情况下启用,如果不需要,解决方案可以是 udev 规则,如下所示:
/etc/udev/rules.d/logitech-unifying.rules
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c52b", ATTR{power/wakeup}="disabled"
在 udev 文章中描述了启用必要触发器的相反情况。
udev 触发器在设备枚举中出现得太早,以至于使用上述方法禁用唤醒触发器会导致(某些?)禁用的触发器不在 /sys/class/wakeup
中列出。这可能取决于设备在启动时是否已经存在,需要进一步阐明。
疑难解答[编辑 | 编辑源代码]
列出设备和/或总线树[编辑 | 编辑源代码]
当试图理解某个系统的所有唤醒触发器时,这些辅助命令会很有用,有助于编写 udev 规则或进行常见的唤醒源故障排除:
# lshw -businfo -numeric # lspci -DPPnn # lsusb -tvv
挂起后被立即唤醒[编辑 | 编辑源代码]
对于某些带有 LynxPoint 或 LynxPoint-LP 芯片组的 Intel Haswell 系统,有报告称它们会在挂起后就立即被唤醒。这与错误的 BIOS ACPI 实现,以及 xhci_hcd
如何在引导期间如何解释它们都有关。作为一项解决方案,内核会将报告的受影响系统逐个添加到拒绝列表(名为 XHCI_SPURIOUS_WAKEUP
)[3]。
电脑也有可能在其它情况下发生立即恢复,比如,一个 USB 设备在挂起后插入,且启动了 ACPI 唤醒触发器。对于这样的情况,如果受影响的系统尚未被加入上述拒绝列表,一个可行的方案是禁用相关的唤醒触发器。通过 USB 禁用唤醒的例子见后[4]。
要查看当前配置:
$ cat /proc/acpi/wakeup
Device S-state Status Sysfs node ... EHC1 S3 *enabled pci:0000:00:1d.0 EHC2 S3 *enabled pci:0000:00:1a.0 XHC S3 *enabled pci:0000:00:14.0 ...
可以看到相关的设备有 EHC1
、EHC2
和 XHC
(USB 3.0设备)。要切换它们的状态,你需要以 root 权限把设备名写入到文件里。
# echo EHC1 > /proc/acpi/wakeup # echo EHC2 > /proc/acpi/wakeup # echo XHC > /proc/acpi/wakeup
这能使挂起重新工作。但是,此设置只是临时的,需要在每次启动时设置。要实现自动化,请参阅 Systemd#临时文件或论坛帖子以了解可能的解决方案。
nouveau 驱动程序[编辑 | 编辑源代码]
如果使用了 nouveau 驱动程序,则立即唤醒的原因可能是驱动程序中的错误,这有时会阻止 GPU 挂起。一种可能的解决方法是在休眠前卸载 nouveau
内核模块,并在唤醒后重新加载。为此,请创建以下脚本:
/usr/lib/systemd/system-sleep/10-nouveau.sh
#!/bin/bash case $1/$2 in pre/*) # echo "进入 $2 状态..." /usr/bin/echo "0" > /sys/class/vtconsole/vtcon1/bind /usr/bin/rmmod nouveau ;; post/*) # echo "从 $2 状态唤醒..." /usr/bin/modprobe nouveau /usr/bin/echo "1" > /sys/class/vtconsole/vtcon1/bind ;; esac
第一个 echo 行将 nouveaufb
从帧缓冲控制台驱动程序(fbcon
)中解除绑定。通常它是 vtcon1
,如本例所示,但也可能是另一个 vtcon*
。查看 /sys/class/vtconsole/vtcon*/name
其中哪一个是 frame buffer device [5]。