服务之力:FreeBSD 电源管理

这是 FreeBSD 操作系统的格言 —— “The Power to Serve(服务之力)” —— 也非常契合本文的主题。十年前(是的,光阴荏苒)我甚至还制作了一张带有这个格言的壁纸 —— 仍可在 DeviatArt 页面上获取。

是时候写一篇关于 FreeBSD 电源管理特性的文章了。它也适用于 FreeBSD 桌面 系列,但不限于此。流行的观点似乎是 FreeBSD 如此偏向服务器,以至于没有任何电源管理机制。事实完全不是这样。虽然在桌面上不那么重要(但仍能减少你的电费)或服务器上不那么关键,但在笔记本电脑上正确配置电源管理是非常必要的,这样它们会拥有更长的电池寿命,并运行得更安静。

我写这篇文章,是因为 FreeBSD 手册11.13. Power and Resource Management 章节中并未涉及全部这些信息。FreeBSD on Laptops 文章的 4. Power Management 部分来自 FreeBSD 10.1-RELEASE 的远古时代。FreeBSD Wiki 页面上也有一些信息,但部分已过时。

FreeBSD 在电源管理领域有许多机制:

  • 关闭没有附加驱动的设备

  • 调整 CPU 频率和功耗

  • 支持 CPU 睡眠状态(C1 / C1E / C2 / C3 / …)

  • 启用/禁用大多数 CPU 可用的睿频

  • 每个 USB 设备的电源管理方案

  • SATA / AHCI 通道/控制器电源管理

  • 挂起/恢复支持(包括用笔记本盖子执行)

  • 支持厂商专有工具帮助测量电源管理

  • 工具与 ACPI 支持风扇速度控制

  • 工具与 ACPI 支持屏幕亮度设置

  • 电池容量状态与运行时间估计

  • 网络接口的节能方案

  • 支持 AMD PowerNow!

  • 支持 Intel (Enhanced) SpeedStep

  • 支持 Intel Speed Shift

  • 支持 AMD Turbo Core

  • 支持 Intel 睿频

关于 FreeBSD 系统中不同设置文件的一句话说明:

  • /etc/rc.conf —— 无需重启,只需重新加载守护进程

  • /etc/sysctl.conf —— 无需重启 —— 可在运行时设置

  • /boot/loader.conf —— 这些设置 需要重启

信息

让我们先从如何获取所需信息开始,例如当前 CPU 速度、已使用的 C 状态、USB 设备当前的电源管理模式、电池容量和剩余时间等。

电池

要获取电池信息,你可以使用工具 acpiconf(8)。以下是 acpiconf(8) 在我的主电池(ThinkPad T420s 笔记本)接通电源源时的输出。

……在接入电源适配器后:

现在,当从笔记本电脑上拔掉电源时,Remaining time: 字段会显示该单块电池的剩余时间估计,这里显示为 2:31(两小时三十一分钟)。

下面是我的第二块电池的 acpiconf(8) 输出(位于 ThinkPad T420s 的 ultrabay,替代了 DVD 驱动器)。

……在接入电源适配器后:

在拔掉电源的情况下,它会将第二块电池的 Remaining time: 显示为 1:36

因此,总电池续航时间估计为 4:07。同样的时间,以分钟表示(247),会显示在名为 sysctl(8)hw.acpi.battery.time 中,如下所示。

你还可以通过 sysctl(8) hw.acpi.battery,获取更“完整”的电池信息。

如果连接了电源,hw.acpi.battery.time 将显示为 -1

电池损耗

随着时间的推移,电池会失去其“设计”容量。经过一到两年,这类电池的实际效率可能只剩下原来的 70% 或更低。

检查这些信息所需的全部数据,都可以通过 acpiconf(8) 命令中的 Design capacity:Last full capacity: 获取。我写了个 battery-capacity.sh 脚本,可以告诉你当前电池的效率。下面是该脚本运行时的效果示例。

下面是 battery-capacity.sh 脚本。

CPU

要获取当前 CPU 的信息,你需要使用 dev.cpu,或者使用 dev.cpu.0 来获取第一个物理 CPU 核心的信息。

