4. 进程管理

4.1 模块边界与层次划分

进程管理模块位于Android架构的Native层与Java框架层之间,核心参与者包括Zygote进程、SystemServer(内含AMS)、lmkd守护进程及所有应用进程。层次划分如下:

  • Zygote层(Native + Java混合):系统启动时由init进程启动,预加载核心类库与资源,通过socket监听AMS的fork请求。
  • AMS层(Java框架):负责进程创建决策、进程优先级计算(oom_adj)及生命周期管理,运行于SystemServer进程。
  • lmkd层(Native):监听内核vmpressure与PSI事件,根据AMS写入的oom_adj分数选择杀进程。
  • 应用进程层(Java + Native):由Zygote fork产生,每个进程拥有独立的oom_adj分数,由LMK机制按需回收。
OMG:边界关键点 – Zygote仅负责进程“孵化”,不参与后续调度;AMS是进程优先级唯一的决策者;lmkd是最终执行者。三者通过Unix域Socket(Zygote ↔ AMS)和procfs(AMS ↔ lmkd)通信。

下图为进程管理核心模块之间的边界与协作概览:

Zygote孵化、进程创建与oom_adj机制架构图 Zygote孵化·进程创建·oom_adj机制协作图 Zygote进程 AMS (SystemServer) lmkd App1
oom_adj=0 App2
oom_adj=6
App3
oom_adj=15
fork请求 (Socket) fork并启动ActivityThread 写入/proc/pid/oom_adj 读取oom_adj,杀高值进程 说明: Zygote通过socket接收fork请求,AMS负责计算oom_adj并写入procfs,lmkd根据oom_adj值及内存压力触发杀进程。 App1为前台进程(oom_adj最小),App3为缓存进程(oom_adj最大,优先被回收)。

4.2 核心组件职责

组件所在进程核心职责
Zygotesystem/zygote预加载Java类、资源、共享库;创建Unix域socket ‘@zygote’;fork并执行ActivityThread main()。
AMS (ActivityManagerService)SystemServer决策何时创建新进程(startProcessLocked);管理进程重要性排序;周期性更新oom_adj(updateOomAdjLocked);处理进程死亡与重启。
lmkdinit派生监听内核vmpressure/PSI事件;读取/proc/pid/oom_adj_score;根据配置阈值选择目标进程发送SIGKILL。
Proc文件系统内核提供/proc/pid/oom_adj与oom_score_adj接口,由AMS写入,lmkd读取。

4.3 控制流与数据流

4.3.1 进程创建流程

  1. AMS发起:当应用组件(Activity/Service/BroadcastReceiver)需要运行时,AMS检查对应进程是否存在。若不存在,调用ProcessList.startProcessLocked()。
  2. Socket通信:AMS通过LocalSocket连接Zygote,发送包含启动参数(类名、uid、gids等)的ProcessStartInfo。
  3. Zygote fork:Zygote在子进程中fork()并执行handleChildProc(),调用RuntimeInit.zygoteInit(),最终通过反射执行目标类的main()(如ActivityThread)。
  4. 进程注册:新进程首次与AMS建立Binder连接(调用attachApplication()),AMS记录进程信息到ProcessList。
关键控制点:Zygote fork后,子进程继承父进程的预加载状态,因此启动速度远快于直接exec。但这也意味着所有Zygote派生的进程共享相同的堆初始化内容,导致“写时复制”内存开销。

4.3.2 oom_adj计算与写入

AMS在每当组件可见性、前台服务状态、最近使用等发生变化时,调用updateOomAdjLocked()。该函数遍历所有进程,按照优先级策略重新计算oom_adj(范围为[-1000, 1000]),其中:

  • 前台Foreground App / Service:oom_adj = 0 ~ -17(系统根据adj值反转计算oom_score_adj)
  • 感知进程(Perceptible):oom_adj = 200
  • 可服务进程(Service):oom_adj = 500 ~ 900
  • 缓存进程(Cached):oom_adj = 900 ~ 1000

计算完成后,AMS通过ProcessList.writeOomAdjToProc写入/proc/[pid]/oom_adj(或oom_score_adj)。lmkd进程周期性或事件驱动检查这些值,结合内存压力决定杀进程。

