Files
blog.hifuu.ink/source/_posts/scx-auto.md
2026-06-14 19:45:56 +08:00

164 lines
5.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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