如果你使用 kldload(8) 命令加载内核模块 coretemp(4),就能获得额外的温度信息。

下面是加载内核模块 coretemp(4) 后,相同的 sysctl(8) dev.cpu.0 输出。

让我说下最有用的值。

CPU 核心温度。

dev.cpu.0.temperature: 49.0C

CPU 支持的 C 状态(此 CPU 支持 C1 和 C2)。

dev.cpu.0.cx_supported: C1/1/1 C2/3/104

CPU C -states 使用统计(仅使用了 C1 状态)。

dev.cpu.0.cx_usage_counters: 16549 0 dev.cpu.0.cx_usage: 100.00% 0.00% last 1489us

CPU 启用的最最深的 C 状态。

dev.cpu.0.cx_lowest: C1

CPU 支持的频率级别及其功耗(‘/’后面的数字表示功耗)。例如 2500/35000 可理解为 2.5 GHz 频率,功耗 35 W,而 2501 表示 Turbo 模式。最低频率为 800 MHz,功耗约 9 W。

dev.cpu.0.freq_levels: 2501/35000 2500/35000 2200/29755 2000/26426 1800/23233 1600/20164 1400/17226 1200/14408 1000/11713 800/9140

CPU 当前频率(使用守护进程 powerd(8)powerdxx(8) 时会变化)。

dev.cpu.0.freq: 800

hw.acpi.thermal.tz0.temperature 也会显示当前热区温度。

要检查你有多少颗 CPU 核心,可以使用以下命令。

如果我的说明不够直观,你还能用 sysctl(8) 命令的参数 -d,如下所示。

lscpu(1)

还有个第三方工具 lscpu(8),可以显示你的 CPU 特性和型号。你需要从软件包中安装它。

要使 lscpu(8) 正常工作,需要加载内核模块 cpuctl(4)

下面是我的双核 CPU 使用 lscpu(8) 的显示效果。

dmesg(8)

此外,dmesg(8) 命令(或长时间运行后查看 /var/run/dmesg.boot 文件)也可以显示你的 CPU 型号和特性信息。

CPU 调频

对于 CPU 调频功能,你可以使用 FreeBSD 基本系统提供的守护进程 powerd(8) ,或者使用来自 FreeBSD Ports 或软件包的 powerdxx(8)。守护进程 powerdxx(8) 旨在更好地调频多核系统,在系统负载适中时不会将所有核心都调到高状态,但有些人可能更喜欢那种方法,以便在执行所有操作时都拥有全部性能,而在闲置时节省功耗。因此,powerd(8) 并不比 powerdxx(8) 更好,反之亦然。它们只是不同的实现,为你的需求提供了更多选择。

无论选择何者,都必须在文件 /etc/rc.conf 中进行配置。

powerd(8)

以下是守护进程 powerd(8) 的选项。

-n 选项用于未知状态——当 powerd(8) 无法确定当前是使用电源还是电池供电时使用。-a 用于电源,-b 用于电池供电。adaptive 设置较“温和”,更有利于延长电池续航时间。hiadaptive 设置更激进,适合在电源供电时使用。-m 选项设置最低 CPU 频率,-M 设置最大 CPU 频率,单位均为 MHz。更多细节请参见 powerd(8) 手册页。

powerdxx(8)

首先,你需要安装它。

然后,它的选项与守护进程 powerd(8) 的选项完全相同。

请查看上文 powerdxx(8) 部分获取标志/参数说明。

十年前,FreeBSD 上的 CPU 调频不像现在这么“简单”,你可以看看我 2008 年的老文章 HOWTO: FreeBSD CPU Scaling and Power Saving

C 状态

可以在 /etc/rc.conf 文件中用以下选项配置 C 状态。

  • performance_cx_lowest

  • economy_cx_lowest

economy_cx_lowest 参数用于电池供电时,performance_cx_lowest 参数用于电源供电时。两者都通过 rc(8) 子系统使用的 /etc/rc.d/power_profile 脚本进行设置。该脚本会设置 hw.acpi.cpu.cx_lowest 参数,从而控制所有 dev.cpu.*.cx_lowest 的值。当你连接或断开电源时,也可以在 /var/log/messages 文件中跟踪这些变化。

