Android 14 中 AMS 对进程优先级的完整管控机制

本文基于 android-14.0.0_r1 AOSP 源码 ,对 AMS(ActivityManagerService)如何在 Android 14 中管控进程优先级(OOM 优先级) 进行一次完整、系统、可落源码的总结

重点覆盖:

  • AMS 的设计定位与职责边界
  • Activity 启动后如何触发进程优先级重新评估
  • OomAdjuster 的完整执行流程(含 LSP)
  • OOM 优先级如何最终写入内核
  • 一个完整、确定的 Activity 启动示例

一、总体设计思想

在 Android 14 中,进程优先级管控遵循一个清晰的分层原则:

Activity / Window 决定 "用户感知的重要性",AMS 负责把这种重要性转化为内核可理解的 OOM 优先级。

系统被拆分为三层职责:

层级 职责 关键模块
UI / 状态层 Activity 是否可见、是否可交互 TaskFragment / ATS / ATMS
决策层 进程重要性评估(adj /procState) AMS / OomAdjuster
生效层 内核 OOM 参数写入与回收 ProcessList / lmkd / Kernel

AMS 不管理 Activity 生命周期,只管理 "进程在内存回收中的生死顺序"。


二、进程优先级的核心表示方式

Android 使用两套状态共同描述 "进程优先级"。

1️⃣ oom_score_adj(给内核 /lmkd 使用)

  • 取值范围:-1000 ~ +1000
  • 值越小 → 越不容易被杀
  • 最终写入路径:/proc/pid/oom_score_adj

2️⃣ procState(Framework 内部使用)

  • 例如:

    • PROCESS_STATE_TOP
    • PROCESS_STATE_VISIBLE
    • PROCESS_STATE_CACHED
  • 用途:

    • OOM 决策
    • 后台限制
    • Job / Alarm 策略
    • 进程统计(ProcessStats)

三、AMS 管控进程优先级的核心组件

在 Android 14 中,AMS 内部唯一负责进程优先级计算与调整的核心组件是:
OomAdjuster

其职责包括:

  • 接收 "需要重新评估进程优先级" 的请求
  • 统一计算进程的 adj / procState
  • 判断是否需要把结果同步到 lmkd
  • 触发后置的统计记录(ProcessStats)

四、Activity 启动后触发 AMS 重新评估的真实执行链路

基于 android-14.0.0_r1 源码,Activity 启动后触发 AMS 进行进程优先级更新的正确链路是

plaintext 复制代码
TaskFragment.setResumedActivity
↓
ActivityTaskSupervisor.updateTopResumedActivityIfNeeded
↓
ActivityTaskManagerService.updateOomAdj
↓
ActivityManagerInternal.LocalService.updateOomAdj
↓
ActivityManagerService.updateOomAdjLocked
↓
OomAdjuster.updateOomAdjLocked

关键结论

  • Activity 生命周期变化 不会自动通知 AMS
  • ATMS 在关键状态变化时显式调用 updateOomAdj
  • AMS 是被动进行 OOM 重新评估,而非监听 Activity 生命周期

五、OomAdjuster 内部的完整执行流程(Android 14)

1️⃣ 总入口

java 复制代码
OomAdjuster.updateOomAdjLocked(...)

职责:

  • 判断更新范围(单进程 / 全量)
  • 防止递归和频繁抖动
  • 驱动后续执行流程

2️⃣ 进入 LSP(Low-level Synchronized Path)

updateOomAdjLSP(...)目的:

  • 减少 AMS 大锁持有时间
  • 将计算逻辑与提交逻辑解耦

3️⃣ 实际执行路径

plaintext 复制代码
updateOomAdjLSP
    ↓
performUpdateOomAdjLSP
    ↓
updateOomAdjInnerLSP
    ↓
computeOomAdjLSP   ← 核心算法

4️⃣ computeOomAdjLSP:优先级计算核心逻辑

计算遵循 "从最低假设一路抬升" 的原则:

  1. 默认值:

    • adj = CACHED_APP_ADJ
    • procState = PROCESS_STATE_CACHED_EMPTY
  2. Activity 相关(最重要):

    • 读取 ⁠WindowProcessController
    • ⁠hasForegroundActivities() → ⁠FOREGROUND_APP_ADJ
    • ⁠hasVisibleActivities() → ⁠VISIBLE_APP_ADJ
  3. Service 相关:

    • 前台 Service(FGS)
    • 后台 Service
  4. 依赖传播:

    • ⁠bindService () → adj 继承
    • ⁠ContentProvider → 链式传播
  5. 得到最终结果:

    • ⁠curAdj
    • ⁠curProcState

