摘要
cgroup 最初由 Google 工程师 Paul Menage 和 Rohit Seth 在 2006 年提出,是一种细粒度资源控制的Linux内核机制。于 2007 年合并到 Linux 内核主线中。然而 Google 原生系统直到 Android 11 或更高版本上才支持 CACHE 应用的 CPU 冻结功能。当应用切换到后台并且没有其他活动时,系统会在一定时间内通过状态判断,将进程 ID 迁移到冻结的 cgroup 节点上,实现冻结 CACHE 应用。这项功能可以减少活跃缓存应用在后台存在时所消耗的 CPU 资源,从而达到节电的目的。当应用再次切换到前台时,系统会将该应用的进程解冻,以实现快速启动。


一、冻结场景
主要看哪些场景调用到 freezeAppAsyncInternalLSP 函数
| 冻结场景 |
触发条件 |
执行逻辑 |
操作示例/命令 |
| Adj更新触发冻结 |
进程的OOM_ADJ值≥CACHED_APP_MIN_ADJ(900)且持续10秒无交互 |
系统通过CachedAppOptimizer.freezeAppAsyncLSP延迟10秒执行冻结 |
自动触发,无需手动操作 |
| 广播发送方冻结 |
发送方进程(callerApp)满足isProcessFreezable条件(adj≥900且非豁免) |
1. 检查广播发送数量阈值 2. 调用forceFreezeAppAsyncLSP立即冻结 |
代码逻辑:BroadcastQueueModernImpl.enqueueBroadcastLocked() |
| ADB命令手动冻结 |
通过am freeze命令指定目标进程 |
1. 解析包名/PID 2. 调用runFreeze(pw, true)写入cgroup.freeze=1 |
adb shell am freeze com.tencent.mp adb shell am freeze --pid 9432 |
1.1 Adj 更新触发大于900判断,10秒后执行冻结
冻结流程:前台进程 --> 可感知进程--> 服务进程 --> Cached进程->10秒后->cgroup freeze子系统冻结
CachedAppOptimizer.freezeAppAsyncLSP(ProcessRecord app, @UptimeMillisLong long delayMillis) // 10 秒后冻结->CachedAppOptimizer.freezeAppAsyncInternalLSP(ProcessRecord app, @UptimeMillisLong long delayMillis, boolean force)ActivityManagerService.updateOomAdjLocked→OomAdjuster.updateOomAdjLocked→→OomAdjuster.updateOomAdjLSP→→→OomAdjusterModernImpl.performUpdateOomAdjLSP→→→→OomAdjusterModernImpl.performUpdateOomAdjPendingTargetsLocked→→→→→OomAdjusterModernImpl.partialUpdateLSP→→→→→→OomAdjuster.postUpdateOomAdjInnerLSP→→→→→→→OomAdjuster.updateAndTrimProcessLSP→→→→→→→→OomAdjuster.applyOomAdjLSP→→→→→→→→→OomAdjuster.updateAppFreezeStateLSP adj大于900进程(FREEZER_CUTOFF_ADJ)→→→→→→→→→→CachedAppOptimizer.freezeAppAsyncLSP→→→→→→→→→→→CachedAppOptimizer.freezeAppAsyncInternalLSP 防抖超时阈值(默认10秒)→→→→→→→→→→→→SET_FROZEN_PROCESS_MSG 等10秒(默认10秒防抖)→→→→→→→→→→→→→CachedAppOptimizer.freezeBinder 先冻结Binder再冻结进程的原则→→→→→→→→→→→→→CachedAppOptimizer.freezeProcess 通过cgroup v2 freezer子系统实现→→→→→→→→→→→→→→Process.setProcessFrozen(pid, proc.uid, true) 内核级冻结→→→→→→→→→→→→→→→android_os_Process.android_os_Process_setProcessFrozen(jint pid, jint uid, jboolean freeze)→→→→→→→→→→→→→→→→processgroup.SetProcessProfiles→→→→→→→→→→→→→→→→→TaskProfiles.SetProcessProfiles()→→→→→→→→→→→→→→→→→→TaskProfiles.ExecuteForProcess()→→→→→→→→→→→→→→→→→→→TaskProfiles.ExecuteForProcess()写冻结的文件节点:/sys/fs/cgroup/uid_1000/pid_1234/cgroup.freeze,例如1
1.2 广播发送方若可被冻结,则立刻冻结
防止广播发送方滥用后台资源
当广播发送方进程(callerApp)处于缓存状态(adj≥900)时,若其频繁发送广播,可能通过跨进程通信(Binder)持续占用CPU资源。冻结发送方进程可强制中断其广播发送能力,避免后台进程通过广播保活或消耗系统资源
BroadcastQueueModernImpl.enqueueBroadcastLocked->->CachedAppOptimizer.freezeAppAsyncImmediateLSP(ProcessRecord app) // 立即冻结->->->CachedAppOptimizer.freezeAppAsyncInternalLSP(ProcessRecord app, @UptimeMillisLong long delayMillis, boolean force)/*** 处理广播入队逻辑,并对后台进程的广播发送行为进行限制** 核心设计原则:* 1. 选择性冻结广播发送方(callerApp)而非接收方,原因:* - 发送方通常是后台进程(adj≥900),冻结可防止其滥用广播保活* - 接收方多为用户交互进程(adj≤700),冻结会导致功能中断* - 符合Android非对称优先级策略:牺牲后台进程保前台体验** 2. 异步冻结机制确保系统稳定性:* - freezeAppAsyncImmediateLSP的延迟执行(默认0秒)避免误杀临时性广播* - 超过MAX_FROZEN_OUTGOING_BROADCASTS阈值时立即终止进程*/@Overridepublic void enqueueBroadcastLocked(@NonNull BroadcastRecord r) {// TODO: Apply delivery group policies and FLAG_REPLACE_PENDING to collapse the// outgoing broadcasts.// TODO: Add traces/logs for the enqueueing outgoing broadcasts logic./* 后台进程广播限制逻辑* 触发条件:* 1. 系统启用广播延迟策略(Flags.deferOutgoingBroadcasts)* 2. 发送方为可冻结进程(adj≥900且非豁免状态)*/if (Flags.deferOutgoingBroadcasts() && isProcessFreezable(r.callerApp)) {// 获取或创建进程专属的广播队列final BroadcastProcessQueue queue = getOrCreateProcessQueue(r.callerApp.processName, r.callerApp.uid);/* 广播流量控制* 当单个后台进程发送广播超过阈值时(防DoS攻击):* 1. 记录异常日志(noisy=true触发Log.wtf)* 2. 立即终止进程而非冻结,避免资源持续泄漏** 典型阈值:MAX_FROZEN_OUTGOING_BROADCASTS=5(机型可配置)*/if (queue.getOutgoingBroadcastCount() >= mConstants.MAX_FROZEN_OUTGOING_BROADCASTS) {r.callerApp.killLocked("Too many outgoing broadcasts in cached state",ApplicationExitInfo.REASON_OTHER,ApplicationExitInfo.SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED,true /* noisy */);return;}// 记录广播到进程历史(用于ANR分析和状态恢复)queue.enqueueOutgoingBroadcast(r);mHistory.onBroadcastFrozenLocked(r);/* 执行异步冻结* 关键特性:* 1. 延迟10秒执行,允许短时突发广播* 2. 通过Binder冻结+内存压缩双重优化* 3. 与OomAdjuster协同更新adj值*/mService.mOomAdjuster.mCachedAppOptimizer.freezeAppAsyncImmediateLSP(r.callerApp);return;}}
1.3 adb shell 命令冻结
冻结命令
基础命令格式:adb shell am freeze[options]
:目标应用的包名(如 com.example.app)或进程ID(如 1234)
options]:可选参数(如 --user 0指定用户)
例如
adb shell am freeze com.tencent.mp
adb shell am freeze --pid 9432

