Android进程管理机制研究

1、Linux中的进程管理
在Linux中,进程是指处理器上执行的一个实例,可以使用任意资源以便完成它的任务,具体的进程管理,是经过“进程描述符”来完成的,对应Linux内核中的task_struct数据结构。进程描述符,包括进程标识、进程的属性、构建进程的资源。
一个进程能够经过fork()或者vfork()调用建立出子进程,这些子进程能够访问父进程的地址空间,包括文本段、数据段、堆栈段。java

一般状况下,调用fork()的进程处于task_running状态,则fork出来的子进程默认也处于task_running状态,具体来讲,在fork以后、exec以前,子进程处于task_running状态中的就绪状态。linux

进程的运行状态包括如下几种。
一、task_running:可执行状态。包含正在CPU上执行的、可执行可是还没有被调度执行这两种子状态,后者对应就绪状态。
二、task_interruptible:可中断的睡眠状态。由于等待某事件的发生而被挂起。当等待的事件发生时,处于该状态的进程将被唤醒。
三、task_uninterruptible:不可中断的睡眠状态。处于睡眠状态,可是此刻进程是不可中断的。此时进程不响应异步信号,不能经过发送信号的方式kill之,但能够响应硬件中断,例如磁盘IO,网络IO等。
 四、task_stopped / task_traced:暂停状态或跟踪状态。处于task_traced状态的进程不能响应SIGCONT信号而被唤醒,只能等到调试进程经过ptrace系统调用执行ptrace_cont、ptrace_detach等操做,或调试进程退出,被调试的进程才能恢复task_running状态。
五、task_zombie :僵尸状态。在进程收到SIGSTOP、SIGTTIN、SIGTTOU等信号,即将终止时,会进入该状态,此时进程成为僵尸进程。该状态的进程会处理一些资源释放工做,而后发送SIG_CHLD信号给父进程。android

2、Android进程管理机制
在linux系统中,应用程序执行完成后,最终会清理一些进程使用的文件描述符、释放掉进程用户态使用的相关的物理内存,清理页表,而后发送信号给父进程。但在Android系统中,应用程序执行完成后,该应用所在的进程一般仍是会在后台继续运行,除非应用程序在执行完成后主动调用System.exit或者Process.killProcess之类的方法。
这样设计的好处,主要是加快应用程序再次启动的速度,改善用户体验。当内存不足时,系统会按照特定规则,包括进程优先级、占用内存等信息,来清理进程并释放对应的资源。git

一、进程优先级
在Android中,进程按优先级能够分为:前台进程、可见进程、服务进程、后台进程、空进程。优先级依次下降。github


1)Forground,前台进程
这种进程优先级最高,能够细分下面几种状况: 
case1:有个前台Activity,特指已经执行了onResume但还没执行onPause的Activity;
case2:有个Service且和一个前台Activity绑定的进程;
case3:调用了startForground的前台Service所在进程(这种服务会带个通知);
case4:正在执行onReceive函数的BroadcastReceiver所在进程,以及正在执行服务的生命周期方法诸如onCreate、onStartCommand的进程。缓存

2)Visible,可见进程
可见进程没有处于前台的组件,可是用户仍然能看到进程中的组件,例如某进程的Activity调用了申请权限对话框,具体包括:
case1:有个仅onPause被调用的Activity(可见但被遮挡);
case2:进程中有个Service且和一个可见Activity绑定。
注意这里的可见Activity不包括前台Activity(不然就是前台进程了),而且这种进程在内存不足时也是可能被杀掉的。性能优化

3)Service,服务进程
服务进程是指有个经过startService方式启动的Service进程,而且不属于前面两类进程,例如MediaScannerService。网络

4)Background,后台进程
当前不可见的Activity所在进程属于后台进程,即它们的onStop被调用过,例如用户按下home键。
对于后台进程,系统会保存这些进程到一个LRU列表,当系统须要回收内存时,LRU中那些最近最少使用的进程将被杀死。数据结构

5)Empty,空进程
空进程不包含任何组件,当系统从新须要它们时(例如用户在别的进程中经过startActivity启动了它们),能够省去fork进程、建立Android运行环境等漫长的工做,节省时间,缓存性质。这种进程优先级最低,在任什么时候候均可能被杀掉。异步

如前所述,大部分应用是单进程的,可是进程和组件类型是高度关联的。若是应用中有生命周期差别较大的组件,考虑使用多进程分别处理。一方面是让占用资源较多的进程能够被系统及时回收,另外一方面,避免那些须要长时间持续运行的任务因为组件生命周期的影响进入后台进程执行。

二、内存不足时的杀进程策略
在Linux系统中,进程的优先级对应一个参数,也就是oom_score_adj,lowmemorykiller程序会根据内存使用状况和进程优先级,动态杀进程以释放内存资源。 

当内存不足或者发生oom错误时,lowmemorykiller根据特定策略先杀优先级最低的进程,而后逐步杀优先级更低的进程(一样优先级会按照内存占用状况排序),依此类推,以回收预期的可用系统资源,从而保证系统正常运转。

在Android系统中,进程的组件状态变化时,组件所在进程的优先级也会发生变化。一个典型的场景是,切换到后台的进程,其优先级低于前台进程。

App的前台/后台切换操做对oom_score_adj的影响,咱们可使用adb命令cat/proc/[pid]/oom_score_adj来查看。详细的优先级信息在ProcessList.java中有定义,对应的部分进程类型以下。

  • Cached,缓存进程,包括空进程、只有activity的后台进程,其adj在900~906;
  • B Services,无UI组件且在Lru进程表中位于后2/3的服务进程(比较旧的后台服务进程),其adj为800;
  • Previous,上一个应用进程,例如按home键进入后台的进程,其adj为700;
  • Home,也就是launcher进程,其adj为600;
  • A Services,没有UI组件且在Lru进程表中位于后2/3的服务进程(比较旧的后台服务进程),其adj为500;
  • Perceptible,有着用户可感知组件的进程,包括前台服务进程,adj为200;
  • Visible,可见进程,其adj为100;
  • Foreground,前台应用进程,其adj为0;
  • Persistent,系统常驻进程,例如systemui、phone进程,其adj为-700或-800。若是是在AndroidManifest.xml中申明android:persistent="true"的进程,adj为-800;若是是由startIsolatedProcess()方式启动或由SystemServer进程、persistent进程绑定的服务进程,则为-700;
  • System进程,典型的是SystemServer进程。
  • Native进程,例如init、surfaceflinger、mediaserver进程,其adj为-1000;

内存不足时,AMS会根据上述动态优先级信息,经过ProcessRecord,在native层给指定进程发送信号以终止进程,进而释放内存资源。相关函数包括:killProcessesBelowForeground, killProcessesBelowAdj, ProcessRecord.kill, killPids等。

 

(相关完整且成体系的文章,可参见本人原创的开源电子书《Android系统与性能优化》,地址:https://github.com/carylake/androidnotes)

相关文章
相关标签/搜索