通常我只是使用这些值。

上述设置对于大多数系统来说通常已经足够。要检查你的 CPU 支持哪些 C 状态,请看看 dev.cpu.0.cx_supported 的值。

我的 CPU 仅支持 C1 和 C2,但你的 CPU 可能支持更多。我记得曾经使用一台老旧的 Core 2 Duo 笔记本时,从 C1(运行)状态返回到 C2(休眠)状态会有相当“明显”的延迟,因此需要如下设置。此时不使用参数 performance_cx_lowesteconomy_cx_lowest。你可以将第一个核心设置为 C1,其他所有核心设置为 C2。这样即使在电池供电时,你的系统也能完全响应,而其他核心则可以休眠以节省能源。

例如,如果你有 4 个核心,并且最深支持的 C 状态为 C3,那么你可以将如下内容添加到 /etc/sysctl.conf 文件中。

CPU 睿频

启用睿频模式有两种方式。一种是通过设置守护进程 powerd(8)powerdxx(8),将最大频率设置高于 CPU 标称速度。例如,如果你的 CPU 描述为双核 2.3 GHz,则可以使用参数 -M 将最大速度设置为 4000(即 4 GHz)。如果你不使用 CPU 调频守护进程,则可以使用参数 dev.cpu.0.freq,将其设置为 dev.cpu.0.freq_levels 中的最高(第一个)值。

下面是我系统上支持的 CPU 频率级别。

最高值(左侧)为 2501/35000,因此我需要将 dev.cpu.0.freq 参数设置为该值以启用睿频模式。你只需要使用“频率”部分的值,因为如果连同功耗描述一起设置,会导致失败。

如下所示为正确的使用方法。

USB 设备

要列出已连接的 USB 设备,请使用工具 usbconfig(8)

你会看到 pwr 参数(即 power,电源的缩写)显示当前电源设置,可以为:

  • ON

  • OFF

  • SAVE

要为 ugen1.1 设备设置新的 USB 电源选项,也可以使用 usbconfig(8) 工具,并使用参数 power_save,方法如下。

FreeBSD 的 USB 电源管理没有专用的配置文件,因此我们将设置放入通用的 /etc/rc.local 文件中,该文件在 rc(8) 子系统管理的启动过程结束时运行。下面是添加的内容,唯一的例外是我的无线鼠标“Lenovo USB Receiver 联想 USB 接收器”。

对于鼠标和触控设备,不保存电源是个好主意,因为每次使用时都需要等待约一秒钟,这会很烦人。我使用一个 for 循环为除无线 USB 鼠标(识别为“Lenovo USB Receiver”设备)之外的所有 USB 设备设置节能模式。

SATA/AHCI 电源管理

FreeBSD 通过 acpich(4) 驱动提供 AHCI 通道电源管理。这些电源管理设置可以在启动时通过 /boot/loader.conf 文件中的 hint.ahcich.*.pm_level 参数进行配置。我为多达 8 个通道进行了配置,尽管我实际上只有三个通道。

这是因为对不存在的设备进行设置是无害的,不会显示任何错误信息,而且你也不必为不同系统使用不同设置,从而节省时间。下面是 ahci(4) 手册页中关于 hint.ahcich.*.pm_level 的说明。

可能的电源管理选项如下:

  • 0 – 禁用接口电源管理(默认)

  • 1 – 允许设备主动发起 PM 状态改变,主机被动

  • 2 – 每次端口空闲时,主机发起 PARTIAL PM 状态转换

  • 3 – 每次端口空闲时,主机发起 SLUMBER PM 状态转换

  • 4 – 端口空闲 1 毫秒后,驱动发起 PARTIAL PM 状态转换

  • 5 – 端口空闲 125 毫秒后,驱动发起 SLUMBER PM 状态转换

下面是我在 /boot/loader.conf 文件中的设置。

无驱动的设备