ActivityManagerShellCommand.runFreeze->CachedAppOptimizer.forceFreezeAppAsyncLSP(ProcessRecord app)/*** 执行进程冻结/解冻操作的命令行接口实现** @param pw 输出流,用于打印操作结果* @param freeze 操作类型:true=冻结,false=解冻* @return 执行状态:0=成功,-1=失败* @throws RemoteException 远程调用异常** 关键流程说明:* 1. 解析命令行参数(如--sticky标记)* 2. 获取目标进程记录(ProcessRecord)* 3. 执行冻结/解冻操作并输出结果*/@NeverCompile // 标记该方法不参与编译时优化int runFreeze(PrintWriter pw, boolean freeze) throws RemoteException {// 解析命令行附加参数(如--sticky持久化标记)String freezerOpt = getNextOption();boolean isSticky = false;if (freezerOpt != null) {isSticky = freezerOpt.equals("--sticky");}// 通过shell命令获取目标进程信息ProcessRecord proc = getProcessFromShell();if (proc == null) {return -1; // 进程不存在或权限不足}// 打印操作日志(包含进程名、PID和sticky状态)pw.print(freeze ? "Freezing" : "Unfreezing");pw.print(" process " + proc.processName);pw.println(" (" + proc.mPid + ") sticky=" + isSticky);/* 核心冻结/解冻逻辑(需双重锁保护)* 同步块说明:* 1. mInternal锁:保证AMS服务状态一致性* 2. mProcLock锁:防止进程状态并发修改*/synchronized (mInternal) {synchronized (mInternal.mProcLock) {// 设置持久化标记(控制解冻后是否保持低优先级)proc.mOptRecord.setFreezeSticky(isSticky);if (freeze) {/* 强制异步冻结* 内部流程:* 1. 延迟10ms执行(避免锁竞争)* 2. 调用Binder驱动冻结IPC通信* 3. 写入cgroup.freeze=1触发内核冻结*/mInternal.mOomAdjuster.mCachedAppOptimizer.forceFreezeAppAsyncLSP(proc);} else {/* 内部解冻* 参数说明:* @param proc 目标进程* @param reason 解冻原因(0=手动触发)* @param immediate 是否跳过延迟立即执行*/mInternal.mOomAdjuster.mCachedAppOptimizer.unfreezeAppInternalLSP(proc, 0, true);


7661

被折叠的 条评论
为什么被折叠?



