cgroup 与内存控制:内核如何分组记账
cgroup 与内存控制:内核如何分组记账
上一章 lmkd 挑人时参考分组。这一章回答:进程怎么归组,cgroup v2 的 memory 控制器如何 按组记账、限制与统计?
cgroup v2 把一组进程纳入统一资源层级,并通过 memory 控制器提供统计、压力与限制接口。 memory.max、memory.events 和 oom.group 描述约束及结果,但不等同于 lmkd 的选杀算法。
cgroup 负责分组记账与约束
| 常见误解 | 正确理解 |
|---|---|
| cgroup 是“杀进程的工具” | cgroup 是内核的资源分组与记账机制,本身不杀进程 |
| 每个进程一个独立组 | Android 按优先级把进程归入有限几个组(top-app、background 等) |
| 内存限制越严格越好 | 限制不当会触发组内 OOM,反而误杀关键进程 |
核心结论
- Android 用 cgroup v2 把进程按优先级归入有限分组,路径由 Framework 计算。
- memory 控制器提供按组的内存统计、上限(memory.max)与 OOM 事件。
- lmkd 读分组信息评估候选,cgroup 是它的“账本”来源之一。
- 组内 OOM 时内核会按组处理(oom.group),防止误杀无关进程。
- 分组路径与调度参数(如 uclamp)配套,共同影响“前台优先”。
分组与记账主链
逐步解释:
- 确定分组:Framework 按 adj 计算进程应属于哪个 cgroup。
- 归组:把 pid 写入对应 cgroup 的 cgroup.procs。
- 记账:内核统计该组内存 current/max,并记录 OOM 事件。
- 消费:lmkd 读这些统计做决策;组内超限触发 OOM 处理。
oom.group 为什么重要
memory 控制器可以设置 memory.oom.group=1,让组内任意进程 OOM 时按“整组”处理,而不是 只杀触发者。这样可以把“后台整组”一次性回收,避免误杀同一组里的关键进程,也让 lmkd 的组级决策更干净。
源码证据
阅读目标:确认分组路径的计算与写入。
正文指针:ProcessList.java 中 cgroup 路径计算;system/core/libprocessgroup/ 的 SetProcessGroup 实现。
// ProcessList.java(伪代码,行号以 r75 为准)
void setProcessGroup(int uid, int pid, int group) {
// 计算分组路径并写入 cgroup
}这段代码证明: 分组由 Framework 按 adj 决策并写入;内核只负责记账与限流。cgroup 本身不杀进程,但它提供的统计是 lmkd 决策的基础。
验证与排障
环境:开发设备或模拟器;查看 cgroup 需要 root。
adb shell su 0 cat /proc/<pid>/cgroup
adb shell su 0 cat /sys/fs/cgroup/<分组>/memory.current
adb shell su 0 cat /sys/fs/cgroup/<分组>/memory.events/proc/<pid>/cgroup 看进程归属;memory.current 看组内存;memory.events 看 OOM 事件 计数。排查“整组被杀”时,先确认进程分组与组内成员,再看该组的 OOM 事件是否激增。
常见误区
- “cgroup 负责杀进程”:它记账限流,杀进程是 lmkd/OOM。
- “进程分组越细越好”:分组是有限的,过度细分反而增加管理成本。
- “memory.max 越大越好”:上限要与系统水位匹配,否则回收滞后。
- “cgroup 只和内存有关”:还有 CPU、IO 控制器,Android 主要用内存与 CPU 分组。
延伸问题
- cgroup v1 与 v2 的主要差异是什么?
- uclamp 与 cgroup 如何共同保证前台调度?
- memory.events 的 oom_kill 与 lmkd 的 am_kill 如何区分?
- 厂商如何调整分组路径而不破坏框架逻辑?
源码入口
| 文件 | 关键位置 | 作用 |
|---|---|---|
ProcessList.java | cgroup 路径计算 | 分组决策 |
system/core/libprocessgroup/ | SetProcessGroup | 写入归组 |
/sys/fs/cgroup/ | memory 控制器 | 记账与限流 |
/proc/<pid>/cgroup | 归属查看 | 排障入口 |
公共路径:Framework 在 frameworks/base/services/core/java/com/android/server/am/, libprocessgroup 在 system/core/libprocessgroup/。行号以 r75 检索为准。
进程创建阶段如何减少公共页重复,见 Zygote 与写时复制。