FreeBSD 提供了对未附加驱动的设备不供电的节能选项。该选项称为 hw.pci.do_power_nodriver,可以在 /boot/loader.conf 文件中设置。下面是 pci(4) 手册页中的说明。

它可以设置为以下值之一:

  • 0 – 所有设备保持完全供电(默认)。

  • 1 – 类似于‘2’,但存储控制器仍保持供电。

  • 2 – 关闭大多数设备电源(显示器/内存/外设不关闭)。

  • 3 – 关闭所有没有驱动程序的 PCI 设备电源。

下面是我在 /boot/loader.conf 文件中的设置。

pciconf(8) 工具可以显示系统中的设备以及附加到它们的驱动程序。如果设备没有附加驱动,你会看到 *none@**,例如下面的 none0@。你还可以查看大多数驱动的手册页,例如 em(4) 查看 em0 设备,或 xhci(4) 查看 xhci0 设备。

你也可以使用参数 -v 获取更详细的信息。

Nvidia Optimus

如果由于某种原因你的 BIOS/UEFI 固件无法禁用 Nvidia 独显,你可以使用此脚本将其禁用,从而避免消耗系统电源。该操作需要内核模块 acpi_call(4),该模块由软件包 acpi_call 提供。

你可以在 /etc/rc.local 文件中,将其添加到 USB 节能选项之后,使用如下条目。

它会将有效的 ACPI 调用存储在 /root/.gpu_method 文件中,并在每次启动时执行。

挂起与恢复

挂起/恢复机制的最大敌人是硬件 BIOS/UEFI 固件中的错误。例如,有时禁用蓝牙会有所帮助——这是 ThinkPad T420s 中的一个方案。要检查系统支持哪些挂起模式,请查看 sysctl(8) 中的 hw.acpi.supported_sleep_state

要进入 ACPI S3 睡眠状态(挂起),你可以使用 acpiconf(8) 工具或 zzz(8) 工具。

……或者使用 acpiconf(8) 工具。

这与手册页 zzz(8) 中的说明完全相同。

你还可以设置 sysctl(8) 值,使每次关闭笔记本盖时系统进入睡眠状态。为此,将 hw.acpi.lid_switch_state=S3 添加到 /etc/sysctl.conf 文件中。无论是通过命令还是关闭盖子让硬件进入睡眠状态,打开盖子后笔记本都会恢复。当然,如果在执行 zzz(8) 命令后没有关闭盖子,你需要关闭并重新打开盖子,或按下电源按钮以恢复。当然,你也可以对桌面或甚至备份服务器执行挂起/恢复操作,这并不仅限于笔记本。

此外,不同厂商的 ACPI 子系统还有专用内核模块,如下:

  • /boot/kernel/acpi_asus_wmi.ko

  • /boot/kernel/acpi_asus.ko

  • /boot/kernel/acpi_dock.ko

  • /boot/kernel/acpi_fujitsu.ko

  • /boot/kernel/acpi_hp.ko

  • /boot/kernel/acpi_ibm.ko

  • /boot/kernel/acpi_panasonic.ko

  • /boot/kernel/acpi_sony.ko

  • /boot/kernel/acpi_toshiba.ko

  • /boot/kernel/acpi_video.ko

  • /boot/kernel/acpi_wmi.ko

例如,如果你使用的是 IBM/联想 ThinkPad,则需使用内核模块 acpi_ibm.ko

加载所需模块后,你会得到新的 sysctl(8) 值供使用,例如与风扇速度、键盘背光或屏幕亮度相关的值。下面是在加载 acpi_ibm(4) 内核模块后,sysctl(8) 中新增的 dev.acpi_ibm 部分。

这里是一些更有趣的项的说明。

控制麦克风静音按钮上的 LED 灯。

dev.acpi_ibm.0.mic_led

选择是否管理 CPU 风扇(0)或使用厂商默认设置(1)。

dev.acpi_ibm.0.fan

如果启用 CPU 风扇,设置其转速。

dev.acpi_ibm.0.fan_level

显示 CPU 风扇转速(单位 RPM)。

dev.acpi_ibm.0.fan_speed