// 伪代码:AMS中oom_adj写入关键路径
void updateOomAdjLocked() {
    for (ProcessRecord proc : mProcessList) {
        int adj = computeOomAdj(proc);
        proc.setOomAdj(adj);
        // 写入内核接口
        FileUtils.stringToFile("/proc/" + proc.pid + "/oom_score_adj", String.valueOf(adj));
    }
}
风险提醒:AMS后台计算oom_adj的线程是mProcessList.oomAdjUpdateThread,若计算逻辑过于复杂或进程数过多(> 500),可能阻塞Binder线程,导致系统ANR。Android 12+引入ProcessList.updateOomAdj的异步化改进以缓解此问题。

4.4 设计权衡

权衡点方案优势代价
Zygote预加载策略预加载所有系统类、资源、通用dex 应用启动速度提升,首次类加载全由Zygote完成 Zygote堆内存过大(> 10MB),写时复制抢占物理内存;预加载类越多,冷启动时fork开销越大
oom_adj更新时机实时更新 vs 延迟批量更新 实时更新保证LMK迅速准确选择目标 频繁写入procfs增加CPU开销;延迟更新可能导致杀错进程
进程唯一性保证AMS维护进程映射,避免重复创建 组件复用进程,资源节约 进程死锁检测复杂;进程名相同但uid不同时难以区分

4.5 风险点

  • Zygote Crash导致系统重启:Zygote一旦异常退出,所有应用进程成为孤儿进程,init会重新启动Zygote,但已运行的应用无法恢复。
  • oom_adj误杀关键进程:如果AMS未及时更新前台Service的adj值,lmkd可能误杀包含前台通知的进程,造成用户感知卡顿。
  • 进程“僵尸”残留:Zygote fork后若应用初始化失败,进程可能未完成attachApplication而与AMS失联,形成僵尸进程占满/proc节点。
  • Socket通信拥塞:高并发应用启动(如开机自启动或通知洪水)下,Zygote socket缓冲区溢出导致fork超时。
调试技巧:使用 adb shell dumpsys activity oom 查看当前所有进程的oom_adj状态;使用 adb shell cat /proc/[pid]/oom_score_adj 直接读取内核实际值,比对AMS中的预期值以定位adj不一致问题。

4.6 落地步骤

  1. 理解Zygote启动序列:阅读frameworks/base/cmds/app_process/app_main.cpp及com.android.internal.os.ZygoteInit。关注ZygoteConnection.runOnce()如何解析fork请求。
  2. 实现AMS进程创建接口:在ActivityManagerService.startProcessLocked中,确保通过ProcessList.processRecordForStart构建正确的ProcessRecord,并处理跨用户UID。
  3. 配置lmkd阈值:在device///init.rc中调整ro.lmk.*参数,例如ro.lmk.oom_adj_max和ro.lmk.swap_util_max,与AMS的adj范围对齐。
  4. 编写单元测试:模拟AMS计算adj的过程,使用JUnit验证前台/后台/缓存场景下adj的正确性。
  5. 集成测试:使用am start启动多个组件,通过dumpsys activity oom和cat /proc/pid/oom_score_adj验证一致性。
  6. 性能压测:同时启动50+应用,监控Zygote socket延迟和AMS updateOomAdj耗时。

4.7 验收标准

验收项预期结果验证方法
应用冷启动时间从点击到首帧绘制时间 ≤ 800ms(中端设备)atrace 抓取,关注ActivityManager: startProcess到ActivityThread: bindApplication耗时
oom_adj正确性前台Activity所在进程oom_adj ≤ 0;后台缓存进程adj ≥ 900adb shell dumpsys activity oom 对照当前组件状态
低内存处理可用内存低于200MB时,lmkd按adj降序杀进程,不误杀前台Service构造低内存场景(malloc大块内存),观察logcat中lmkd日志和kill 行为
Zygote socket健壮性同时并发启动10个进程,所有fork在3秒内完成使用am start脚本批量运行,统计ProcessRecord.holdDie无僵尸

进程管理模块是AMS稳定性的基石,理解Zygote孵化、AMS决策与lmkd执行的闭环,是深入掌握Android系统资源管控的核心。