164 lines
5.4 KiB
Markdown
164 lines
5.4 KiB
Markdown
---
|
||
title: 如何在笔记本电脑插拔电源时自动切换 SCX 调度模式
|
||
date: 2026-06-14 19:24:35
|
||
tags: [Archlinux, 系统优化, 技术分享, SCX调度器]
|
||
---
|
||
|
||
一直觉得scx_lavd和scx_flow支持自动判断负载来选择合适的调度模式很不错,但是我还是更信赖Nvidia工程师出品的scx_bpfland,尤其是CachyOS定义的scx_bpfland低延迟模式和省电模式在我笔记本上的表现都非常好。但有一个痛点——笔记本插电和用电池的场景对调度器的需求完全不同:
|
||
|
||
- **插电时**:希望低延迟、高性能,用得爽,程序编译快游戏帧数高
|
||
- **电池时**:希望尽可能省电,续航优先尽量用久一点,不在乎性能牺牲
|
||
|
||
每次手动敲命令切换实在太蠢了使用CachyOS的scx-manager也不太方便。研究了一下后发现可以借助 Linux 的 `udev` 规则来监听电源事件,实现全自动切换。记录一下折腾过程。
|
||
|
||
---
|
||
|
||
## 现状:scx_loader 配置文件
|
||
|
||
这是我的 `/etc/scx_loader.toml`(CachyOS已提前为各个调度器配好了 mode):
|
||
|
||
```toml
|
||
default_sched = "scx_bpfland"
|
||
default_mode = "LowLatency"
|
||
|
||
[scheds.scx_rusty]
|
||
auto_mode = []
|
||
gaming_mode = []
|
||
lowlatency_mode = []
|
||
powersave_mode = []
|
||
server_mode = []
|
||
|
||
[scheds.scx_p2dq]
|
||
auto_mode = []
|
||
gaming_mode = []
|
||
lowlatency_mode = ["-y"]
|
||
powersave_mode = []
|
||
server_mode = ["--keep-running"]
|
||
|
||
[scheds.scx_bpfland]
|
||
auto_mode = []
|
||
gaming_mode = ["-m", "performance"]
|
||
lowlatency_mode = ["-s", "5000", "-S", "500", "-l", "5000", "-m", "performance"]
|
||
powersave_mode = ["-m", "powersave"]
|
||
server_mode = ["-p"]
|
||
|
||
[scheds.scx_lavd]
|
||
auto_mode = ["--autopower"]
|
||
gaming_mode = ["--performance"]
|
||
lowlatency_mode = ["--performance"]
|
||
powersave_mode = ["--powersave"]
|
||
server_mode = []
|
||
|
||
[scheds.scx_tickless]
|
||
auto_mode = []
|
||
gaming_mode = ["-f", "5000", "-s", "5000"]
|
||
lowlatency_mode = ["-f", "5000", "-s", "1000"]
|
||
powersave_mode = ["-f", "50", "-p"]
|
||
server_mode = ["-f", "100"]
|
||
|
||
[scheds.scx_flash]
|
||
auto_mode = []
|
||
gaming_mode = []
|
||
lowlatency_mode = []
|
||
powersave_mode = []
|
||
server_mode = []
|
||
```
|
||
|
||
仔细观察 `[scheds.scx_bpfland]` 这一块:
|
||
|
||
- `lowlatency_mode` 正好对应插电状态想要的参数:`-s 5000 -S 500 -l 5000 -m performance`
|
||
- `powersave_mode` 正好对应电池状态想要的参数:`-m powersave`
|
||
|
||
不用自己写额外参数配置,直接复用已有的 mode 定义就好。
|
||
|
||
---
|
||
|
||
## 思路
|
||
|
||
`scx_loader` 提供了配套的命令行工具 `scxctl`,可以用来切换调度器和模式。剩下的就是找一个能在电源事件发生时自动执行命令的机制——Linux 内核的 `udev` 子系统天生就是干这个的。
|
||
|
||
整体流程很简单:**插拔电源 → 内核发出 uevent → udev 匹配规则 → systemd-run 异步执行 → scxctl switch 切换模式**。
|
||
|
||
---
|
||
|
||
## 步骤一:测试手动切换命令
|
||
|
||
先确认 `scxctl` 能正常工作(需要 root 权限):
|
||
|
||
```bash
|
||
# 模拟插电 → 低延迟/高性能模式
|
||
sudo scxctl switch -s bpfland -m lowlatency
|
||
|
||
# 模拟拔电 → 省电模式
|
||
sudo scxctl switch -s bpfland -m powersave
|
||
```
|
||
|
||
如果这两条命令都能成功切换,就可以继续下一步。
|
||
|
||
---
|
||
|
||
## 步骤二:编写 udev 规则
|
||
|
||
创建规则文件:
|
||
|
||
```bash
|
||
sudo nano /etc/udev/rules.d/99-scx-power.rules
|
||
```
|
||
|
||
写入以下内容:
|
||
|
||
```udev
|
||
# 插入电源 (AC) → 切换为 bpfland 的 lowlatency 模式
|
||
SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="1", RUN+="/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m lowlatency"
|
||
|
||
# 拔出电源 (Battery) → 切换为 bpfland 的 powersave 模式
|
||
SUBSYSTEM=="power_supply", ATTR{type}=="Mains", ATTR{online}=="0", RUN+="/usr/bin/systemd-run --no-block /usr/bin/scxctl switch -s bpfland -m powersave"
|
||
```
|
||
|
||
> **为什么用 `systemd-run --no-block` 包裹?**
|
||
>
|
||
> `scxctl` 底层通过 D-Bus 与 `scx_loader` 守护进程通信。如果让 `udev` 直接执行,可能会因为 udev 的受限执行环境(缺少 D-Bus session、环境变量缺失)或者命令执行时间过长阻塞 udev 进程导致失败。交给 systemd 后台异步执行是最稳妥的做法。
|
||
|
||
---
|
||
|
||
## 步骤三:重载规则使其生效
|
||
|
||
```bash
|
||
sudo udevadm control --reload-rules
|
||
sudo udevadm trigger
|
||
```
|
||
|
||
`udevadm trigger` 会立即重放一次当前设备状态,所以如果此时已经插着电源,规则会立刻触发一次,可以顺便验证是否生效。
|
||
|
||
---
|
||
|
||
## 步骤四(可选):验证电源类型
|
||
|
||
绝大多数笔记本的外部电源在内核中的设备类型都是 `Mains`,上面规则直接就能用。如果插拔电源发现没反应,检查一下你的电源设备类型:
|
||
|
||
```bash
|
||
cat /sys/class/power_supply/*/type
|
||
```
|
||
|
||
如果输出的不是 `Mains`(比如是 `AC` 或其他名字),把 udev 规则里的 `ATTR{type}=="Mains"` 换成实际类型即可。
|
||
|
||
---
|
||
|
||
## 验证效果
|
||
|
||
插拔电源后,可以用以下命令确认模式是否已经切换:
|
||
|
||
```bash
|
||
scxctl get
|
||
```
|
||
|
||
---
|
||
|
||
## 总结
|
||
|
||
整个过程其实非常简单——核心思路就是「udev 监听电源事件 → systemd-run 异步调用 scxctl 切换 mode」。全程没有写一个新参数,完全复用 `scx_loader.toml` 里已有的 mode 定义。
|
||
|
||
这个方案同样适用于其他调度器:只要在 `scx_loader.toml` 里给对应调度器配好 `lowlatency_mode` 和 `powersave_mode`,然后把 udev 规则里的 `-s bpfland` 换成其他调度器名字就行。
|
||
|
||
这下笔记本插拔电源再也不用惦记着手动切调度器了:D
|