启用/禁用 WiFi(前提是在 BIOS 中启用)。

dev.acpi_ibm.0.wlan

启用/禁用蓝牙(前提是在 BIOS 中启用)。

dev.acpi_ibm.0.bluetooth

启用/禁用 ThinkLight。

dev.acpi_ibm.0.thinklight

静音/取消静音扬声器。

dev.acpi_ibm.0.mute

扬声器音量。

dev.acpi_ibm.0.volume

屏幕亮度。

dev.acpi_ibm.0.lcd_brightness

在大多数情况下,你可能无需直接使用这些参数,因为通常会使用厂商定义的键盘快捷键(可能需要 Fn 键)或厂商特定的专用按键。但有时你可能希望创建或使用自己的设置,或者需要自定义快捷键,或者想根据 CPU 温度以不同于厂商预设的方式控制风扇转速。这时,这些专用的 ACPI 内核模块就非常有用。

例如,我最近觉得我的 CPU 风扇声音似乎有些大,所以创建了基于 cron(8) 的自定义 acpi-thinkpad-fan.sh 脚本,在 CPU 温度足够低时使用较低或更安静的风扇转速。简单来说,它的工作逻辑是:CPU 温度低于 50°C 时关闭风扇,温度在 50°C 到 60°C 之间时设置为级别‘1’,温度超过 60°C 时设置为级别‘3’。

……下面是它在 crontab(5) 中的条目:

网卡

如果驱动支持节能功能,ifconfig(8) 也要相应选项,称为 powersave,用法如下:

我在自己的 network.sh 网络管理脚本中使用了它,脚本在文章 FreeBSD Network Management with network.sh 中有详细介绍。

厂商工具

FreeBSD 上也有一些厂商提供的工具,例如 powermon(8)。请注意,它需要内核模块 cpuctl(4) 才能工作。

DTrace

动态追踪框架(像 ZFS 一样由 Solaris/Illumos 引入 FreeBSD)在延长电池使用时间方面也可能是款有用的工具。

首先安装软件包 dtrace-toolkit

系统停止节能或唤醒 CPU,通常是因为有任务需要执行。你大多数情况下会使用 ps(1)top(1) 来查看运行情况,但这些工具无法显示具体启动了哪些命令或命令的执行频率。这时 DTrace 就派上用场了。

我们将使用软件包 dtrace-toolkit 中的 /usr/share/dtrace/toolkit/execsnoop 脚本。它会打印系统中执行的每一个命令及其所有参数。当没有命令执行时,它将保持静默,请注意。

下面是我在更新 dzen2 工具栏时的示例输出。

屏幕顶部的信息更新会启动许多进程。这就是为什么我只每 5 分钟刷新一次 dzen2 信息,如果我想获得当前时刻的精确信息和系统状态,我只需点击 dzen2 工具栏,它就会执行所有这些命令并刷新自己。

通过这种方式使用 DTrace,你可以知道是否有不必要的进程消耗了宝贵的电池时间。你可以在我的文章 FreeBSD Desktop – Part 13 – Configuration – Dzen2 中找到相关 dzen2 配置。

其他

ZFS

默认情况下,ZFS 每 5 秒提交一次事务组,这对于 vfs.zfs.txg.timeout 参数是一个良好的默认设置。如果需要,你可以稍微增加该值,例如设为 10。我之所以提到这个参数,是因为很多指南出于性能原因建议将其设置为 1,但请记住,将其设置为 1 会阻止你的磁盘(和 CPU)进入休眠,从而消耗更多电池寿命。

如果你想调整 vfs.zfs.txg.timeout 值,可以在 /boot/loader.conf 文件中设置。

应用程序

延长电池使用时间时,所使用的应用程序也至关重要。例如,Thunar 比 Caja 或 Nautilus 占用更少的 CPU 时间。Geany 文本编辑器比 Scite 或 Gedit 占用更少的 CPU 资源和内存,即便是 GVim 也会占用更多资源。更不用说基于自定义 Openbox/Fluxbox/${YOUR_FAVORITE_WM} 的窗口管理器设置,其 CPU 占用远低于整个 Gnome 或 Mate 环境。

