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 仅承担事后统计记录的角色。

相关推荐
清 澜7 小时前
c++高频知识点总结 第 1 章:语言基础与预处理
c++·人工智能·面试
豆苗学前端8 小时前
你所不知道的前端知识,html篇(更新中)
前端·javascript·面试
努力学算法的蒟蒻9 小时前
day58(1.9)——leetcode面试经典150
算法·leetcode·面试
UrbanJazzerati9 小时前
统计学的"测谎仪":一文搞懂方差、标准差与“N-1”的秘密
面试
顾林海10 小时前
Android文件系统安全与权限控制:给应用数据上把“安全锁”
android·面试·操作系统
青莲84310 小时前
Android 动画机制完整详解
android·前端·面试
No芒柠Exception10 小时前
从开发到上线的CI/CD 完整流程
后端·面试·架构
CCPC不拿奖不改名11 小时前
网络与API:从HTTP协议视角理解网络分层原理+面试习题
开发语言·网络·python·网络协议·学习·http·面试
程序员飞哥12 小时前
几年没面试,这次真的被打醒了!
java·面试