一:背景

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

二:功能详解

  2.1 进程冻结原理

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

  2.2 进程冻结流程图

    进程冻结的触发条件是进程优先级(adj)的改变,当有进程变成cache进程(adj>=900)时,就会触发cache进程的冻结。关于进程优先级的更新、计算等具体逻辑,可以参考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 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& profiles) {

return TaskProfiles::GetInstance().SetProcessProfiles(

uid, pid, std::span(profiles), false);

}

##system\core\libprocessgroup\task_profiles.cpp

bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid, std::span 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

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

  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,这里暂时就先介绍这些吧。。

  

精彩链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: