调度整体说明
这篇文档定位为 进阶维护说明。
如果你只是想改当前官网对外使用的线程写法,请优先看:
免费版 BPF自定义线程付费版 BPF自定义线程动态调频场景分类 categories.jsonFAS 帧感知
本文按当前磁盘上的真实文件与源码实现整理,目标不是只解释几个字段,而是把这套模块从安装、启动、加载、热更新、运行时判定,到每类 JSON 的真实作用和常见改法一次讲清。
这份文档当前定位如下:
- 这是模块仓库当前唯一保留的总说明
- 它优先描述磁盘上的真实实现,不按旧规划文档口径回退
- 它同时服务三类场景:
- 日常改配置
- 接手维护运行时
- 对照 APK 侧编辑器理解设备侧到底在吃什么
0. 先记住的 10 件事
如果你第一次接手这套调度,先记住下面 10 件事:
- 这不是单一 shell 调参脚本,而是
模式配置 + 线程专项 + FAS + 场景分类 + 运行时识别五层叠加 - 日常最常改的主文件要按版本区分:
- 免费版主看
bin/cpu/<SoC>.json、bin/cpu/<拓扑>.json、config/categories.json - 付费版再额外看
bin/cpu/fas.json
- 免费版主看
bin/cpu/<拓扑>.json虽然文件名还是普通 JSON,但当前主线实际承载的是version: 3的紧凑线程结构- 前台识别当前主链不是只看
cpuset,而是top-resumed-activity -> window -> activities -> /proc PID多级回退 FAS当前主路径是queueBuffer-only,不再回到旧 GPU 主路径- 线程专项已经不只管前台应用,也能管
persistent常驻规则 - persistent 常驻规则已经接入 eBPF 增量刷新,不再只靠粗暴轮询
- 功能开关的真实优先级是:模式默认值 -> 分类规则 -> 应用规则
mode.txt不是只有“当前模式”这一行,它还可以承载应用级模式覆盖- 付费版
fas.json里 APK 同步留下的远端元数据不参与运行时主逻辑,真正主线读取的是defaults和apps
1. 当前仓库真实结构
模块仓库当前最关键的目录和文件如下:
| 路径 | 作用 |
|---|---|
service.sh | 开机入口,等待系统启动后拉起模块运行时 |
customize.sh | 刷入安装期脚本,负责初始配置、环境处理、冲突组件处理 |
activity_diaodu.rc | 启动 bin/activity_diaodu 的外层脚本 |
bin/activity_diaodu | 设备侧实际运行的调度主二进制 |
bin/activity_diaodu.cpp | 主入口源码,包含路径常量、主循环拼装、模式文件读取等 |
bin/activity_config_loading.inc | 所有 JSON 的核心加载逻辑 |
bin/activity_mode_watch.inc | 模式与热更新监听逻辑 |
bin/activity_scene_foreground.inc | 前台包名、Activity、PID 识别逻辑 |
bin/activity_scene_policy.inc | 场景分类与类目策略逻辑 |
bin/activity_scheduler_internal.inc | 线程规则、角色规则、守护与重打动作逻辑 |
bin/activity_scheduler_runtime.inc | 线程调度运行时 |
bin/activity_dynamic_tuning.inc | 动态调频主逻辑 |
bin/activity_fas_sampling.inc | FAS 配置读取与 queueBuffer 采样逻辑 |
bin/activity_fas_runtime.inc | FAS 运行时控制逻辑 |
bin/cpu/ | 模式、线程专项、FAS 配置目录 |
config/categories.json | 场景分类主配置 |
vtools/init_vtools.sh | 生成 /data/powercfg.json 与 /data/powercfg.sh |
action.sh | 卸载/清理相关脚本 |
appopt源码/ | eBPF 相关源码与产物 |
2. 安装链、启动链、运行链
2.1 安装阶段做了什么
刷入模块时,customize.sh 主要做这些事:
- 检查基础系统文件是否存在
- 屏蔽冲突项,例如
/system/vendor/bin/msm_irqbalance - 初始化
/data/adb/modules/muronggameopt/config/mode.txt - 调用
vtools/init_vtools.sh生成/data/powercfg.json和/data/powercfg.sh - 为模块目录、主二进制设置权限
- 检查并处理不同 ROM 的冲突服务:
com.oplus.cosacom.xiaomi.joyosecom.vivo.gamewatchoiface
- 复制
affinity.conf.default到affinity.conf
安装阶段额外要注意两点:
mode.txt首行是全局默认模式,后续每行可以写包名 模式- 当前仓库磁盘里没有看到
CPUcluster.sh文件,但customize.sh仍引用了它;后续如果要继续整理安装链,这一处需要单独核实打包来源
2.2 开机启动链
当前启动顺序如下:
service.sh作为开机入口执行service.sh最多循环等待系统启动完成- 执行
vtools/init_vtools.sh - 写入若干系统节点,例如:
/proc/sys/walt/sched_conservative_pl/proc/oplus-votable/GAUGE_UPDATE/force_val/proc/oplus-votable/GAUGE_UPDATE/force_active/sys/module/cpufreq_bouncing/parameters/enable
- 调用
activity_diaodu.rc activity_diaodu.rc解锁cpufreq目录权限后,后台启动bin/activity_diaodu
2.3 主二进制初始化顺序
bin/activity_diaodu 启动后,初始化顺序大致是:
- 备份并清理旧日志
- 检查 root 权限
- 检测 CPU 型号与平台类型
- 重新挂载部分
/sys为可写 - 检测 CPU 集群和 policy
- 加载
fas.json - 加载线程专项配置
- 加载
categories.json - 读取
mode.txt - 按当前模式加载 SoC 模式配置
- 初始化 cluster policy
- 进入主循环并开启 inotify 热监听
3. 配置文件到底放哪
运行时最常用的真实路径如下:
| 路径 | 说明 |
|---|---|
/data/adb/modules/muronggameopt/config/mode.txt | 当前模式状态文件,同时支持应用级模式覆盖 |
/data/adb/modules/muronggameopt/config/categories.json | 场景分类配置 |
/data/adb/modules/muronggameopt/bin/cpu/fas.json | 付费版 FAS 配置;免费版当前没有这条独立主文件 |
/data/adb/modules/muronggameopt/bin/cpu/<SoC>.json | SoC 模式配置 |
/data/adb/modules/muronggameopt/bin/cpu/<拓扑>.json | 线程专项主配置 |
/data/adb/modules/muronggameopt/bin/cpu/<SoC>.scheduler.json | 可选会话兜底配置 |
/data/powercfg.json | 外部场景控制描述文件 |
/data/powercfg.sh | 外部场景写回入口脚本 |
4. 这套调度真正分成哪几层
可以把当前系统理解成下面五层:
- 模式层
- 定义
powersave / balance / performance / fast - 负责频率、governor、cpuset、模式级动态调频
- 定义
- 线程专项层
- 决定某个应用或常驻进程里的关键线程怎么绑核、怎么上优先级、怎么做 RT
- FAS 层
- 按
queueBuffer节奏推断目标帧率并驱动频率控制
- 按
- 场景分类层
- 按游戏、桌面、相机、扫码、小程序等类目给默认策略
- 运行时识别层
- 负责前台包名、前台 Activity、前台 PID、persistent 常驻规则与 eBPF 增量刷新
最容易记的理解方式:
SoC JSON决定模式地基拓扑 JSON决定线程专项- 付费版
fas.json决定 FPS 档位与 FAS 控制参数;免费版当前没有这层独立文件 categories.json决定“这是什么场景”和“这一类默认偏什么”
5. 当前真实加载顺序
5.1 模式主配置
SoC 模式配置读取路径:
bin/cpu/<SoC>.json
例如:
bin/cpu/SM8850.jsonbin/cpu/SM8750.jsonbin/cpu/MT6991.json
这类文件决定:
cpu_policiescpuset- 模式级
dynamic_tuning - 模式级
features
5.2 线程专项配置
线程专项配置按下面顺序找:
bin/cpu/<SoC>.threads.jsonbin/cpu/<拓扑>.threads.jsonbin/cpu/<拓扑>.json
例如 SM8850 的 6+2 设备,顺序就是:
bin/cpu/SM8850.threads.jsonbin/cpu/6+2.threads.jsonbin/cpu/6+2.json
当前磁盘上的真实情况是:
*.threads.json不一定存在6+2.json、6+1.json、4+4.json这类拓扑文件就是当前线程专项主路径
5.3 场景分类配置
场景分类当前固定读取:
config/categories.json
5.4 FAS 配置
FAS 当前固定读取:
bin/cpu/fas.json
这条独立主文件当前按付费版主线理解;免费版当前磁盘里没有这条独立文件。
5.5 会话兜底配置
代码支持:
bin/cpu/<SoC>.scheduler.json
但磁盘上不一定有;没有时就回到代码默认值,再叠加分类和应用覆盖。
6. 哪些改动能热生效,哪些不能
这是当前最容易踩坑的一块。
6.1 当前已接 inotify 热监听的文件
运行时当前明确监听这些更新:
config/mode.txtconfig/categories.jsonconfig/runtime_license.jsonbin/cpu/fas.json(付费版)bin/cpu/<SoC>.json这类当前 CPU 型号对应的模式配置文件- 当前实际生效的线程专项配置文件
bin/cpu/<SoC>.threads.jsonbin/cpu/<拓扑>.threads.jsonbin/cpu/<拓扑>.json三者里当前命中的那一个
- 当前实际生效的
bin/cpu/<SoC>.scheduler.json
6.2 热生效后的动作
对应行为如下:
- 改
mode.txt- 立即重读模式
- 立即重新加载当前模式的 SoC 配置
- 改
categories.json- 立即重载场景分类规则
- 改
fas.json(付费版)- 立即重载 FAS 应用配置
- 改当前 CPU 型号对应的 SoC 配置
- 立即重载模式配置
- 重新初始化 cluster policy
- 改当前实际生效的线程专项配置
- 立即重载线程规则
- 恢复旧线程已施加的 nice / scheduler 调整
- 重置当前调度会话
- 清理线程缓存、学习样本和 eBPF 目标映射
- 下一轮重新识别前台 PID 和线程
- 改当前实际生效的
scheduler.json- 立即重载会话默认值
- 走同一条线程运行时重置路径,避免旧会话继续残留
6.3 仍然要注意的地方
虽然线程专项现在已经支持热更新,但仍要注意两点:
- 只有“当前实际命中的线程配置路径”会被当成线程专项配置热更新
- 如果你改的是另一个未命中的备选文件,运行时不会自动去切主配置源
另外,下面这些情况下仍建议主动重启服务或设备:
- 你同时改了大量线程规则和运行时代码
- 你怀疑旧进程已经带着旧策略运行太久
- 你想验证完整冷启动路径而不是单纯热更新路径
7. mode.txt 的真实语义
mode.txt 不是只保存一个“当前模式名”,它真实结构是:
powersave
com.tencent.tmgp.sgame balance
com.miHoYo.Yuanshen balance
com.tencent.tmgp.cf balance含义如下:
- 第一行是全局默认模式
- 后续每一行是
包名 模式 - 运行时会先读第一行,再检查后续是否存在针对当前前台包名的覆盖
逐行解释:
- 第一行
- 必须是全局默认模式
- 当前前台没有命中应用覆盖时,就吃这一行
- 后续每一行
- 格式是
包名 模式 - 只对对应包名生效
- 格式是
- 如果同一个包写了多次
- 维护时不建议依赖重复写法
- 最稳妥的做法是只保留一条
当前支持的模式值只有:
powersavebalanceperformancefast
所以如果想给单独某个应用强制走更高模式,不一定非要改 SoC JSON,也可以先改 mode.txt 的应用覆盖。
8. 模式主配置 bin/cpu/<SoC>.json
8.1 这类文件负责什么
SoC 模式配置主要负责:
- 各个模式下每个簇的频率上下限
- governor 与 governor 参数
- cpuset
- 模式级
dynamic_tuning - 模式级
features
8.2 顶层结构
典型结构:
{
"version": "5.8",
"powersave": {},
"balance": {},
"performance": {},
"fast": {}
}字段说明:
| 字段 | 含义 |
|---|---|
version | 配置版本标记 |
powersave | 省电模式 |
balance | 均衡模式 |
performance | 性能模式 |
fast | 极速模式 |
逐字段解释:
version- 配置文件版本标记
- 当前主要用于人工识别和同步版本,不直接决定运行时走哪条逻辑
powersave- 省电模式总配置
balance- 均衡模式总配置
performance- 性能模式总配置
fast- 极速模式总配置
理解要点:
- 这四个模式对象结构相同
- 只是频率上限、cpuset、动态调频参数不同
8.3 模式对象常见字段
{
"cpu_policies": [],
"cpuset": {},
"dynamic_tuning": {},
"features": {}
}| 字段 | 含义 |
|---|---|
cpu_policies | 每个簇或每个 policy 的频率与 governor 配置 |
cpuset | background / foreground / topapp 等组的 CPU 范围 |
dynamic_tuning | 模式级动态调频默认参数 |
features | 模式级功能开关默认值 |
8.4 features
当前模式级功能开关要按版本区分:
- 免费版主线:
scheduler_master、bpf_thread、scene_category、base_profile - 付费版主线:
scheduler_master、dynamic_tuning、fas、custom_thread、dynamic_thread_scheduler、scene_category、base_profile
这层是总默认值,后面还会被分类和应用专项覆盖。
逐字段解释:
| 字段 | 作用 | 典型理解 |
|---|---|---|
scheduler_master | 是否默认开启调度总控 | 关掉后,模式级调度主控能力整体停用 |
dynamic_tuning | 付费版是否默认开启动态调频主链 | 关掉后,这个模式只保留频率基线,不再让动态调频主动调天花板 |
fas | 付费版是否默认允许 FAS 工作 | 关掉后,即使 fas.json 里有应用档位,也不会进入 FAS 主链 |
bpf_thread | 免费版是否默认允许 BPF自定义线程工作 | 免费版当前主线字段 |
custom_thread | 付费版是否默认允许静态自定义线程规则工作 | 控制 main / gfx / render / worker / other / extra:[pattern] / default_action 这条静态规则链 |
dynamic_thread_scheduler | 付费版是否默认允许动态线程运行时工作 | 控制线程学习、守护和动态重打 |
scene_category | 是否默认允许场景分类覆写 | 关掉后不再吃 categories.json 分类覆写 |
base_profile | 是否默认允许模式基础档写入 | 关掉后基础模式动作不参与写入 |
组合理解:
dynamic_tuning=false, fas=true表示允许 FAS,但不允许动态调频主链- 免费版看
bpf_thread=true - 付费版静态线程规则看
custom_thread=true - 付费版动态线程运行时看
dynamic_thread_scheduler=true
8.5 cpu_policies
常见结构:
{
"cluster": 0,
"governor": "performance",
"max_freq": 4608000,
"min_freq": 1977600,
"dynamic_tuning": {
"target_loads": "70 2419200:82 3187200:90",
"target_load": 70,
"load_margin": 5,
"debug_log": 0
},
"params": {}
}字段说明:
| 字段 | 含义 |
|---|---|
cluster | 簇编号,从 0 开始 |
governor | governor 名称 |
max_freq | 当前模式该簇的最高频率 |
min_freq | 当前模式该簇的最低频率(非FAS模式下的底线) |
optimum_freq | 【新增】当前模式该簇的“甜点频率”(FAS驻留频段) |
dynamic_tuning | 该簇自己的动态调频参数 |
params | 额外 governor 参数 |
逐字段解释:
cluster- 指向哪个 CPU 簇
- 不是逻辑优先级,而是实际簇编号
governor- 决定这一簇走哪种 governor 逻辑
- 它会影响后面
params字段到底有没有意义
max_freq- 当前模式下该簇可达到的最高频率
- 动态调频、FAS、boost 都不会突破这个模式上限
min_freq- 当前模式下该簇的最低频率地板
- 注意:在 FAS 模式下,此参数会被忽略,FAS 拥有彻底探底至硬件最低频率(如 384MHz)的权限以追求极致省电。
optimum_freq- 当前模式该簇的“甜点频率”
- FAS 模式下,当未发生严重掉帧时,频率会被极力拉长停留在该数值附近,实现功耗与性能的最佳平衡。
dynamic_tuning- 该簇自己的动态调频参数
- 它的优先级高于模式级总默认值
params- governor 私有参数补丁区
- 更适合微调,不适合当主逻辑总线
8.6 dynamic_tuning
当前主要字段:
| 字段 | 含义 |
|---|---|
enabled | 是否默认开启动态调频 |
target_loads | 分段目标负载表 |
target_load | 默认目标负载 |
load_margin | 负载容差 |
debug_log | 是否打开更多辅助日志 |
当前实现里的一个重要细节:
debug_log打开后,不只是调频日志会多debug_log还会让debug_log.txt、fas_log.txt、affinity_log.txt、stats_log.txt这类辅助日志真正开始写内容
逐字段解释:
enabled- 当前模式或当前簇是否允许动态调频主链真正接管
- 这是动态调频总开关,不是仅调日志
target_loads- 分段目标负载表
- 频率越高,可以配越严格的目标负载
target_load- 没命中更细分段时的默认目标负载
- 值越小越激进,越容易抬频
load_margin- 目标负载的容差
- 值越大越保守,频率变化更平缓
debug_log- 是否开启更多调试日志
- 打开后更适合排查,不适合长期常开
8.7 cpuset
常见字段:
backgroundsystembackgroundforegroundtopapp
逐字段解释:
background- 普通后台线程组可用 CPU
systembackground- 系统后台组可用 CPU
foreground- 前台应用普通线程组可用 CPU
topapp- 顶层前台应用热点线程主要可用 CPU
理解要点:
cpuset是模式地基,不是专项线程策略- 它会影响系统线程默认可跑范围,但不会替代线程专项的精确绑核
建议:
- 这层是模式总地基,不要把应用专项需求直接塞到这里
- 想做某个游戏的个别热线程绑核,优先用线程专项,不要反过来全局改坏模式基线
9. 线程专项 bin/cpu/<拓扑>.json
9.1 这类文件现在到底是什么
这类文件例如:
bin/cpu/6+2.jsonbin/cpu/6+1.jsonbin/cpu/4+4.json
虽然文件名还是普通 JSON,但当前主线实际承载的是 version: 3 的紧凑线程结构。
9.2 顶层结构
{
"version": 3,
"apps": [
{
"friendly": "王者荣耀",
"packages": ["com.tencent.tmgp.sgame"],
"scope": "foreground",
"main": { "match": ["UnityMain"], "cpus": "6-7", "fifo": 1 },
"gfx": { "match": ["UnityGfx*"], "cpus": "4-5", "rr": 10 },
"worker": { "match": ["*worker*"], "limit": 2, "cpus": "4-5", "rr": 5 },
"extra:[binder:*]": { "limit": 8, "cpus": "0-5" },
"other": { "cpus": "1-4" }
}
]
}| 字段 | 含义 |
|---|---|
version | 当前紧凑线程结构版本,建议保持 3 |
apps | 应用级或常驻级专项规则主体 |
9.3 apps[] 里的公共字段
当前两版共同主线先看这些字段:
| 字段 | 作用 |
|---|---|
friendly | 规则显示名,方便日志和 APK 编辑器识别 |
packages | 包名列表,支持一条规则绑定多个包 |
scope | 作用域;不写通常视为前台,persistent/global/system 走常驻路径 |
main / gfx / render / worker / other | 固定角色规则 |
extra:[pattern] | 手写补充线程规则,适合单独抓特殊线程名 |
要按版本理解两点:
- 免费版当前最稳妥的主线就是维护这些公共字段和各角色对象
- 付费版会再额外支持应用级
features、session、default_action
9.4 apps 规则到底怎么理解
apps 里的每一项,不一定是一条“单应用规则”,也可能是一条“多包名共用规则组”。
也就是说:
- 一条规则可以挂一个包名
- 一条规则也可以挂一组包名
- APK 侧现在已经支持按应用展开查看,但底层仍然是规则组结构
典型结构:
{
"friendly": "王者荣耀",
"packages": ["com.tencent.tmgp.sgame"],
"scope": "persistent",
"main": { "match": ["UnityMain"], "limit": 1, "cpus": "6-7", "fifo": 1 },
"worker": { "match": ["*worker*"], "limit": 2, "cpus": "4-5", "rr": 5 },
"extra:[binder:*]": { "limit": 8, "cpus": "0-5" },
"other": { "cpus": "1-4" }
}如果你维护的是付费版,还可以按需再叠:
featuressessiondefault_action
9.5 friendly
用途:
- 给规则取可读名
- 便于日志、APK 编辑器和人工维护识别
9.6 packages
当前线程专项加载器读取的是:
packages
注意:
- 线程专项当前不是像 FAS 那样同时兼容
package + packages - 线程专项这里应优先写
packages
运行时匹配细节:
- 读取时会统一转小写
- 支持显式包名
- 支持带
*、?的通配 - 非通配情况下,当前实现也接受“完整相等或包含命中”
建议:
- 游戏专项尽量写完整包名
- persistent 规则尽量写显式系统进程包名
- 非必要不要用太宽的包含式关键词
9.7 scope
当前运行时理解如下:
| 写法 | 含义 |
|---|---|
| 不写 | 前台作用域 |
persistent | 常驻进程作用域 |
global | 当前也会按 persistent 对待 |
system | 当前也会按 persistent 对待 |
所以当前代码实际口径是:
persistentglobalsystem
这三种写法都会进常驻路径。
9.8 features
这层要按版本理解:
- 免费版当前主线不需要在线程专项
apps[]里依赖这层 - 付费版才更常见应用级
features覆盖
当前付费侧支持字段:
dynamic_tuningfascustom_threaddynamic_thread_scheduler
优先级:
- 模式默认值
- 分类级
features - 应用级
features
逐字段解释:
| 字段 | 作用 | 典型效果 |
|---|---|---|
dynamic_tuning | 控制这个应用是否允许动态调频主链 | false 时,此应用会禁止动态调频,即使模式默认开启 |
fas | 控制这个应用是否允许 FAS | false 时,此应用不会进入 FAS 逻辑 |
custom_thread | 控制这个应用是否允许静态自定义线程规则 | false 时,不吃 main / gfx / render / worker / other / extra:[pattern] / default_action 这条静态规则链 |
dynamic_thread_scheduler | 控制这个应用是否允许动态线程运行时 | false 时,不做线程学习、守护和动态重打 |
组合示例:
dynamic_tuning=false, fas=true, custom_thread=true, dynamic_thread_scheduler=true- 允许 FAS
- 允许静态自定义线程规则
- 允许动态线程运行时
- 禁止动态调频
dynamic_tuning=true, fas=false- 允许动态调频
- 禁止 FAS
9.9 session
这层同样主要按付费版主线理解。
当前支持字段:
learning_duration_msguard_interval_ms
并且当前加载器带下限校验:
learning_duration_ms必须>= 10000guard_interval_ms必须>= 100
优先级:
- 代码默认值
scheduler.json兜底- 分类级
session - 应用级
session
逐字段解释:
| 字段 | 作用 | 说明 |
|---|---|---|
learning_duration_ms | 学习期时长 | 运行时会在这段时间内观察线程特征,再锁定更稳定的分配 |
guard_interval_ms | 守护重打间隔 | 锁定后多久允许重新检查并补打一次规则 |
理解要点:
learning_duration_ms越长,越保守guard_interval_ms越短,越容易频繁重打
9.10 default_action
这是真实执行的兜底动作,不是注释。
当前紧凑结构里,other 是更常见的主兜底角色;如果付费侧还写了应用级 default_action,它会在角色规则没命中时作为额外兜底入口。
常见字段:
clusterscpusmask_hexnicescheduler
逐字段解释:
clusters- 让兜底线程优先落到哪些簇
cpus- 让兜底线程明确落到哪些 CPU
mask_hex- 用十六进制掩码表达 CPU 集
nice- 兜底线程的常规优先级偏移
scheduler- 兜底线程的实时调度策略
理解要点:
default_action是“角色规则没命中时的最后动作”- 它真的会执行,不是注释字段
9.11 main / gfx / render / worker / other
当前固定角色就是这五类:
maingfxrenderworkerother
可以这样理解:
main- 主线程入口
gfx- 图形和渲染前置线程
render- Render 或更接近提交链的线程
worker- 高负载工作线程
other- 当前紧凑结构里的主要兜底角色
9.12 extra:[pattern]
这就是当前主线里对旧 custom 思路的替代写法。
理解方式:
- key 本身带线程模式,例如
extra:[binder:*] - value 仍然是和普通角色相同的角色对象
- 适合补抓某个特殊线程名,而不是再套一层
custom -> selectors -> action
9.13 match
当前角色对象和 extra:[pattern] 里最常见的字段是:
matchlimitcpusclustersnicerrfifo
其中 match 用来写线程名模式列表,例如:
- 普通包含:
"UnityMain" - 通配匹配:
"UnityGfx*"、"*worker*" - 精确匹配:
"=renderengine" - 排名后缀:
"RenderThread@1"、"*worker*@1-2"
理解要点:
match直接贴线程名模式,不再拆成旧selectors.patterns@1、@1-2这类后缀用于同类线程里抓第几个
9.14 limit
limit 表示这个角色最多抓几个线程。
最常见理解:
main通常保持1worker或extra:[pattern]更常需要显式限额limit和宽通配一起使用时要特别谨慎
9.15 cpus / clusters
这两类字段都是在指定线程落点:
cpus- 直接写 CPU 编号范围,例如
6-7、4-5
- 直接写 CPU 编号范围,例如
clusters- 按簇写目标,例如
little、mid、big、prime
- 按簇写目标,例如
建议:
- 优先用
clusters表达意图 - 只有非常明确要写死编号时再用
cpus - 同一条规则不要同时写得过于冲突
9.16 nice / rr / fifo
这三项决定优先级和 RT 方式:
nice- 常规优先级偏移
rrSCHED_RR优先级,值就是 RT 优先级
fifoSCHED_FIFO优先级,值就是 RT 优先级
建议:
rr比fifo更适合作为默认 RT 试探fifo只给极少数关键线程- 不要把大量
worker线程推到高优先级 RT
9.17 main 的真实语义
main 的意思不是“所有同名线程全部吃进主线程规则”。
当前真实语义是:
main只占主规则名额- 同线程名出现多条时,不会把所有同名线程都算成
main - 剩余线程还可以继续流向
extra:[pattern]或other
9.18 线程专项最常见的坑
下面这些最容易把规则写坏:
- 把宽泛的
thread-*和真正主线程关键字塞到同一个角色里 - 上一条还叠
limit: 1 - 误以为旧
threads / custom / selectors / action / safety仍是当前主线 - 给大量
worker配fifo - 对 persistent 常驻规则使用过宽通配
10. config/categories.json
10.1 这类配置负责什么
场景分类负责:
- 识别当前前台属于哪一类
- 给该类提供默认功能开关
- 给该类提供默认会话参数
- 给该类提供默认线程动作兜底
10.2 顶层结构
当前是数组,每一项是一条分类规则。
[
{
"friendly": "王者荣耀",
"packages": ["com.tencent.tmgp.sgame"],
"game": true,
"features": {},
"session": {},
"default_action": {},
"category": "HonorOfKings"
}
]10.3 字段说明
| 字段 | 含义 |
|---|---|
friendly | 可读名 |
category | 类目标识,供代码和日志使用 |
game | 是否属于游戏家族 |
packages | 包名匹配列表 |
activities | Activity 匹配列表 |
processes | 进程名匹配列表,支持 :tool、:peak 等后缀 |
features | 分类级功能开关,付费版更常见 |
session | 分类级会话参数,付费版更常见 |
default_action | 分类级线程动作兜底,两版都常见 |
逐字段解释:
friendly- 这条分类规则的人类可读名
category- 代码里的类目标识
- 不是纯显示字段
game- 是否把这类前台视为游戏家族
packages- 包名命中源
activities- Activity 命中源
processes- 进程命中源,适合
:tool、:peak、:appbrand
- 进程命中源,适合
features- 这类前台的默认功能开关,付费版更常见
session- 这类前台的默认学习期/守护期,付费版更常见
default_action- 这类前台在线程专项没命中时的兜底动作
特别说明:
category- 不只是标签
- 还是场景策略里的实际分流键
game- 用来把该条规则标记进游戏家族
- 更偏运行时辅助判断,不是单纯展示字段
- 免费版当前最常见主线是
friendly / category / packages / activities / processes / default_action - 付费版才更常用分类级
features / session覆盖
10.4 当前匹配优先级
当前分类匹配顺序是:
- 先看
processes - 再看
activities - 最后看
packages
这点很重要,因为:
- 同一个包可能同时承载普通页面、扫码页面、小程序页面
- 仅靠包名不够时,必须靠进程名或 Activity 把它们拆开
10.5 当前已实际使用的类目
当前代码和配置里已实际用到的类目包括:
HonorOfKingsGenshinImpactGameLauncherScannerCameraMiniProgramScrollOptWhiteList
这些类目当前可以这样理解:
HonorOfKings- 王者荣耀专项类目
GenshinImpact- 原神专项类目
Game- 普通游戏类目
Launcher- 桌面、启动器、回桌面快速操作类
Scanner- 扫码识别类场景
Camera- 相机与拍摄类场景
MiniProgram- 小程序与轻应用子进程场景
ScrollOpt- 偏滑动流畅性优化类
WhiteList- 白名单保护类,不希望被误干预
10.6 典型使用场景
最典型的几种:
com.tencent.mobileqq:tool识别扫一扫com.tencent.mobileqq:peak识别相机相关页面com.tencent.mm:appbrand、com.tencent.mm:toolsmp识别小程序- 桌面通过
Launcher包名和小程序启动 Activity 识别
10.7 分类级 features
这层主要按付费版主线理解。
当前支持字段与应用级一致:
dynamic_tuningfascustom_threaddynamic_thread_scheduler
优先级:
- 模式默认值
- 分类级
features - 应用级
features
逐字段解释与线程专项 features 一致:
dynamic_tuning- 控制这类前台是否默认允许动态调频
fas- 控制这类前台是否默认允许 FAS
custom_thread- 控制这类前台是否默认允许静态自定义线程规则
dynamic_thread_scheduler- 控制这类前台是否默认允许动态线程运行时
10.8 分类级 session
这层也主要按付费版主线理解。
支持字段:
learning_duration_msguard_interval_ms
这层主要用于给整类应用提供默认学习期和守护期,再由应用级 session 覆盖。
逐字段解释:
learning_duration_ms- 这类前台默认学习期
guard_interval_ms- 这类前台默认守护重打间隔
10.9 分类级 default_action
作用是:
- 当前前台没命中应用级线程专项时
- 仍然能给该类前台提供一个兜底线程动作
当前这层已经实际接入,不是摆设。
它的字段写法和线程专项 default_action 一样,常见也是:
clusterscpusmask_hexnicescheduler
11. bin/cpu/fas.json
11.1 这类配置负责什么
fas.json 负责:
- FAS 的默认 FPS 档位参数
- 特定应用支持哪些
target_fps - 每个档位的
margin_fps - 每个档位的
kp - 自动档位识别时的兜底档
它不负责:
- 线程绑核
- RT
- cpuset
- 模式总频率上限
11.2 顶层结构
{
"defaults": {},
"apps": [],
"projects": [],
"_murong_remote": {}
}当前运行时真正会读取的主字段是:
defaultsapps
当前 projects 和 _murong_remote 更像 APK 同步或远端元数据,不是运行时主控制入口。
逐字段解释:
defaults- FAS 的全局默认参数
apps- 应用级 FAS 规则主体
projects- APK 同步或远端项目描述信息
- 当前主运行时不依赖它来决定 FAS 档位
_murong_remote- 远端同步元数据
- 当前主运行时不把它当控制字段
11.3 defaults
当前常见字段:
| 字段 | 含义 |
|---|---|
default_target_fps | 默认目标帧率 |
base_margin_fps | 基础帧率容差 |
kp | 默认比例控制系数 |
逐字段解释:
default_target_fps- 某个应用没有显式定义 FPS 档位时的默认目标帧率
base_margin_fps- 默认允许的帧率误差
kp- 默认比例控制强度
- 越大通常越激进
11.4 apps
当前 apps 同时兼容两种包名写法:
packagepackages
其中:
package可以是单包名package也可以是逗号分隔字符串packages可以是数组- 运行时会统一收集并去重
逐字段解释:
friendly- 应用可读名
package- 单包名或逗号分隔包名
packages- 数组写法的多包名
fps_profiles- 这个应用支持的 FPS 档位细分参数
kp- 应用级默认
kp
- 应用级默认
fallback- 应用级默认兜底标记
理解要点:
friendly只提升可读性,不参与实际包名匹配package和packages最终都会被统一成包名集合kp和fallback如果同时在应用级和档位级都写,维护时更建议以档位级为准去理解
典型结构:
{
"friendly": "王者荣耀",
"package": "com.tencent.tmgp.sgame",
"fps_profiles": []
}或者:
{
"friendly": "永劫无间手游",
"packages": [
"com.netease.l22",
"com.netease.lztys"
],
"fps_profiles": []
}11.5 fps_profiles
典型结构:
{
"target_fps": 144,
"margin_fps": 2.5,
"kp": 2.8,
"fallback": true,
"sweet_spot_threshold": 600,
"avalanche_threshold": 800
}字段说明:
| 字段 | 含义 |
|---|---|
target_fps | 目标帧率 |
margin_fps | 对应档位帧率容差 |
kp | 对应档位比例控制系数 |
fallback | 自动档位识别时的兜底档 |
sweet_spot_threshold | 【新增】甜点驻留阈值(默认600) |
avalanche_threshold | 【新增】防雪崩突破阈值(默认800) |
重要参数进阶理解:
sweet_spot_threshold与avalanche_threshold- 这两个参数决定了 FAS 对掉帧的容忍度与激进程度。
- 0 ~
sweet_spot_threshold:常规稳态。频率会被限制在min_freq到optimum_freq(甜点频率) 之间平滑游走,极致省电。 sweet_spot_threshold~avalanche_threshold:长尾预警。主力核开始向max_freq冲刺,但基础小核(如0-5簇)依然会“躺平”在甜点频率不凑热闹。> avalanche_threshold:雪崩救场。所有核心(包括基础小核)解除封印,强制突破甜点频率限制,并配合底线保护机制瞬间拉满,彻底杜绝大掉帧。- 调校建议:电量焦虑党可调高阈值(如 800/950),电竞死忠党可调低阈值(如 400/600)。
FAS 与动态调频(眼睛)的联动机制
- 纯 FAS 就像“蒙着眼睛踩油门”,只看帧率不看负载。为了解决这一痛点,现在 FAS 会将动态调频作为**“动态天花板 (Ceiling)”**。
- 当控制压力
< avalanche_threshold时,如果 FAS 算出的目标频率高于当前 CPU 物理负载允许的天花板,将强制被天花板压制。这意味着如果游戏发生“自旋空转”假死,频率绝不会乱飙,实现 1+1>2 的省电效果。 - 只有当压力突破
avalanche_threshold时,FAS 才会一脚踢开天花板,强制满血救场。
逐字段解释:
target_fps- 这一档要维持的目标 FPS
margin_fps- 这一档允许的误差
kp- 这一档的比例控制强度
fallback- 当自动识别不稳定或未命中时,回退到这一档
建议:
- 同一游戏尽量把常见 FPS 档写全
- 一条应用只保留一个
fallback: true - 高刷档通常要给更宽的
margin_fps
11.6 当前 FAS 实现口径
理解当前 FAS 时,记住这几条:
- 当前主链是
queueBuffer-only - 不再走旧 GPU 主路径
- 目标帧率已经不是“掉到多少就认多少”
- 现在已有高档优先、降档滞回、切档清窗口
fas.json只是给 FAS 提供目标档位和参数,不取代线程专项和模式调频
12. scheduler.json
代码当前支持:
bin/cpu/<SoC>.scheduler.json
建议它只承载会话参数兜底,不要继续往里塞新总线。
推荐结构:
{
"default": {
"learning_duration_ms": 90000,
"guard_interval_ms": 700
},
"performance": {
"learning_duration_ms": 120000,
"guard_interval_ms": 1000
}
}作用:
- 给所有模式一个总默认会话参数
- 再给具体模式做覆盖
- 最后仍然会被分类和应用级
session覆盖
逐字段解释:
default- 全模式通用会话默认值
powersave / balance / performance / fast- 对应模式下的会话覆盖
learning_duration_ms- 当前模式的默认学习期
guard_interval_ms- 当前模式的默认守护间隔
理解要点:
scheduler.json当前只建议做“会话默认值层”- 不建议再往里面继续塞线程动作、FAS 参数或分类信息
13. 前台识别、场景识别、persistent 与 eBPF
13.1 前台识别当前主链
当前前台识别主链是:
dumpsys activity top-resumed-activitydumpsys window windowsdumpsys activity activities/proc + oom_score_adj + cgroup选前台 PIDcpuset top-app只作为辅助
13.2 为什么不是只看包名
因为当前系统不只是识别“前台是哪个应用”,还要进一步识别:
- 是普通前台
- 还是扫码页
- 还是相机页
- 还是小程序子进程
- 还是桌面启动器相关场景
13.3 persistent 常驻规则
当前 persistent 路径已经是实装能力,不是计划项。
主要特点:
- 适合长期存活的系统进程
- 不依赖前台切换才生效
- 会额外扫描指定包名对应进程
- 配合 eBPF 事件做增量刷新
13.4 eBPF 在当前系统里的职责
当前 eBPF 不是单独的“全能调度器”,它主要做两件事:
- 前台应用侧
- 聚合前台 TGID 的线程 runtime
- 帮助跟踪渲染热点线程
- 采样
queueBuffer
- 常驻规则侧
- 监听
fork / exec / sched_setaffinity - 命中后快速重扫 persistent 规则进程
- 监听
也就是说:
- eBPF 负责更快知道“线程变了”
- 真正决定“怎么打动作”的还是 JSON 和用户态运行时
14. 优先级关系
14.1 功能开关优先级
- 模式默认值
- 分类级
features - 应用级
features
14.2 会话参数优先级
- 代码默认值
scheduler.json- 分类级
session - 应用级
session
14.3 线程动作优先级
- 应用级角色规则
main / gfx / render / worker - 应用级
extra:[pattern] - 应用级
other - 应用级
default_action(付费版可选) - 分类级
default_action - 代码默认模板
14.4 常驻规则刷新理解
当前可以这样理解:
- 常规
/proc扫描负责兜底 - eBPF 事件负责加速重扫
- 官调冲突时,前台线程规则和 persistent 路径会一起暂停重打
15. APK 侧界面与模块侧文件怎么对应
为了防止“软件端看着能改,但不知道实际改到哪个文件”,当前可以这样对应:
| APK 侧入口 | 模块侧文件 |
|---|---|
| 应用策略 | config/mode.txt |
| CPU 调度配置 | bin/cpu/<SoC>.json |
| 线程分配 | bin/cpu/<拓扑>.json |
| 场景分类规则 | config/categories.json |
| FAS | bin/cpu/fas.json(付费版) |
理解要点:
- APK 负责“让人能改”
- 模块负责“让机器真跑”
- 只改 APK 页面文案,不改模块字段,最终不会生效
15.1 软件里“功能总开关”到底关在哪里
很多人会把首页“功能开关”里的这几项和对应 JSON 文件混在一起:
- 动态调频
- FAS
- BPF自定义线程
- 场景分类
当前 APK 里,这几项总开关并不是分别直接写到 fas.json、threads.json、categories.json 里,而是统一写回:
bin/cpu/<SoC>.json- 当前模式对象下的
features
也就是实际写回位置是:
{
"powersave": {
"features": {}
},
"balance": {
"features": {}
},
"performance": {
"features": {}
},
"fast": {
"features": {}
}
}比如当前模式如果是 balance,那首页总开关改的是:
balance.features.scheduler_masterbalance.features.dynamic_tuningbalance.features.fas- 免费版主线是
balance.features.bpf_thread - 付费版静态线程规则主线是
balance.features.custom_thread - 付费版动态线程运行时主线是
balance.features.dynamic_thread_scheduler balance.features.scene_categorybalance.features.base_profile
要特别注意一个实现细节:
动态调频总开关除了写features.dynamic_tuning- 还会同步写当前模式下的
dynamic_tuning.enabled
也就是说,首页总开关当前真实对应关系如下:
| APK 开关名 | 实际写回字段 | 作用理解 |
|---|---|---|
| 总控 | <当前模式>.features.scheduler_master | 总闸门;关掉后调度主控能力整体停用 |
| 动态调频 | <当前模式>.features.dynamic_tuning + <当前模式>.dynamic_tuning.enabled | 控制当前模式是否允许动态调频主链运行 |
| FAS | <当前模式>.features.fas | 控制当前模式是否允许 FAS 主链运行 |
| BPF自定义线程 | 免费版看 <当前模式>.features.bpf_thread | 控制免费版线程规则总开关 |
| 自定义线程 | 付费版看 <当前模式>.features.custom_thread | 控制付费版静态线程规则是否生效 |
| 动态线程 | 付费版看 <当前模式>.features.dynamic_thread_scheduler | 控制付费版线程学习、守护和动态重打 |
| 场景分类 | <当前模式>.features.scene_category | 控制当前模式是否允许吃 categories.json 的分类覆写 |
| 模式基础 | <当前模式>.features.base_profile | 控制当前模式基础档是否参与写入 |
要这样理解这几个“总开关”:
动态调频总开关不是去改fas.jsonFAS总开关不是去删fas.json里的应用配置BPF自定义线程总开关不是去改线程规则文件场景分类总开关不是去改categories.json内容本身
它们本质上都是:
- 当前模式级别的运行时准入开关
- 决定“允不允许这条能力在当前模式下参与调度”
所以如果你的目标不是“当前模式下一键关总开关”,而是“改单独配置内容”,对应关系应该是:
- 想改单独动态调频参数:
- 去
bin/cpu/<SoC>.json - 主要看
<mode>.dynamic_tuning
- 去
- 想改单独 FAS 档位、应用列表:
- 去
bin/cpu/fas.json
- 去
- 想改单独 BPF自定义线程规则、绑核规则、RT 规则:
- 去
bin/cpu/<拓扑>.json
- 去
- 想改单独场景分类命中条件、分类级 features/session/default_action:
- 去
config/categories.json
- 去
再说得更直接一点:
- 首页“功能开关”改的是当前模式的总阀门
- 配置页里的
cpu.json / fas.json / 线程分配 / 场景分类规则改的是各条能力自己的配置内容
16. 日志、辅助文件、运行时产物
16.1 当前主要日志路径
| 路径 | 说明 |
|---|---|
/data/adb/modules/muronggameopt/config/log.txt | 主日志 |
/data/adb/modules/muronggameopt/config/debug_log.txt | 动态调频辅助日志 |
/data/adb/modules/muronggameopt/config/fas_log.txt | FAS 辅助日志 |
/data/adb/modules/muronggameopt/config/affinity_log.txt | 线程绑核与优先级辅助日志 |
/data/adb/modules/muronggameopt/config/stats_log.txt | 运行时统计日志 |
/data/adb/modules/muronggameopt/config/log_backups/ | 日志备份目录 |
16.2 哪些日志默认会比较安静
辅助日志并不是一直全量写。
当前实现里,下面这些更依赖 debug_log:
debug_log.txtfas_log.txtaffinity_log.txtstats_log.txt
所以如果你在调动态调频、FAS、绑核,却发现辅助日志很少,先检查当前模式或 cluster 的 dynamic_tuning.debug_log 是否打开。
16.3 运行时还会写什么
当前运行时还会写:
- trace marker
/sys/kernel/tracing/trace_marker/sys/kernel/debug/tracing/trace_marker
/data/powercfg.json/data/powercfg.sh
也就是说,当前系统不仅有文本日志,也有面向 systrace/atrace 的即时打点能力。
17. 最常见的改法
17.1 新增一个游戏专项
建议顺序:
- 先在
bin/cpu/<拓扑>.json的apps里加规则 - 补
friendly - 补
packages - 先写
main / gfx / render / worker - 再按需要补
extra:[pattern]和other - 付费版再按需要补
features / session / default_action - 如果这游戏要单独 FAS 档位,再去
bin/cpu/fas.json补fps_profiles - 如果它需要独立场景默认策略,再去
config/categories.json补一条类目或专用条目
17.2 新增一个前台类目
建议顺序:
- 在
config/categories.json新增一条规则 - 先写
friendly - 必写
category - 按需要写
packages / activities / processes - 先写
default_action;付费版再按需要写features / session
17.3 给某个应用临时做强绑核
优先改:
<角色>.clusters- 必要时再用
<角色>.cpus
不建议上来就全局改 cpuset。
17.4 给某类应用默认走中高簇
优先改:
categories.json里的default_action.clusters
例如:
{
"default_action": {
"clusters": ["middle", "big"]
}
}17.5 给某个常驻系统进程做专项
建议:
- 在线程专项里新增一条
apps规则 packages写显式进程包名scope写persistent- 谨慎设置关键角色、
other和 RT 参数
18. 当前边界与维护建议
18.1 当前明确不做的事
- 不引入热反馈调度
- 不恢复
SurfaceFlinger/gfxinfo旧回退链 - 不为了极限帧率把所有模式长期锁高功耗
- 不把一切场景逻辑全塞回线程角色层
18.2 当前维护建议
- 改字段前先确认 APK 侧是否也要同步
- 改运行时逻辑前先以磁盘真实状态为准,不要按旧设计稿脑补
- 想改模式和频率,优先看
bin/cpu/<SoC>.json - 想改线程专项,优先看
bin/cpu/<拓扑>.json - 想改分类,优先看
config/categories.json - 想改 FAS 档位,优先看
bin/cpu/fas.json - 想让改动即时生效,先确认这类文件是否被 inotify 监听
- 线程专项现在已支持热生效,但大改后仍建议顺手验证一次完整冷启动路径
19. 一句话总记忆
如果你只想记住一段最短的话:
- 模式和频率改
bin/cpu/<SoC>.json - 线程专项改
bin/cpu/<拓扑>.json - 场景分类改
config/categories.json - FAS 档位改
bin/cpu/fas.json - 当前模式状态和应用模式覆盖改
config/mode.txt
