进程管理之进程优先级

↘锁芯ラ 提交于 2019-12-28 16:05:38

进程优先级实际上是系统对进程重要性的一个客观评价。根据这个评价的结果来为进程分配不同的系统资源,这个资源包括内存资源和CPU资源。为了保证“公平公正”的评价每个进程,Google工程师为此设计了一套评价系统。本文试图从面相对象的角度和功能设计者角度来理解该功能,具体思路如下:

要实现一个功能,有必要的时候我们会去为他制定一些属性,对于设计者而言这些属性肯定是必须的,因此作为使用者我们必须理解这些属性的意义是什么。

同样要实现一个功能,设计者会定制一些该功能特有的行为,在代码里面体现为特定的函数,那么我们作为使用者,必须了解以下几个面:a.该行为的能产生什么样的结果,即行为的目的,b.该行为的触发条件,即什么时候会用到这些方法,为什么在这个时候要去触发?

本文会从如下三个关键词和三个关键方法来展开分析和讨论

  • adj:通过调整oom_score_adj来影响进程寿命(Lowmemorykiller杀进程策略);
  • schedGroup:影响进程的CPU资源调度与分配;
  • procState:从进程所包含的四大组件运行状态来评估进程状态,影响framework的内存控制策略。比如控制缓存进程和空进程个数上限依赖于procState,再比如控制APP执行handleLowMemory()的触发时机等
  • updateOomAdjLocked更新
  • computeOomAdjLocked计算
  • applyOomAdjLocked应用

ADJ

AMS中有几个和adj相关的非常重要的方法:

  1. updateOomAdjLocked(ProcessRecord app, int cachedAdj,

ProcessRecord TOP_APP, boolean doingAll, long now)

  1. updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll)
  2. updateOomAdjLocked()
  3. computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,

            boolean doingAll, long now)

  1. applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,

            long nowElapsed)

这个几个方法的关系如下:updateOomAdjLocked(更新),包括computeOomAdjLocked(计算)applyOomAdjLocked(应用)这俩个动作!

                           

 

总体算法示意图

 

              

 

 

updateOomAdjLocked触发场景

该方法作为adj更新的核心方法,设计者用它来更新进程的adj。我们在前言说过进程优先级的评价是“公平公正”的,但是“公平公正”并不等同于平均,平均分配系统资源实际上是不符合用户体验的需求的。用户必定希望自己想用的进程能够得到足够的系统资源。没有使用的进程尽可能的分配较少的资源。因此,设计者必须了解当前的进程所处的场景。然后,基于不同场景来及时的更新adj,及时的重新分配系统资源。因此,设计必须在Android系统中进行“埋点”。

下面是搜索的updateOomAdjLocked,该方法的埋点结果。涉及的类

                          

                          

  updateOomAdjLocked大致可以分为俩类:1.带参数的(在后面把它称为updateOomAdjLockedA:针对指定的单个进程更新优先级,如果oomAdjAll这个参数为true,就有可能去调用updateOomAdjLockedB),2.带参数的(在后面把他成为updateOomAdjLockedB对所有进程更新优先级)。

 

1.ActiveServices

  • bindServiceLocked:开始是由于AMS中的binderervices被调用,即该场景可以理解为当有服务绑定的时候,就会有updateOomAdjLockedB,当然由于绑定服务只单个应用的行为,那么updateOomAdjLockeB也是针对单个应用的。之所有绑定服务的时候,需要调用updateOomAdjLockedB,是因为,服务进程的adj是由,client和server端之间的关系来决定的。
  • unbindServiceLocked: 最开始是由于AMS中的unbindService被调用,即该场景可以理解为当有服务解绑的时候,就会有updateOomAdjLockedB。这个原因和第一点是一样的,服务的解绑也会导致client端和server之间的关系发生变化。
  • unbindServiceLocked这里还进行了updateOomAdjLockedA
  • realStartServiceLocked:这个最开始是由于AMSstartServices或者attachApplicationLocked调用。即场景可以理解为:有startServices或者新的进程起来,会去updateOomAdjLockedA.这原因可以理解为:一个服务或者进程起来,它会导致系统其它进程的状态发生变化,(比如一个进程的前后台关系),因此要调用就会有updateOomAdjLockedA,
  • sendServiceArgsLocked:

