HarmonyOS鸿蒙最新Android Freezer_cachedappoptimizer(2),鸿蒙binder面试题

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!


通过更新原因可以看到,进程的状态变化,或者组件的状态变化会触发重新计算adj。


举例, 当一个APP处理广播时,就会触发重新计算这个APP的adj,此时的更新原因是OOM\_ADJ\_REASON\_START\_RECEIVER:



private final void processCurBroadcastLocked(BroadcastRecord r,
ProcessRecord app) throws RemoteException {

r.receiver = thread.asBinder();
r.curApp = app;
final ProcessReceiverRecord prr = app.mReceivers;
prr.addCurReceiver®;
app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
mService.updateLruProcessLocked(app, false, null);
// Make sure the oom adj score is updated before delivering the broadcast.
// Force an update, even if there are other pending requests, overall it still saves time,
// because time(updateOomAdj(N apps)) <= N * time(updateOomAdj(1 app)).
mService.enqueueOomAdjTargetLocked(app);
mService.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);

}


OomAdjuster在computeOomAdjLSP中会将该app的adj提升至FOREGROUND\_APP\_ADJ:



private boolean computeOomAdjLSP(ProcessRecord app, int cachedAdj,
ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,
boolean computeClients) {

} else if (state.getCachedIsReceivingBroadcast(mTmpBroadcastQueue)) {
// An app that is currently receiving a broadcast also
// counts as being in the foreground for OOM killer purposes.
// It’s placed in a sched group based on the nature of the
// broadcast as reflected by which queue it’s active in.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = (mTmpBroadcastQueue.contains(mService.mFgBroadcastQueue))
? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
state.setAdjType(“broadcast”);
procState = ActivityManager.PROCESS_STATE_RECEIVER;
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making broadcast: " + app);
}
}


### 2.3 Debounce time


当某个APP进程的adj达到CACHED\_APP\_MIN\_ADJ时,CachedAppOptimizer就会调用freezeAppAsyncLSP,在debounce time之后,正式冰冻该进程, 这是为了防止进程在进入cached状态时还有任务没有完成从而间接影响用户体验。


Debounce time 在Android S 上默认是十分钟,这个值是可以在线修改的, 比如改为1秒:



adb shell device_config put activity_manager_native_boot freeze_debounce_timeout 1000


修改后的值重启会失效,变回默认值。


与异步冰冻流程相反,解冻的过程是同步进行的,一个App进程一旦被解冻成功,它就立刻恢复正常运行。


### 2.4 freezeProcess


