Android 70 ActivityManagerService7 进程管理相关流程分析1.docx
- 文档编号:4618602
- 上传时间:2023-05-07
- 格式:DOCX
- 页数:23
- 大小:70.91KB
Android 70 ActivityManagerService7 进程管理相关流程分析1.docx
《Android 70 ActivityManagerService7 进程管理相关流程分析1.docx》由会员分享,可在线阅读,更多相关《Android 70 ActivityManagerService7 进程管理相关流程分析1.docx(23页珍藏版)》请在冰点文库上搜索。
Android70ActivityManagerService7进程管理相关流程分析1
Android7.0ActivityManagerService(7)进程管理相关流程分析
(1)
Android框架定义了四大组件,一般的功能都是基于这些组件的。
因此,在Android平台上开发时,很少会接触到进程的概念。
但根据前面分析Activity、Service和BroadcastReceiver的流程,
我们可以清晰地看到,所有这些组件最终还是要运行在具体的进程之中。
于是,AMS作为管理四大组件的核心服务,也承担起了进程管理的责任。
在AMS中,进程管理相关的函数分别为updateLruProcessLocked和updateOomAdjLocked。
这两个函数在之前的流程分析中多次遇到过,从这篇博客开始,我们一起来看看它们具体的工作流程。
一、Linux进程管理的基本概念
Android中进程管理相关的工作,实际上依赖于Linux的进程管理策略。
因此,先简单地了解一下Linux进程管理的基本概念。
1、Linux进程调度优先级和调度策略
调度优先级和调度策略,是操作系统对CPU资源进行管理和控制的依据。
调度优先级:
我们知道,操作系统上运行的进程数量较多,同时CPU的资源是有限的。
因此,操作系统需要合理地为不同进程分配CPU资源。
调度优先级就是操作系统分配CPU资源给进程时,需要参考的一个重要指标。
一般而言,优先级较高的进程将更有机会得到CPU资源。
调度策略:
调度策略用于描述,操作系统的调度模块,分配CPU资源给应用进程时,所遵循的规则。
即用于描述,将CPU控制权交给调度模块时,它如何决定下一个要运行的进程。
由于多个进程可能具有相同的调度优先级,因此调度模块不能仅按照各进程的调度优先级进行决策。
一个良好的调度策略,一定要兼顾到效率和公平性。
LinuxAPI:
Linux提供了两个API用于设置调度优先级及调度策略。
1)设置调度优先级的API为:
intsetpriority(intwhich,intwho,intprio)
其中,which和who联合使用。
当which为PRIO_PROGRESS时,who代表一个进程,即pid;
当which为PRIO_PGROUP时,who代表一个进程组,即gid;
当which为PRIO_USER时,who代表一个用户,即uid。
prio用于设置进程的nice值,取值范围为-20~+19。
该值越大表示进程越友好(nice),即对CPU资源的依赖越低。
于是,进程的prio值越大,其被调用的优先级越低。
2)设置调度策略的API为:
intsched_setscheduler(pid_tpid,intpolicy,conststructsched_param*param);
其中,pid表示进程id;
policy表示调度策略。
Linux定义了很多种调度策略,具体的内容可以参考相关的资料。
param参数中,最重要的是该结构体中的sched_priority变量。
该变量用于设置该调度策略下,进程的优先级。
2、Linux进程oom_adj的介绍
从Linuxkernel2.6.11开始,内核提供了进程的OOM控制机制。
目的是:
当系统出现内存不足的情况时,内核可以根据进程的oom_adj值,
来选择杀死一些进程,以回收内存。
oom_adj用于表示Linux进程内存资源的优先级,其可取值范围为-16至+15,数值越小,被kill的可能性越低。
此外,还有一个特殊值-17,表示禁止系统在OOM情况下杀死该进程。
Linux没有提供单独的API用于设置进程的oom_adj,一般是向/proc/[pid]/oom_adj文件中写入对应的oom_adj值。
在Linuxkernel2.6.36后,/proc/[pid]/oom_adj被弃用了,改为/proc/[pid]/oom_score_adj。
oom_score_adj对应的取值范围变为-1000到1000,仍然是数值越小,被kill概率越小。
此时,-1000表示禁止系统在OOM情况下杀死该进程。
AMS的updateOomAdjLocked函数,就借用了Linux中oom_adj的概念。
此外,Android为linuxkernel新增了LowMemoryKiller(LMK)机制。
LMK的配置文件中,预先定义了不同的内存阈值及对应的oom_adj。
当LMK监控到系统内存下降到某个阈值时,就会kill掉当前系统内,
oom_adj超过该阈值预定义oom_adj的所有进程。
例如,预定义一组对应关系,内存剩余2048KB时,oom_adj为0。
那么,当LMK监控到系统内存少于2048KB时,将kill掉系统内所有oom_adj大于0的进程。
以上是Linux进程管理的一些基本概念,
如果需要深入理解对应的过程,还是需要阅读相关源码和资料。
接下来,我们来看看Android中进程管理相关的内容。
二、Android中进程管理的基本概念
进程的调度优先级和和调度策略,主要通过Process.Java提供的接口来设置,
由Linux来完成实际的工作。
1Process类中的API
我们先看看Process类中,进程优先级相关的API:
以下两个函数主要用于设置线程对应的调度优先级:
/**
*Setthepriorityofathread,basedonLinuxpriorities.
*@paramtidTheidentifierofthethread/processtochange.
*@parampriorityALinuxprioritylevel,from-20forhighestscheduling
*priorityto19forlowestschedulingpriority.
*/
//设置tid对应线程的优先级
publicstaticfinalnativevoidsetThreadPriority(inttid,intpriority)
throwsIllegalArgumentException,SecurityException;
//设置调用线程对应的优先级
publicstaticfinalnativevoidsetThreadPriority(intpriority)
throwsIllegalArgumentException,SecurityException;
以下两个函数分别用于设置线程和进程对应的Group,不同的Group实际上对应者不同的调用策略:
/**
*Setstheschedulinggroupforathread.
*@hide
*@paramtidTheidentifierofthethreadtochange.
*@paramgroupThetargetgroupforthisthreadfromTHREAD_GROUP_*.
*/
publicstaticfinalnativevoidsetThreadGroup(inttid,intgroup)
throwsIllegalArgumentException,SecurityException;
/**
*Setstheschedulinggroupforaprocessandallchildthreads
*@hide
*@parampidTheidentifieroftheprocesstochange.
*@paramgroupThetargetgroupforthisprocessfromTHREAD_GROUP_*.
*/
publicstaticfinalnativevoidsetProcessGroup(intpid,intgroup)
throwsIllegalArgumentException,SecurityException;
不同的THREAD_GROUP_*,对应着不同的CPU共享方式,详细的内容可以参考源码对应的注释。
最后一个API与Linux中的sched_setscheduler接口对应,定义指定进程或线程的调度策略及相应的优先级。
/**
*Settheschedulingpolicyandpriorityofathread,basedonLinux.
*
*@paramtidTheidentifierofthethread/processtochange.
*@parampolicyALinuxschedulingpolicysuchasSCHED_OTHERetc.
*@parampriorityALinuxprioritylevelinarangeappropriateforthegivenpolicy.
*
*/
publicstaticfinalnativevoidsetThreadScheduler(inttid,intpolicy,intpriority)
throwsIllegalArgumentException;
从Process.java中API可以看出,这些接口的具体实现都是由native函数来完成,
定义于frameworks/base/core/jni/android_util_Process.cpp中。
我们以设定具体线程调度优先级的setThreadPriority函数为例,简单看看具体的实现:
//这是对应的native函数
voidandroid_os_Process_setThreadPriority(JNIEnv*env,jobjectclazz,
jintpid,jintpri){
..............
//调用androidSetThreadPriority函数
intrc=androidSetThreadPriority(pid,pri);
.............
}
intandroidSetThreadPriority(pid_ttid,intpri){
..........
//set_sched_policy函数,将调用Linux的sched_setscheduler接口设置调度策略
//具体的函数此处不做展开
//后台线程的调度策略为SCHED_BATCH,前台为SCHED_NORMAL
//此处调度策略对应的调度优先级为0
if(pri>=ANDROID_PRIORITY_BACKGROUND){
rc=set_sched_policy(tid,SP_BACKGROUND);
}elseif(getpriority(PRIO_PROCESS,tid)>=ANDROID_PRIORITY_BACKGROUND){
rc=set_sched_policy(tid,SP_FOREGROUND);
}
........
//调用Linux的setpriority接口,设置调度优先级
//注意到Linux不区分线程和进程,此处使用的均是PRIO_PROCESS
if(setpriority(PRIO_PROCESS,tid,pri)<0){
rc=INVALID_OPERATION;
}else{
errno=lasterr;
}
returnrc;
}
Process.java中的其它接口,最终也均是调用之前介绍的LinuxAPI完成具体的功能,不再进一步分析。
Android框架提供了接口,来设置调度策略及优先级,但系统本身更侧重于对进程内存的管理。
接下来我们主要分析一下,Android中内存相关的进程管理。
在此之前,还是要先了解一下基本概念。
2进程分类
为了提高程序的启动速度,即使用户不再使用一个程序时(例如将程序切换到后台等),
Android系统也会尽可能长时间地保留该程序所在的进程(避免重新使用程序时,需要fork进程及创建Android运行环境等)。
但当内存资源紧张的时候,系统终究会为一些新的或者更重要的进程,杀死一些旧的进程来释放内存。
系统主要是根据进程中组件的运行状态,来决定每一个进程的重要性,从而决定哪个进程需要杀死,哪个进程需要保持。
于是,根据进程中运行组件的特点,Android将进程进行了分类。
以下按照重要性降低的顺序,列出不同种类进程的特点。
2.1Foreground进程(前台进程)
前台进程是指:
用户完成当前工作而需要的进程。
进程至少具有以下条件中一项时,才能被称为前台进程:
1)含有一个前台的Activity,即该Activity的onResume函数被调用过了,当前正显示在界面上。
2)含有一个和前台Activity绑定的Service。
3)含有一个运行在前台的Service,即该Service主动调用了startForeground函数;
4)其中的某个Service正在调用其生命周期的函数(例如onCreate、onStart、onDestroy等)。
5)其中某个BroadcastReceiver正在执行onReceive函数。
一般情况下,在特定的时刻,系统中也仅会有为数不多的几个前台进程。
这些前台进程的重要性最高,直到系统内存极低,以致于不能继续运行所有的前台进程时,
系统才会杀死其中的某个进程。
2.2Visible进程(可视进程)
可视进程是指:
其中没有前台运行的组件,但仍然会对用户在屏幕看到的内容造成影响的进程。
此类进程需要满足下列条件中的一项:
1)其中某个Activity不在前台,但仍然是可见的(即该Activity仅调用了onPause()方法,仍工作在前台,只不过部分界面被遮住。
例如,正在前台运行的Activity启动了一个对话框,这个对话框悬浮在这个activity之上)。
2)其中的某个Service绑定到了一个可视(或前台)的activity(该activity已调用了onPause()方法)。
可视进程也是有着极高重要性的进程。
仅当为了保证前台进程能够继续运行时,系统才会杀死可视进程。
2.3Service进程(服务进程)
服务进程是指:
不满足上面两种进程的条件,但是包含一个通过startService方法,启动的service的进程(service没有与前台或可视Activity绑定)。
虽然服务进程可能和用户可见的内容没有直接的联系,但它们所做的工作也是用户关心的,
系统会一直保留这类进程,直到为了保证前台和可见进程的运行时,才会杀死服务进程。
2.4Background进程(后台进程)
后台进程是指:
不满足上面三种进程的条件,但包含当前对用户不可见的Activity的进程。
不可见的Activity是指,调用了onStop()方法的Activity。
后台进程不会对用户的体验造成任何影响,并且系统可以在前台进程、可视进程、服务进程需要内存资源的时候杀死该类进程。
通常系统中会有很多后台进程运行,并且这些后台进程被保存在一个最近最少使用列表(LRU,LeastRecentlyUsed)中,
当系统需要内存时,将按照LRU中进程的顺序,依次杀死进程。
这样做的好处就是保证用户最近看到的进程最后被杀死。
2.5Empty进程(空进程)
空进程是指:
其中没有运行任何组件的进程。
系统保留空进程的原因是:
提高组件的启动时间。
当进程中的某项功能被需要时,可以直接启动对应组件,
省去了fork进程、创建Android运行环境等工作。
系统经常会杀死这些空进程来保持整个系统资源和内核缓存之间的平衡。
2.6总结
Android根据进程中运行的最重要的组件,来划分进程的种类。
例如,如果一个进程中既有一个可视的activity,又有一个service,
那么这个进程属于可视进程而不是服务进程。
此外,根据上面的分类情况,我们知道了:
一个正在运行的Service所在的进程,重要性高于一个处于后台的activity所在的进程。
因此,如果一个activity要执行某个耗时操作时,最好将该操作递交给service处理,而不是仅仅创建一个工作线程。
同样,广播接收者在需要时,也应该将耗时工作递交给Service处理,而不是启动一个线程或滥用goAsync。
AMS中与进程分类思想关系比较密切的函数是updateLruProcessLocked,接下来我们一起来看看这个函数。
三、AMS中的updateLruProcessLocked
在AMS四大组件涉及的流程中,updateLruProcessLocked函数被多次调用,用于更新进程的使用情况。
当内存紧张时,将按照进程的种类及使用情况kill一些进程。
...................
/**
*Listofrunningapplications,sortedbyrecentusage.
*Thefirstentryinthelististheleastrecentlyused.
*/
finalArrayList
...................
如上面的代码所示,在AMS中有一个成员变量mLruProcesses,
根据进程的使用时间及进程种类,按照重要性由低到高的顺序,
保存着进程对应的ProcessRecord信息。
即排在越前面的进程,优先级越低,在系统内存不足时,越有可能被kill。
updateLruProcessLocked函数被调用时,就会更新参数对应进程在mLruProcesses中的位置。
1updateLruProcessLocked
下面我们分段来看看updateLruProcessLocked函数的代码流程。
1.1updateLruProcessLockedPart-I
finalvoidupdateLruProcessLocked(ProcessRecordapp,booleanactivityChange,
ProcessRecordclient){
//判断进程中是否含有Activity
finalbooleanhasActivity=app.activities.size()>0||app.hasClientActivities
||app.treatLikeActivity;
//目前,并没有考虑进程中是否含有Service
//因此,虽然理论上定义了Service相关的进程分类,但并没有实现对应的管理策略
//在以下代码中,hasService一直为false
finalbooleanhasService=false;//notimplyet.app.services.size()>0;
if(!
activityChange&&hasActivity){
//Theprocesshasactivities,soweareonlyallowingactivity-basedadjustments
//tomoveit.Itshouldbekeptinthefrontofthelistwithother
//processesthathaveactivities,andwedon'twantthosetochangetheir
//orderexceptduetoactivityoperations.
//当进程中包含Activity时,将由Activity决定该进程的排序
//当activityChange为false时,说明进程并没有改变Activity的状态
//因此,不调整进程在LRU中的位置
return;
}
//每次更新LRU列表,都会分配对应的编号
mLruSeq++;
finallongnow=SystemClock.uptimeMillis();
app.lastActivityTime=now;
//Firstaquickreject:
iftheappisalreadyatthepositionwewill
//putit,thenthereisnothingtodo.
if(hasActivity){
finalintN=mLruProcesses.size();
//该进程包含Activity,并且已经在LRU尾端(最新使用的),不用更新
if(N>0&&mLruProcesses.get(N-1)==app){
if(DEBUG_LRU)Slog.d(TAG_LRU,"Notmoving,alreadytopactivity:
"+app);
return;
}
}else{
//不包含Activity,但在LRU其它分类的最尾段,不用更新
if(mLruProcessServiceStart>0
&&mLruProcesses.get(mLruProcessServiceStart-1)==app){
if(DEBUG_LRU)Slog.d(TAG_LRU,"Notmoving,alreadytopother:
"+app);
return;
}
//获取进程当前在LRU的位置
//不存在则返回-1
intlrui=mLruProcesses.lastIndexOf(app);
//常驻进程不用管
if(app.persistent&&lrui>=0){
//Wedon'tcareaboutthepositionofpersistentprocesses,aslongas
//theyareinthelist.
...........
return;
}
.................
updateLruProcessLocked函数的第一部分主要是判断:
是否需要调整进程在LRU表中的位置。
这部分的代码相当简单,但从中可以看出LRU中进程排列的规则,基本上可以用下图来表示:
LRU表按照顺序,重要性逐渐升高,即当系统需要内存时,将优先kill掉排在前面的
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Android 70 ActivityManagerService7 进程管理相关流程分析1 进程 管理 相关 流程 分析
链接地址:https://www.bingdoc.com/p-4618602.html