服务起来或者进程别杀的时候。这个函数是一个私有函数,被调用的地方如下:

                

           

 

 

三个参数为adj是否已经调整过调整或就不在进行更新

有三处调用:

bringUpServiceLocked服务起来的时候。

realStartServiceLocked :第三个参数为false因此,不用更新。第二不用更新的原因是,第二方法最终是在realStartServiceLocked调用的,在该方法调用之前,前面已经调用过updateOomAdjLockedA。

cleanUpRemovedTaskLocked:进程被杀死的时候调用

 

BroadcastQueue

1.processCurBroadcastLocked:在发送广播(sendPendingBroadcastsLocked

和处理广播processNextBroadcast的时候调用updateOomAdjLockedA

2. deliverToRegisteredReceiverLocked:处理有序广播的时候,调用updateOomAdjLockedB,由于是挨个处理,因此调用针对单个应用的。

3. processNextBroadcastLocked在处理完最后一个有序广播后,调用updateOomAdjLockedA,需要重新更新所以进程的adj

 

3.ActivityManagerService

1.onWakefulnessChanged:充电过程中需要更新adj,调用updateOomAdjLockedA

2.setSystemProcess在systerm_server进程起来的时候调用updateOomAdjLockedA更新全部应用的adj

3.AMS构造函数中:updateOomAdjLockedA

4.startActivityAsUserEmpty空进程创建的时候调用updateOomAdjLockedB

5.appDiedLocked:进程死掉的时候,调用updateOomAdjLockedA

6.killAllBackgroundProcesses:用shell命令把进程杀死的时候会调用updateOomAdjLockedA

7.killPackageProcessesLocked:通过包名杀进程的时候updateOomAdjLockedA

8.attachApplicationLocked进程创建的时候updateOomAdjLockedA

9.importanceTokenDiedupdateOomAdjLockedA

 

4.ActivityStack

1. resumeTopActivityInnerLockedActivity所在的栈activity见的时候

2. finishSubActivityLocked:最开始是由于Activityfinish,finishd 时候会调用updateOomAdjLockedA

3. finishVoiceTaskactivities are using voicefinish的时候被调用

4. finishCurrentActivityLockedupdateOomAdjLockedA

5. destroyActivityLockedActivity被销毁的调用updateOomAdjLockedA

 

5.ActivityStackSupervisor

  1. realStartActivityLocked新启动一个actvity

updateOomAdjLocked 逻辑分析

该函数的算法过程示意图:

                      

 

1.初始化参数

                              

                              

 

 

2.为各类进程分配adj

                            

 

                                  

3.计算各个类型进程的数量杀进程

                            

                          

  1. 计算和设置内存level

ProcState

trimMemoryLevel

PROCESS_STATE_PERSISTENT = 0;

TRIM_MEMORY_RUNNING_CRITICAL = 5
TRIM_MEMORY_RUNNING_LOW = 10
TRIM_MEMORY_RUNNING_MODERATE = 15

PROCESS_STATE_PERSISTENT_UI = 1;

PROCESS_STATE_TOP = 2;

PROCESS_STATE_FOREGROUND_SERVICE = 3;

PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 4;

PROCESS_STATE_IMPORTANT_FOREGROUND = 5;

PROCESS_STATE_IMPORTANT_BACKGROUND = 6;

TRIM_MEMORY_UI_HIDDEN =20

PROCESS_STATE_TRANSIENT_BACKGROUND = 7;

PROCESS_STATE_BACKUP = 8;

PROCESS_STATE_SERVICE = 9;

PROCESS_STATE_RECEIVER = 10;

PROCESS_STATE_TOP_SLEEPING = 11;

PROCESS_STATE_HEAVY_WEIGHT = 12;

TRIM_MEMORY_BACKGROUND = 40

PROCESS_STATE_HOME = 13;

TRIM_MEMORY_COMPLETE = 80
TRIM_MEMORY_MODERATE = 60
TRIM_MEMORY_BACKGROUND = 40

PROCESS_STATE_LAST_ACTIVITY = 14;

PROCESS_STATE_CACHED_ACTIVITY = 15;

PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 16;

PROCESS_STATE_CACHED_RECENT = 17;

PROCESS_STATE_CACHED_EMPTY = 18;

PROCESS_STATE_NONEXISTENT = 19;

 

TRIM_MEMORY_UI_HIDDEN 表示应用程序的 所有UI界面被隐藏了.这个进程本身还是比较重要的,不能被销毁的,但是由于目前内存很低了,因此必须释放一些不是非常重要的资源。

 

下面三个等级应用程序真正运行时的回调:

 

TRIM_MEMORY_RUNNING_MODERATE 表示这个进程本身还是比较重要的,不能被销毁的。但是目前手机的内存running moderately low on memory,

TRIM_MEMORY_RUNNING_LOW 表示应用程序正常运行,并且不会被杀掉。但是目前手机的内存已经is running low on memory,我们应该去释放掉一些不必要的资源以提升系统的性能,同时这也会直接影响到我们应用程序的性能。

TRIM_MEMORY_RUNNING_CRITICAL 表示应用程序仍然正常运行,但是内存已经非常低了(is running extremely low)但是系统已经根据LRU缓存规则杀掉了大部分缓存的进程了。这个时候我们应当尽可能地去释放任何不必要的资源,不然的话系统可能会继续杀掉所有缓存中的进程,并且开始杀掉一些本来应当保持运行的进程,比如说后台运行的服务。

 

当应用程序是缓存的,会有如下内存回收的类型:

 

TRIM_MEMORY_BACKGROUND 表示手机目前内存已经很低了,系统准备开始根据LRU缓存来清理进程。这个时候我们的程序在LRU缓存列表的最近位置,是不太可能被清理掉的,但这时去释放掉一些比较容易恢复的资源能够让手机的内存变得比较充足,从而让我们的程序更长时间地保留在缓存当中,这样当用户返回我们的程序时会感觉非常顺畅,而不是经历了一次重新启动的过程。

TRIM_MEMORY_MODERATE 表示手机目前内存已经很低了,并且我们的程序处于LRU缓存列表的中间位置,如果手机内存还得不到进一步释放的话,那么我们的程序就有被系统杀掉的风险了。

TRIM_MEMORY_COMPLETE 表示手机目前内存已经很低了,并且进程处于LRU缓存列表的最边缘位置,系统如果还是没有找到更多的内存那么该进程就会被杀掉,在这个时候应当尽可能地把一切可以释放的东西都进行释放。

                           

                               

 

5.TrimMemory

                           

                           

                          

进程的curProcState可能取值,以及对应的场景见ProcState这一节

                          

 

UidChange和后台行为受限 (见另外一篇博客)

 

 

 

computeOomAdjLocked

该函数的算法过程示意图:

                   

 

1.ADJ级别

ADJ可能取值如下表。但是有时候不限于下面的值,对于不可见进程系统只是规定了最小值

CACHED_APP_MIN_ADJ(900),最大值CACHED_APP_MAX_ADJ(906)。对于不可见进程来说,那么他取值会在这个范围之间[900,901,902,903,904,905,906]

ADJ级别

取值

含义

NATIVE_ADJ

-1000

native进程

SYSTEM_ADJ

-900

仅指system_server进程

PERSISTENT_PROC_ADJ

-800

系统persistent进程

PERSISTENT_SERVICE_ADJ

-700

关联着系统或persistent进程

FOREGROUND_APP_ADJ

0

前台进程

VISIBLE_APP_ADJ

100

可见进程

PERCEPTIBLE_APP_ADJ

200

可感知进程,比如后台音乐播放

BACKUP_APP_ADJ

300

备份进程

HEAVY_WEIGHT_APP_ADJ

400

重量级进程

SERVICE_ADJ

500

服务进程

HOME_APP_ADJ

600

Home进程

PREVIOUS_APP_ADJ

700

上一个进程

SERVICE_B_ADJ

800

B List中的Service

CACHED_APP_MIN_ADJ

900

不可见进程的adj最小值

CACHED_APP_MAX_ADJ

906

不可见进程的adj最大值

 

 

2.前置条件和参数初始化处理 

 

                               

  1. 处理app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ这种高优先级情况

这种情况的结果为:

curSchedGroup

ProcessList.SCHED_GROUP_DEFAULT

ProcessList.SCHED_GROUP_TOP_APP

ProcessList.SCHED_GROUP_RESTRICTED

curProcState

ActivityManager.PROCESS_STATE_PERSISTENT

ActivityManager.PROCESS_STATE_PERSISTENT_UI

ActivityManager.PROCESS_STATE_PERSISTENT_UI

curAdj

<= ProcessList.FOREGROUND_APP_ADJ

 

                  

                   

 

  1. 定义FOREGROUND_APP_ADJ具体场景

Case

adj

procState

schedGroup

PROCESS_STATE_CUR_TOP == ActivityManager.PROCESS_STATE_TOP && app == TOP_APP

FOREGROUND_APP_ADJ

PROCESS_STATE_TOP

SCHED_GROUP_TOP_APP

app.runningRemoteAnimation

VISIBLE_APP_ADJ

PROCESS_STATE_TOP_SLEEPING

SCHED_GROUP_TOP_APP

当instrumentation不为空时

FOREGROUND_APP_ADJ

PROCESS_STATE_FOREGROUND_SERVICE

SCHED_GROUP_DEFAULT

当进程存在正在接收的broadcastrecevier

FOREGROUND_APP_ADJ

PROCESS_STATE_RECEIVER

SCHED_GROUP_DEFAULT
SCHED_GROUP_BACKGROUND

当进程存在正在执行的service

FOREGROUND_APP_ADJ

PROCESS_STATE_SERVICE

SCHED_GROUP_DEFAULT
SCHED_GROUP_BACKGROUND

app == TOP_APP

FOREGROUND_APP_ADJ

PROCESS_STATE_TOP_SLEEPING

SCHED_GROUP_BACKGROUND

以上条件都不符合

adj=cachedAdj

PROCESS_STATE_CACHED_EMPTY

SCHED_GROUP_BACKGROUND

 

                           

                            

  1. 定义VISIBLE_APP_ADJPERCEPTIBLE_APP_ADJ具体场景

ProcessList.VISIBLE_APP_ADJ的场景定义如果没有前台的activity:但是activity是可见

PERCEPTIBLE_APP_ADJ的场景定义:ActivityState.PAUSING, ActivityState.PAUSED  r.isState(ActivityState.STOPPING) 在这些状态系统认为这些应用优先级比较高,如果adj的值大于PERCEPTIBLE_APP_ADJ就把他调整到PERCEPTIBLE_APP_ADJ,还有前台服务他的adj值也会被调整到 PERCEPTIBLE_APP_ADJ

这俩种场景对应的schedGroup都是>= SCHED_GROUP_DEFAULT 

                        

                           

                                    

 

 

 

 

  1. 定义HEAVY_WEIGHT_APP_ADJ具体场景    

adj

<=ProcessList.HEAVY_WEIGHT_APP_ADJ

schedGroup

ProcessList.SCHED_GROUP_BACKGROUND

procState

<=ActivityManager.PROCESS_STATE_HEAVY_WEIGHT

                 

 

 

  1. 定义HOME_APP_AD具体场景

adj

<=ProcessList.HOME_APP_ADJ

schedGroup

ProcessList.SCHED_GROUP_BACKGROUND

procState

<=ActivityManager.PROCESS_STATE_HOME

 

 

  1. 定义PREVIOUS_APP_ADJ具体场景

adj

<=ProcessList.PREVIOUS_APP_ADJ

schedGroup

ProcessList.SCHED_GROUP_BACKGROUND

procState

<=ActivityManager.PROCESS_STATE_LAST_ACTIVITY

 

  1. 定义BACKUP_APP_ADJ的具体场景

adj

<=ProcessList.BACKUP_APP_ADJ

procState

<=ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND

                        

 

  1. 根据client和service的关系来确定serviceadj

这个关系主要是指服务的caller, 主要依据调用bindService(Intent service, ServiceConnection conn, int flags)时候传入的flags参数来判断,这个flags可以选择的值为:BIND_AUTO_CREATE, BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND,BIND_ABOVE_CLIENT, BIND_ALLOW_OOM_MANAGEMENT, or BIND_WAIVE_PRIORIT

                      

                    

                         

                      

 

 

 

 

 

12.根据client和provide关系来确定serviceadj 

                   

                 

  

 

 

applyOomAdjLocked

该函数的算法过程示意图:

                 

1.把计算出来的ADJ设下去

                      

 

2.分配不同的ProcessGroup

                          

                              

                              

 

 

 

3. 把计算出来的进程状态下去
 

   

                       

 

  1. 重新计算PSS

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!