一:背景
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<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://www.toymoban.com/news/detail-858387.html
文章来源地址https://www.toymoban.com/news/detail-858387.html
到了这里,关于Android墓碑机制(AndroidU)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!