Android墓碑机制(AndroidU)

Android墓碑机制(AndroidU)进程冻结 getprocessfr

大家好,欢迎来到IT知识分享网。

一:背景

    Android为了加速进程的启动速度,在进程退出前台之后并不会立刻杀死进程,而是会尽可能的保留进程,当用户下次启动进程时,进程就会由冷启动变成温启动,启动速度大大加快。但后台进程过多时,会抢占CPU、memory等有限的系统资源,那么如果保证在不杀死进程的情况下,避免进程抢占系统资源呢?墓碑机制(进程冻结)应运而生。

二:功能详解

  2.1 进程冻结原理

    Android将进程按照优先级从高到低分为: 前台进程 -> 可感知进程 -> 服务进程 -> Cached进程。Freezer通过冻住cached进程来释放这些进程占用的系统资源。

  2.2 进程冻结流程图

    进程冻结的触发条件是进程优先级(adj)的改变,当有进程变成cache进程(adj>=900)时,就会触发cache进程的冻结。关于进程优先级的更新、计算等具体逻辑,可以参考Android进程管理-进程优先级(AndroidU)这篇文章,这里就不详细展开了。

Android墓碑机制(AndroidU)

  2.3 进程冻结功能实现

    2.3.1 进程冻结的初始化

    2.3.1.1 SystemServer.run

    Android启动,会调用到SystemServer.main,在main函数中会调用自身的run函数来启动各个系统服务。具体Android启动流程这里就不详述了,以后会出专门的文章来解析。

 private void run() { ... startBootstrapServices(t);//初始化引导类型系统服务 startCoreServices(t);//初始化核心系统服务 startOtherServices(t);//初始化其他系统服务 startApexServices(t);//初始化apex系统服务 updateWatchdogTimeout(t);//更新看门狗超时时间 ... }
  2.3.1.2 SystemServer.startOtherServices

  会调用ContentProviderHelper.installSystemProviders函数来初始化SettingsProvider

 private void startOtherServices(@NonNull TimingsTraceAndSlog t) { ... t.traceBegin("InstallSystemProviders"); //初始化SettingsProvider mActivityManagerService.getContentProviderHelper().installSystemProviders(); // Device configuration used to be part of System providers mSystemServiceManager.startService(UPDATABLE_DEVICE_CONFIG_SERVICE_CLASS); // Now that SettingsProvider is ready, reactivate SQLiteCompatibilityWalFlags SQLiteCompatibilityWalFlags.reset(); t.traceEnd(); ... }
2.3.1.3 ContentProviderHelper.installSystemProviders

会调用OomAdjuster.initSettings来初始化OomAdjuster

 public final void installSystemProviders() { ... mService.mOomAdjuster.initSettings();//初始化OomAdjuster ... }
2.3.1.4 OomAdjuster.initSettings

会调用CachedAppOptimizer.init来初始化CachedAppOptimizer

 void initSettings() { mCachedAppOptimizer.init(); mCacheOomRanker.init(ActivityThread.currentApplication().getMainExecutor()); ... }
2.3.1.5 CachedAppOptimizer.init

会调用CachedAppOptimizer.updateUseFreezer来初始化进程冻结功能

 public void init() { ... updateUseFreezer();//进程冻结初始化 ... }
2.3.1.6 CachedAppOptimizer.updateUseFreezer

    会先判断CACHED_APPS_FREEZER_ENABLED数据库的值,如果是disabled,会把mUseFreezer设置为false,接着会调用enableFreezer(false);直接返回false。

    如果CACHED_APPS_FREEZER_ENABLED数据库的值是enabled或者use_freezer对应的DeviceConfig的值是true,则会调用isFreezerSupported函数判断驱动是否支持,接着更新Debounce超时时间(10s)。

    如果mUseFreezer是true,调用enableFreezer来使能冻结功能,并启动CachedAppOptimizerThread线程来做进程冻结的操作。

private void updateUseFreezer() { //获取CACHED_APPS_FREEZER_ENABLED数据库的值 final String configOverride = Settings.Global.getString(mAm.mContext.getContentResolver(), Settings.Global.CACHED_APPS_FREEZER_ENABLED); if ("disabled".equals(configOverride)) {//如果是disabled,则不支持进程冻结 mUseFreezer = false; } else if ("enabled".equals(configOverride) || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {//如果CACHED_APPS_FREEZER_ENABLED数据库的值是enabled或者use_freezer对应的config的值是true mUseFreezer = isFreezerSupported();//判断驱动支不支持Freezer updateFreezerDebounceTimeout();//更新Debounce超时时间 updateFreezerExemptInstPkg();//将软件包标记/取消标记为自动安装 } else {//CACHED_APPS_FREEZER_ENABLED数据库没有定义且use_freezer对应的config值为false mUseFreezer = false; } final boolean useFreezer = mUseFreezer; // enableFreezer() would need the global ActivityManagerService lock, post it. mAm.mHandler.post(() -> { if (useFreezer) { Slog.d(TAG_AM, "Freezer enabled"); enableFreezer(true);//使能进程冻结功能 if (!mCachedAppOptimizerThread.isAlive()) { mCachedAppOptimizerThread.start();//启动CachedAppOptimizerThread线程 } if (mFreezeHandler == null) { mFreezeHandler = new FreezeHandler(); } Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(), Process.THREAD_GROUP_SYSTEM);//设置CachedAppOptimizerThread线程的cgroup } else { Slog.d(TAG_AM, "Freezer disabled"); enableFreezer(false); } }); }
2.3.1.7 CachedAppOptimizer.isFreezerSupported

先读取/sys/fs/cgroup/uid_0/pid_*/cgroup.freeze节点的值,如果是1或者0,则调用getBinderFreezeInfo判断驱动是否支持freezer(kernel版本较低的话,默认不会实现BINDER_GET_FROZEN_INFO,就不支持freezer,kernel-5.4, kernel-5.10或者更高版本是支持的),接着调用isFreezerProfileValid检查task_profiles.json是否包含进程冻结相关的Profiles

 public static boolean isFreezerSupported() { boolean supported = false; FileReader fr = null; try { String path = getFreezerCheckPath();// /sys/fs/cgroup/uid_0/pid_*/cgroup.freeze fr = new FileReader(path); char state = (char) fr.read(); if (state == '1' || state == '0') { getBinderFreezeInfo(Process.myPid());//判断驱动是否支持freezer // Check if task_profiles.json contains invalid profiles supported = isFreezerProfileValid();//检查task_profiles.json是否包含进程冻结相关的Profiles } else { Slog.e(TAG_AM, "Unexpected value in cgroup.freeze"); } } ... return supported; } getBinderFreezeInfo实现逻辑中,通过BINDER_GET_FROZEN_INFO 系统调用到 binder driver status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, uint32_t *sync_received, uint32_t *async_received) { int ret = 0; binder_frozen_status_info info = {}; info.pid = pid; #if defined(__ANDROID__) if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info) < 0) ret = -errno; #endif *sync_received = info.sync_recv; *async_received = info.async_recv; return ret; }

  2.3.2 进程冻结的触发

    前文有提到进程冻结的触发条件是进程优先级(adj)的改变,这里以Activity执行onDestroy举例来跟踪触发流程。

    2.3.2.1 ActivityRecord.setState

      Activity调用onDestroy函数最终会调用到ActivityRecord的setState函数,具体的调用逻辑后面会出专门的文章说明,流程很长,这里就不详述了。

     跟踪ActivityRecord.setState函数流程,会调用到OomAdjuster.updateOomAdjLocked来继续更新adj的流程。

frameworks\base\services\core\java\com\android\server\wm\ActivityRecord.java void setState(State state, String reason) { ... switch (state) { case DESTROYING: if (app != null && !app.hasActivities()) { app.updateProcessInfo(true /* updateServiceConnectionActivities */, false /* activityChange */, true /* updateOomAdj */, false /* addPendingTopUid */); } break; } frameworks\base\services\core\java\com\android\server\wm\WindowProcessController.java void updateProcessInfo(boolean updateServiceConnectionActivities, boolean activityChange, boolean updateOomAdj, boolean addPendingTopUid) { ... //这里是延迟去调用mListener的WindowProcessListener::updateProcessInfo方法,而mListener实际是实现了WindowProcessListener接口的ProcessRecord final Message m = PooledLambda.obtainMessage(WindowProcessListener::updateProcessInfo, mListener, updateServiceConnectionActivities, activityChange, updateOomAdj); mAtm.mH.sendMessage(m); } frameworks\base\services\core\java\com\android\server\am\ProcessRecord.java public void updateProcessInfo(boolean updateServiceConnectionActivities, boolean activityChange, boolean updateOomAdj) { synchronized (mService) { ... if (updateOomAdj) { mService.updateOomAdjLocked(this, OOM_ADJ_REASON_ACTIVITY); } } } frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java final boolean updateOomAdjLocked(ProcessRecord app, @OomAdjReason int oomAdjReason) { return mOomAdjuster.updateOomAdjLocked(app, oomAdjReason); }
    2.3.2.2 OomAdjuster.updateOomAdjLocked

    经过adj的计算和应用,如果adj大于cache进程(adj>=900),调用CachedAppOptimizer的freezeAppAsyncLSP函数来进行进程冻结

frameworks\base\services\core\java\com\android\server\am\OomAdjuster.java boolean updateOomAdjLocked(ProcessRecord app, @OomAdjReason int oomAdjReason) { synchronized (mProcLock) { return updateOomAdjLSP(app, oomAdjReason); } } private boolean updateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) { ... return performUpdateOomAdjLSP(app, oomAdjReason); ... } private boolean performUpdateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) { ... updateOomAdjInnerLSP(oomAdjReason, topApp, processes, uids, containsCycle, false); ... } private void updateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, final ProcessRecord topApp, ArrayList<ProcessRecord> processes, ActiveUids uids, boolean potentialCycles, boolean startProfiling) { ... //计算adj computeOomAdjLSP(app, UNKNOWN_ADJ, topApp, fullUpdate, now, false, computeClients); ... boolean allChanged = updateAndTrimProcessLSP(now, nowElapsed, oldTime, activeUids, oomAdjReason); ... } private boolean updateAndTrimProcessLSP(final long now, final long nowElapsed, final long oldTime, final ActiveUids activeUids, @OomAdjReason int oomAdjReason) { ... applyOomAdjLSP(app, true, now, nowElapsed, oomAdjReason); ... } private boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now, long nowElapsed, @OomAdjReason int oomAdjReson) { ... updateAppFreezeStateLSP(app, oomAdjReson); ... } private void updateAppFreezeStateLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) { if (!mCachedAppOptimizer.useFreezer()) {//如果不支持进程冻结,直接返回 return; } ... if (state.getCurAdj() >= CACHED_APP_MIN_ADJ && !opt.isFrozen() && !opt.shouldNotFreeze()) { mCachedAppOptimizer.freezeAppAsyncLSP(app); } else if (state.getSetAdj() < CACHED_APP_MIN_ADJ) { mCachedAppOptimizer.unfreezeAppLSP(app, CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason)); } }
2.3.2.3 CachedAppOptimizer.freezeAppAsyncLSP

  延时10s发送进程冻结的消息,在10s内如果收到进程解冻的消息,会把进程冻结消息移除,也就不会执行进程冻结的操作。调用freezeProcess进行进程冻结。

 void freezeAppAsyncLSP(ProcessRecord app) { freezeAppAsyncLSP(app, updateEarliestFreezableTime(app, mFreezerDebounceTimeout)); } private void freezeAppAsyncLSP(ProcessRecord app, @UptimeMillisLong long delayMillis) { freezeAppAsyncInternalLSP(app, delayMillis, false); } void freezeAppAsyncInternalLSP(ProcessRecord app, @UptimeMillisLong long delayMillis, boolean force) { ... mFreezeHandler.sendMessageDelayed( mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app), delayMillis);//延时10s发送进程冻结的消息 ... } private final class FreezeHandler extends Handler implements ProcLocksReader.ProcLocksReaderCallback { public void handleMessage(Message msg) { switch (msg.what) { case SET_FROZEN_PROCESS_MSG: { ProcessRecord proc = (ProcessRecord) msg.obj; Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,"freezeProcess"); synchronized (mAm) { freezeProcess(proc);//冻结进程 } Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); if (proc.mOptRecord.isFrozen()) {//进程冻结成功 onProcessFrozen(proc);//压缩进程 removeMessages(DEADLOCK_WATCHDOG_MSG); sendEmptyMessageDelayed(DEADLOCK_WATCHDOG_MSG, FREEZE_DEADLOCK_TIMEOUT_MS);//延时1s发送消息检查文件锁的持有情况 } } break; case DEADLOCK_WATCHDOG_MSG: { try { mProcLocksReader.handleBlockingFileLocks(this);//检查是否持有文件锁"/proc/locks" } catch (IOException e) { Slog.w(TAG_AM, "Unable to check file locks"); } } break;
2.3.2.4 CachedAppOptimizer.freezeProcess

先调用freezeBinder冻结进程的Binder端,再调用Process.setProcessFrozen冻结进程自身。

 private final class FreezeHandler extends Handler implements ProcLocksReader.ProcLocksReaderCallback { private void freezeProcess(final ProcessRecord proc) { ... synchronized (mProcLock) { ... try { //先冻结进程的binder端 if (freezeBinder(pid, true, FREEZE_BINDER_TIMEOUT_MS) != 0) { handleBinderFreezerFailure(proc, "outstanding txns"); return; } } ... try { ... //冻结进程 Process.setProcessFrozen(pid, proc.uid, true); ... } ... final UidRecord uidRec = proc.getUidRecord(); if (frozen && uidRec != null && uidRec.areAllProcessesFrozen()) { uidRec.setFrozen(true);//如果UidRecord中所有进程都被冻结了,设置mUidIsFrozen为true postUidFrozenMessage(uidRec.getUid(), true); } ... } } }
2.3.2.5 com_android_server_am_CachedAppOptimizer.com_android_server_am_CachedAppOptimizer_freezeBinder

  freezeBinder是一个native函数,最终会调用到com_android_server_am_CachedAppOptimizer.com_android_server_am_CachedAppOptimizer_freezeBinder

frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp static jint com_android_server_am_CachedAppOptimizer_freezeBinder(JNIEnv* env, jobject clazz, jint pid, jboolean freeze, jint timeout_ms) { jint retVal = IPCThreadState::freeze(pid, freeze, timeout_ms); if (retVal != 0 && retVal != -EAGAIN) { jniThrowException(env, "java/lang/RuntimeException", "Unable to freeze/unfreeze binder"); } return retVal; } frameworks/native/libs/binder/IPCThreadState.cpp status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) { struct binder_freeze_info info; int ret = 0; info.pid = pid; info.enable = enable; info.timeout_ms = timeout_ms; #if defined(__ANDROID__) //这里进行binder冻结的操作,再具体的代码细节,这里就不继续跟了 if (ioctl(self()->mProcess->mDriverFD, BINDER_FREEZE, &info) < 0) ret = -errno; #endif return ret; }
2.3.2.6 Process.setProcessFrozen

setProcessFrozen是一个native函数,会调用到android_util_Process的android_os_Process_setProcessFrozen函数,在此函数里会调用cgroup中间抽象层libprocessgroup的API,通过cgroup本身的freezer子系统来实现进程冻结功能

frameworks\base\core\java\android\os\Process.java public static final native void setProcessFrozen(int pid, int uid, boolean frozen); frameworks\base\core\jni\android_util_Process.cpp void android_os_Process_setProcessFrozen( JNIEnv *env, jobject clazz, jint pid, jint uid, jboolean freeze) { bool success = true; if (freeze) { success = SetProcessProfiles(uid, pid, {"Frozen"});//调用cgroup中间抽象层libprocessgroup的API来实现进程冻结 } else { success = SetProcessProfiles(uid, pid, {"Unfrozen"}); } if (!success) { signalExceptionForGroupError(env, EINVAL, pid); } }
2.3.2.7 processgroup.SetProcessProfiles

通过接口SetProcessProfiles()精细是SetCgroupAction类型的profile,最终调用 ExecuteForProcess(),先通过Controller的GetProcsFilePath()接口获取该profile需要修改的path,最终写的文件就是CGROUP_PROCS_FIL,也就是cgroup.procs文件

system\core\libprocessgroup\processgroup.cpp bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) { return TaskProfiles::GetInstance().SetProcessProfiles( uid, pid, std::span<const std::string>(profiles), false); } system\core\libprocessgroup\task_profiles.cpp bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid, std::span<const T> profiles, bool use_fd_cache) { bool success = true; for (const auto& name : profiles) { TaskProfile* profile = GetProfile(name); if (profile != nullptr) { ... if (!profile->ExecuteForProcess(uid, pid)) { LOG(WARNING) << "Failed to apply " << name << " process profile"; success = false; } } else { LOG(WARNING) << "Failed to find " << name << " process profile"; success = false; } } return success; } bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const { for (const auto& element : elements_) { if (!element->ExecuteForProcess(uid, pid)) { LOG(VERBOSE) << "Applying profile action " << element->Name() << " failed"; return false; } } return true; } bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const { CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, pid); if (result != ProfileAction::UNUSED) { return result == ProfileAction::SUCCESS; } // fd was not cached or cached fd can't be used //通过Controller的GetProcsFilePath()接口获取该profile需要修改的path std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid); unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC))); if (tmp_fd < 0) { PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << procs_path; return false; } if (!AddTidToCgroup(pid, tmp_fd, controller()->name())) { LOG(ERROR) << "Failed to add task into cgroup"; return false; } return true; } 

三:小结

  3.1 如何手动开启或者关闭进程冻结功能

    方式一:开发者选项 “Suspend execution for cached apps”。选择Enabled或者Disabled

    方式二:通过adb命令  adb shell settings put global cached_apps_freezer <enabled|disabled|default>

    以上两种方式都需要重启手机才能生效

  3.2 进程冻结是如何实现的

    进程冻结最终是通过cgroup的Freezer子系统来实现的

  3.3 进程冻结的版本要求

    进程冻结Android版本最低是R,但是R上有bug,推荐在S及之后版本上正式使用

    进程冻结kernel版本要求是kernel-5.4,kernel-5.10或者更高版本

  3.4 进程冻结针对哪类进程

    进程冻结仅仅针对cache进程

  3.5 进程冻结后,何时解冻,如何解冻

    进程冻结后,当进程变成非cache进程时(例如用户手动启动该进程),会触发CachedAppOptimizer的unfreezeAppLSP函数来进行进程解冻

  3.6 进程冻结的优缺点

    优点:冻结cache进程,可以释放cache进程所占用的memory给系统其他进程使用,也防止了该类进程抢占CPU资源

    缺点:如果频繁的冻结、解冻进程,可能会导致卡顿或者响应延迟,影响用户体验

四:番外篇

因为进程冻结最终实现是通过cgroup,这里简单介绍下cgroup。

  cgroups (全称:control groups) 是 Linux 内核提供的一种可以限制单个进程或者多个进程所使用资源的机制,可以对 CPU、memory 等资源实现精细化的控制。cgroup有blkio、cpu、cpuset、memory、freezer等子系统,本文的进程冻结就是通过freezer子系统来实现的。

  cgroup中间抽象层libprocessgroup,主要提供两个功能,其一在启动阶段,根据cgroups.json 来装载具体的cgroup; 其二根据task_profiles.json来定义对cgroup具体的操作以及参数。主要代码路径:/system/core/libprocessgroup/ ,其中cgroups.json和task_profiles.json非常重要。

  后面会写文章来专门介绍cgroup,这里暂时就先介绍这些吧。。

  

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/131837.html

(0)
上一篇 2025-08-04 22:15
下一篇 2025-08-04 22:26

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信