![](https://img-blog.csdnimg.cn/2693df3b104442cd8af6d5dda8dbb919.png#pic_center)


第一步,检查文件锁"/proc/locks"的状态。这是为了防止冰冻进程持有文件锁引起死锁。考虑到一些特殊场景下,进程在被冰冻的过程中拿住了文件锁,冰冻成功后还会再检查一次,发现持有锁就立刻解冻。


第二步,freeze binder。这一步禁掉该进程对同步binder请求的接收和处理,以及对异步binder请求的处理。该过程需要在100ms内成功,如果进程需要处理的请求过多导致无法完成,则再多给一轮debounce time。


第三步,setProcessFrozen,调用抽象层提供的API冰冻进程。


第四步,再次检查该进程有没有需要处理的binder请求,有则解冻进程,再多给一轮debounce time。这个检查是为一些特殊场景下,进程在被冰冻的过程中产生了binder请求。


### 2.5 setProcessFrozen



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"});
  } else {
      success = SetProcessProfiles(uid, pid, {"Unfrozen"});
  }

  if (!success) {
      signalExceptionForGroupError(env, EINVAL, pid); 

调用cgroup抽象层的SetProcessProfiles, 传入参数“Frozen”。


## 3 cgroup 中间抽象层


cgroup中间抽象层libprocessgroup,主要提供两个功能,其一在启动阶段,根据cgroups.json 来装载具体的cgroup; 其二根据task\_profiles.json来定义对cgroup具体的操作以及参数。 主要代码:



/system/core/libprocessgroup/


### 3.1 cgroups.json


示例文件:



{
“Cgroups”: [
{
“Controller”: “cpu”,
“Path”: “/dev/cpuctl”,
“Mode”: “0755”,
“UID”: “system”,
“GID”: “system”
},
{
“Controller”: “memory”,
“Path”: “/dev/memcg”,
“Mode”: “0700”,
“Optional”: true
}
],
“Cgroups2”: {
“Path”: “/sys/fs/cgroup”,
“Mode”: “0755”,
“UID”: “system”,
“GID”: “system”,
“Controllers”: [
{
“Controller”: “freezer”,
“Path”: “.”,
“Mode”: “0755”,
“UID”: “system”,
“GID”: “system”
}
]
}
}


该文件分为两部分,“Cgroups” 描述cgroup v1 的控制子系统 和"Cgroups2" 描述cgroup v2的控制子系统。如果不了解cgroup 两个版本,可以参看以下网址:


[www.kernel.org/doc/html/la…](https://bbs.csdn.net/topics/618636735) [www.kernel.org/doc/html/la…](https://bbs.csdn.net/topics/618636735)


目前v1和v2是共存的, freezer所使用的是v2,对应根目录是/sys/fs/cgroup, UID和GID都是system。


启动阶段,Init在SetupCgroupAction中通过调用CgroupSetup,解析cgroups.json文件,完成对cgroup的装载。


需要注意的是,cgroups.json文件可能不止一个:



/system/core/libprocessgroup/profiles/cgroups.json //默认文件,都有
/system/core/libprocessgroup/profiles/cgroups_.json //API级别的文件,可能有
/vendor/xxx/cgroups.json //vendor自定义的文件


这三个文件加载的顺序是: 默认–> API级别 --> vendor,于是就存在一个覆盖的流程。只要后面一个文件中定义的"Controller"与前面的字符串相同,就会覆盖前者的定义。


形成的层级结构是这样的:


![](https://img-blog.csdnimg.cn/6a9604b31840409699b9f94fff44786f.png)


Android freezer 在/sys/fs/cgroup下建立了以uid/pid命名的二级子目录,对uid节点的控制会同步应用于其下面所有的pid节点。 uid在应用安装的时候确定, pid在应用使用的时候产生。如果manifest中使用了android:process,则在当前uid目录下建立一个新的pid开头的目录; 如果进程主动调用了fork产生子进程,则不会产生新的pid目录,而是放在父进程目录下,这就意味着父进程被冰冻的时候同目录的子进程也会被冰冻。


### 3.2 task\_profiles.json


示例 task\_profiles.json



{
“Attributes”: [
{
“Name”: “UClampLatencySensitive”,
“Controller”: “cpu”,
“File”: “cpu.uclamp.latency_sensitive”
},
{
“Name”: “FreezerState”,
“Controller”: “freezer”,
“File”: “cgroup.freeze”
}
],

“Profiles”: [
{
“Name”: “Frozen”,
“Actions”: [
{
“Name”: “SetAttribute”,
“Params”:
{
“Name”: “FreezerState”,
“Value”: “1”
}
}
]
},
{
“Name”: “Unfrozen”,
“Actions”: [
{
“Name”: “SetAttribute”,
“Params”:
{
“Name”: “FreezerState”,
“Value”: “0”
}
}
]
},
{
“Name”: “CpuPolicySpread”,
“Actions”: [
{
“Name”: “SetAttribute”,
“Params”:
{
“Name”: “UClampLatencySensitive”,
“Value”: “1”
}
}
]
}
],

“AggregateProfiles”: [
{
“Name”: “SCHED_SP_BACKGROUND”,
“Profiles”: [ “HighEnergySaving”, “LowIoPriority”, “TimerSlackHigh” ]
}
]
}


task\_profiles.json主要由三部分组成, Attributes, Profiles 和AggregateProfiles。


**Attributes**定义属性, 包含名称,控制子系统 以及接口文件。比如上面的示例文件中,定义了名称“FreezerState”的属性, 使用到的控制子系统是freezer, 接口文件是"cgroup.freeze"。


**Profiles** 定义操作。 一种是SetAttribute,引用前面定义的Attributes项,添加参数(Value);一种是JoinCgroup, 包含控制子系统及其路径。


比如上面的示例文件中,名称“Frozen”的项,使用SetAttribute的action,引用“FreezerState” 这个attribute,参数为1,也就是往cgroup.freeze写入1,这正是2.5小节SetProcessProfiles的具体实现。


**AggregateProfiles** 是Profiles项的组合使用。


task\_profiles.json也可能不止一个:



/system/core/libprocessgroup/profiles/task_profiles.json
/system/core/libprocessgroup/profiles/task_profiles_.json
/vendor/xxx/task_profiles.json


加载、覆盖的顺序和cgroups.json类似,按照"Name"来匹配,只要两个文件中定义了同名项,后者就会覆盖前者的定义。


**需要注意的是**,厂商需要定制自己的cgroup 或者task profile时,应该去修改vendor下面对应的文件, 而不要去改默认的或者API对应的文件:



/vendor/xxx/cgroups.json
/vendor/xxx/task_profiles.json


## 4 Kernel


从cgroup的抽象层到 kernel 通过文件写入:




**深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

![](https://img-blog.csdnimg.cn/direct/743b668910224b259a5ffe804fa6d0db.png)
![img](https://img-blog.csdnimg.cn/img_convert/cc06e563801c601c8695d5f113379f22.png)
![img](https://img-blog.csdnimg.cn/img_convert/f18ec81d62ba7093689da4ff7677ff34.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618636735)**

nimg.cn/direct/743b668910224b259a5ffe804fa6d0db.png)
[外链图片转存中...(img-uV3Eb9Pc-1715832333446)]
[外链图片转存中...(img-7fz8YVps-1715832333446)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618636735)**

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值