硬件

有时可以通过硬件本身获得更多电池时间。例如,当你为笔记本购买新的固态硬盘时,可以选择速度不是最快但最节能的型号。你可能感受不到性能差异,但会明显获得更长的电池使用时间。

大多数内存模块的电压为 1.5V,但你的笔记本可能支持 LPDDR 模块(1.35V),从而延长电池续航。此外,每根内存条大约消耗 0.5–1.0W 功率,因此使用单根 8 GB 内存条比使用两根 4 GB 内存条能拥有更多电池时间。当然这也有性能上的缺点,因为单条内存无法使用双通道技术,会限制内存速度。一些笔记本甚至有四条内存槽(例如 ThinkPad W520),因此为了更长的电池寿命,可以选择使用两根 8 GB 内存条而不是四根 4 GB 内存条,而不会损失性能。

有时你可以将 DVD 光驱替换为内部辅助电池。例如 Dell Latitude D630、ThinkPad T420s 或 ThinkPad T500/W500。有些厂商提供整块贴合笔记本底部的扩展电池,例如 ThinkPad X220、T420/T520/W520 或第一代 ThinkPad X1 笔记本的扩展电池。

希望这些信息能帮助你在 FreeBSD 上延长一些电池使用时间(或至少节省一些电量) :🙂:

更新 1 – 显卡节能

如果你安装了 graphics/drm-kmod 包,你可能使用的是最新的内核模块 i915kms.ko

要为 Intel 核显设置最大化电源管理,请将以下内容添加到 /boot/loader.conf 文件中:

过去使用的旧设置如下,但现在已不再使用:

更新 2 – AMD CPU 温度

对于 Intel CPU 使用 coretemp(4) 内核模块,而 amdtemp(4) 内核模块可提供 AMD CPU 的额外温度信息。

更新 3 – Suspend/Resume 提示

Suspend/resume 子系统最大的敌人是 BIOS/UEFI 固件中的 BUG。有时禁用 Bluetooth 有助于解决问题——例如 Lenovo ThinkPad T420s 的做法。在 Lenovo ThinkPad X240 上,需要禁用 TPMTrusted Platform Module)。

更新 4 – Intel Speed Shift

我在较老的 2011 年 ThinkPad W520 上运行 FreeBSD,因此这个选项对我来说仍然是个谜——但对于运行更新系统的用户可能会有帮助。

随着 Intel Skylake(第六代)CPU 的引入,FreeBSD 能够利用 Intel Speed Shift 技术。你可以在 hwpstate_intel(4) 手册页中了解更多信息。要启用该功能,请在 /boot/loader.conf 文件中添加以下行:

然后在 /etc/sysctl.conf 文件中,你需要为每个 CPU 线程(而非核心)都添加一行,使用你期望的设置:

这里的 N 代表线程编号。对于双核四线程 CPU,你将有 4 行这样的设置;对于八核 CPU,你将有 8 行。

Y 的取值含义如下:

  • 0 – 最大性能

  • 50 – 平衡(默认)

  • 100 – 最大节能

若希望获得最大化节能,可以为所有线程使用 100,如下所示:

当然,你也可以为单个线程使用全性能,将一个线程设置为平衡,其余线程节能,如下所示:

… 当你接入电源而非使用电池时,你可能希望全部线程都使用无限制性能,将所有值设为 0

以下是具体设置示例。

理想情况下,当使用电源时,你会使用 performance(性能)设置,而在使用电池时则运行 power save(节能)模式。你可以通过下面这个简单脚本实现,并让它在 cron(8) 守护进程中运行。

… 下面是 crontab(5) 的条目。我这里假设你已经具备所需的 doas(1) 权限。如果你使用 sudo(8),请相应修改脚本。

遗憾的是,我目前没有搭载 Intel Speed Shift CPU 的笔记本,所以不确定为前两个线程分别设置 050 是否最佳。也许将所有线程都设为 100 以节省更多电力会更好。当我将来拥有这样的笔记本时,会再做更新 🙂

最后更新于

这有帮助吗?