5️⃣ 是否需要真正生效

java 复制代码
// We don't need to apply the update for the process which didn't get computed
if (state.getCompletedAdjSeq() == mAdjSeq) {
  applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason, true);
}

仅当结果发生变化时才继续。


六、真正的生效路径:applyOomAdjLSP → lmkd → 内核

1️⃣ applyOomAdjLSP

ProcessList.setOomAdj(pid, uid, adj, procState)

2️⃣ ProcessList:system_server 中的 lmkd 直接客户端

在 android-14.0.0_r1 中:

  • ProcessList 在 system_server 启动时:

    • 通过 ⁠LocalSocket
    • 建立到 ⁠/dev/socket/lmkd 的长连接
  • 不经过 JNI / Binder

  • 直接通过 socket 向 lmkd 写入 OOM 信息

3️⃣ socket 协议(简化)

plaintext 复制代码
LMK_SET_PROC_ADJ
pid
uid
adj
procState
  • 二进制协议
  • system_server → lmkd

4️⃣ lmkd 的职责

  • 接收 ⁠LMK_SET_PROC_ADJ
  • 更新内部进程表
  • 唯一允许写入 ⁠/proc//oom_score_adj 的用户态进程

七、完整示例:Activity 启动后的进程优先级调整(Android 14)

场景

  • 后台进程 P(cached)
  • 启动 Activity A
  • A 成为用户可见、可交互的 Activity

1️⃣ Activity 进入 Resume

plaintext 复制代码
TaskFragment.setResumedActivity
    ↓
ActivityTaskSupervisor.updateTopResumedActivityIfNeeded

2️⃣ ATMS 请求重新评估 OOM

plaintext 复制代码
ActivityTaskManagerService.updateOomAdj
    ↓
AMS.updateOomAdjLocked

3️⃣ AMS 内部执行

plaintext 复制代码
OomAdjuster.updateOomAdjLocked
    ↓
updateOomAdjLSP
    ↓
computeOomAdjLSP
        → hasForegroundActivities == true
        → adj = FOREGROUND_APP_ADJ (0)
        → procState = PROCESS_STATE_TOP

4️⃣ 生效路径

plaintext 复制代码
applyOomAdjLSP
    ↓
ProcessList.setOomAdj
    ↓
LocalSocket → lmkd
    ↓
/proc/<pid>/oom_score_adj = 0

八、最终总结

在 Android 14(android-14.0.0_r1)中,AMS 通过 OomAdjuster 对进程优先级进行集中管控;Activity 成为 Top Resumed 后,由 ATMS 显式触发 OOM 重新评估,OomAdjuster 在 LSP 路径中计算进程 adj,并由 ProcessList 通过 socket 直接通知 lmkd 写入 ⁠/proc/pid/oom_score_adj,从而完成进程优先级在内核侧的最终生效,而 ProcessStatsService 仅承担事后统计记录的角色。

相关推荐
Lee川5 小时前
优雅进化的JavaScript:从ES6+新特性看现代前端开发范式
javascript·面试
Lee川9 小时前
从异步迷雾到优雅流程:JavaScript异步编程与内存管理的现代化之旅
javascript·面试
晴殇i11 小时前
揭秘JavaScript中那些“不冒泡”的DOM事件
前端·javascript·面试
绝无仅有11 小时前
Redis过期删除与内存淘汰策略详解
后端·面试·架构
绝无仅有11 小时前
Redis大Key问题排查与解决方案全解析
后端·面试·架构
AAA梅狸猫12 小时前
Looper.loop() 循环机制
面试
AAA梅狸猫12 小时前
Handler基本概念
面试
Wect13 小时前
浏览器缓存机制
前端·面试·浏览器
掘金安东尼14 小时前
Fun with TypeScript Generics:玩转 TS 泛型
前端·javascript·面试
掘金安东尼14 小时前
Next.js 企业级落地
前端·javascript·面试