Android 进程
进程的创建
在Android系统中,进程可以分为系统进程
和应用进程
两大类
系统进程
系统内置的(例如:
init
,zygote
,system_server
)进程,这一部分是操作系统中必不可少的一部分。这部分进程的数量通常是固定的(出厂或者系统升级之后就确定了),并且这部分进程通常是一直存活,常驻内存的。系统进程的异常或者退出可能会导致设备无法正常使用这一部分进程的作用在于
- 管理硬件设备
- 提供访问设备的基本能力
- 管理应用进程
应用进程
是指程序运行的进程,这些程序可能是系统出厂自带的(短信、电话等),也有可能是用户自己安装的(淘宝、支付宝等)。这部分进程在每个人使用的设备上通常是不一样的。如何管理着一些不确定的应用进程,就是操作系统本身要仔细考虑的内容,也是衡量一个操作系统好坏的重要指标
init进程
在Android系统中,所有进程号都是不确定的,只有init进程例外(进程号为固定的1),因为这个进程一定是系统跑起来的第一个进程,它掌握着整个系统的启动逻辑。
因为Android可能运行在各种不通的平台、不同的设备上,所以,启动逻辑也是各有千秋。为了应对各平台的和设备的需求,init
进程的初始化工作通过init.rc
(这个通过Android Init Language语法来进行配置)这个配置文件来管理。这部分配置并不是只有一个文件,只是因为配置文件的主入口文件是init.rc
,这个主入口文件会通过import
来引入其他的几个文件,所以暂时统称这些文件为init.rc
。
在init.rc
中需要配置系统启东时该做哪些事情,以及应该启动那些系统进程。这其中有两个特别重要的进
zygote
这个翻译过来就是“受精卵”。所有的进程都是由
zygote
fork出来的子进程,因此zygote
是所有应用程序的父进程init.rc
会根据平台不一样,选择下面的几个文件中的一个来启动zygote进程,这几个文件的大致内容都是一样的- init.zygote32.rc
- init.zygote32_64.rc
- init.zygote64.rc
- init.zygote64_32.rc
以init.zygote32.rc文件为例子
1
2
3
4
5
6
7
8
9
10
11> service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
> class main
> socket zygote stream 660 root system
> onrestart write /sys/android_power/request_state wake
> onrestart write /sys/power/state on
> onrestart restart audioserver
> onrestart restart cameraserver
> onrestart restart media
> onrestart restart netd
> writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
>这段配置中,启动了一个名为zygote的服务进程,这个进程是通过
/system/bin/app_process
这个可执行程序创建的,并且在启动它的时候传递了-Xzygote /system/bin --zygote --start-system-server class main
这些参数。在这个进程启动以后,会启动一个socket套接字,并通过Looper一直在这个套接字上等待连接,所有应用进程都是通过发送数据到这个套接字上,然后有zygote创建的app_process的源码在
frameworks/base/cmds/app_process/app_main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38//以下仅为部分代码
246 while (i < argc) {
247 const char* arg = argv[i++];
248 if (strcmp(arg, "--zygote") == 0) {
249 zygote = true;
250 niceName = ZYGOTE_NICE_NAME;
251 } else if (strcmp(arg, "--start-system-server") == 0) {
252 startSystemServer = true;
253 } else if (strcmp(arg, "--application") == 0) {
254 application = true;
255 } else if (strncmp(arg, "--nice-name=", 12) == 0) {
256 niceName.setTo(arg + 12);
257 } else if (strncmp(arg, "--", 2) != 0) {
258 className.setTo(arg);
259 break;
260 } else {
261 --i;
262 break;
263 }
264 }
279 if (startSystemServer) {
280 args.add(String8("start-system-server"));
281 }
282
306 if (zygote) {
307 runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
308 } else if (className) {
309 runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
310 } else {
311 fprintf(stderr, "Error: no class name or --zygote supplied.\n");
312 app_usage();
313 LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
314 return 10;
315 }在这里会进行一次判断,根据参数不同,执行不同的操作
这其中的
runtime.start
就是启动Java的虚拟机,并在虚拟机中启动指定的类,然后接下来的逻辑就在ZygoteInit.java
中了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
693 try {
694 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygoteInit");
695 RuntimeInit.enableDdms();
696 // Start profiling the zygote initialization.
697 SamplingProfilerIntegration.start();
698
699 boolean startSystemServer = false;
700 String socketName = "zygote";
701 String abiList = null;
702 for (int i = 1; i < argv.length; i++) {
703 if ("start-system-server".equals(argv[i])) {
704 startSystemServer = true;
705 } else if (argv[i].startsWith(ABI_LIST_ARG))
......
}
742 Zygote.nativeUnmountStorageOnInit();
743
744 ZygoteHooks.stopZygoteNoThreadCreation();
745
746 if (startSystemServer) {
747 startSystemServer(abiList, socketName);
748 }
749
750 Log.i(TAG, "Accepting command socket connections");
751 runSelectLoop(abiList);
752
753 closeServerSocket();
754 } catch (MethodAndArgsCaller caller) {
755 caller.run();
756 } catch (RuntimeException ex) {
757 Log.e(TAG, "Zygote died with exception", ex);
758 closeServerSocket();
759 throw ex;
760 }
761 }精髓所在:
通过
registerZygoteSocket(socketName)
注册Zygote Socket通过
preload()
预先加载所有应用都需要的公共资源(如:Framwork相关的一些基础类和Resource资源)(ps:开发者通过Android SDK开发应用所调用的API实现都在Framework中)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23191 static void preload() {
192 Log.d(TAG, "begin preload");
193 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "BeginIcuCachePinning");
194 beginIcuCachePinning();
195 Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
196 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadClasses");
197 preloadClasses();
198 Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
199 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadResources");
200 preloadResources();
201 Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
202 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
203 preloadOpenGL();
204 Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
205 preloadSharedLibraries();
206 preloadTextResources();
207 // Ask the WebViewFactory to do any initialization that must run in the zygote process,
208 // for memory sharing purposes.
209 WebViewFactory.prepareWebViewInZygote();
210 endIcuCachePinning();
211 warmUpJcaProviders();
212 Log.d(TAG, "end preload");
213 }好处是:
- 加快应用启动速度,因为这些资源在zygote进程启动的时候已经加载好了
- 通过共享的方式节省内存,这是Linux本身提供的机制,父进程已经加载的内容可以在子进程中进行共享,而不用多份数据拷贝,如果子进程对这些数据进行了修改,那另当别论
通过
startSystemServer(abList, socketName)
启动system_server
通过
runSelectLoop(abList)
,在Looper上等待连接
system_server
这是一个系统服务进程,zygote进程启动以后会根据需要启动system_server
进程。Framwork层的几乎所有服务都位于这个进程中,这其中就包括了Android四大组件中的Service
system_server
中包含了大量的系统服务
- 负责网络管理的NeteworkManagementService
- 负责窗口管理的WindowManagerService
- 负责震动管理的VibratorService
- 负责输入管理的InputManagerService
下面2重点讲一下**ActivityManagerService
**
上面我提到过,zygote进程在启动后会启动一个socket,然后一直咋这个socket等待连接。会连接它的就是ActivityManagerService,因为ActivityManagerService
掌握了所有应用程序的创建。
所有应用程序的进程都是由ActivitymanagerService
通过socket发送创建请求给zygote
进程,然后由zygote fork创建的。
ActivityManagerService
通过Process.start
方法来请求zygote
创建进程
1 |
|
这个函数会将启动进程所需要的参数组装好,并通过socket发送给zygote
进程,然后zygote
进程根据发送过来的参数将进程fork出来
1 | 3487 private final void startProcessLocked(ProcessRecord app, String hostingType, |
在ActivityManagerService
中,调用Process.stat
的地方是下面这个地方(其实四大组件进程的创建,都是调用这里的startProcessLocked
这个方法而创建的)
对于每一个应用进程,在ActivityManagerService
中,都有一个ProcessRecord
与之对应,这个对象记录记录了应用程序的所有详细状态
1 | /* |
以上,总结就是
关于应用组件
因为,当某个应用组件启动且该应用没有运行其他任何组件时,Android系统会使用单个执行行线程为应用启动新的Linux进程,所以四大组件中的任何一个开始都会导致应用进程的创建。
在应用程序中,我们可以通过以下方法启动相应的东西
startActivity(Intent intent)
启动ActivitystartService(Intent service)
启动servicesendBroadcast(Intent intent)
发送广播- ContentResolver中的接口来使用ContentProvider
实际上,这里提到的这些方法,最终都是通过Binder
调用到ActivityManagerService
中,由其进行处理的。(ps:应用进程和ActivityManagerService
所在进程,即system_server
进程,是相互独立的)
在Android中,专门提供了Binder
框架来提供进程间通讯和方法调用的能力
Activity与进程创建
前面提到过,在ActivityManagerService
中,对每一个运行中的Activity都有一个ActivityRecord
对象与之对应,这个对象记录了Activity的详细状态
ActivityManagerService
中的startActivity()
方法接受Context.startActivity
的请求
1 |
|
一些零碎的东西
ActivityManagerService
中通过Stack和Task来管理Activity每个Activity都属于一个Task,一个Task可能包含多个Activity。一个Stack包含多个Task
ActivityStackSupervisor
类负责管理所有的StackActivity的启动过程会牵涉到
- Intent的解析
- Stack、Task的查询或者解析
- Activity进程的创建
- Activity窗口的创建
- Activity生命周期的调度
进程的优先级
在Android系统中,进程的优先级影响着以下三个因素
- 当内存紧张时,系统对于进程的回收策略
- 系统对于进程的CPU调度策略
- 虚拟机对于进程的内存分配和垃圾回收策略
具体的,系统对于进程的优先级有如下五个分类
- 前台进程
- 可见进程
- 服务进程
- 后台进程
- 空进程
优先级的依据
组件与进程的相关信息
每一个Android的应用进程中,都可能包含四大组件中的至少一种
对于运行中的Service和ContentProvider来说,可能有若干个客户端进程正在对其使用
应用进程是由ActivityManagerService发送请求让zygote创建的,并且
ActivityManagerService
中对于每一个运行中的进程都有一个ProcessRecord
对象与之对应ProcessRecord简化图
ProcessRecord记录了组件的相关信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
158 // all activities running in the process
159 final ArrayList<ActivityRecord> activities = new ArrayList<>();
160 // all ServiceRecord running in this process
161 final ArraySet<ServiceRecord> services = new ArraySet<>();
162 // services that are currently executing code (need to remain foreground).
163 final ArraySet<ServiceRecord> executingServices = new ArraySet<>();
164 // All ConnectionRecord this process holds
165 final ArraySet<ConnectionRecord> connections = new ArraySet<>();
166 // all IIntentReceivers that are registered from this process.
167 final ArraySet<ReceiverList> receivers = new ArraySet<>();
168 // class (String) -> ContentProviderRecord
169 final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
170 // All ContentProviderRecord process is using
171 final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();
activitys:记录的运行中的Activity
services:在程序里面跑这着的
executingServices:当前正在执行的服务(需要保持前台
connections:记录了对于Service的连接
receives:记录了程序中运行的BroadcastProvider
pubProviders:运行的ContentProvider
conProviders:记录了对于ContentProvider的连接
连接就是对于客户端使用状态的记录,对于Service和ContentProvider是类似的,每有一个客户端就要记录一个连接。连接的意义在于:连接的客户端的进程优先级会影响被使用Service和ContentProvider所在进程的优先级。
所有的这些组件的状态就是其所在的进程优先级的决定性因素,组件状态指:
Activity是否在前台,用户是否可见
Service正在被哪些客户端使用
ContentProvider正在被哪些客户端使用
Service正在被哪些客户端使用
BroadcastReceiver是否正在接受广播
优先级的基础
oom_score_adj
对于每一个运行中的进程,Linux内核都通过proc文件系统暴露这样一个文件来允许其他程序修改指定进程的优先级:/proc/[pid]/oom_score_adj (pid是 Linux 中在其命名空间中唯一标识进程而分配给它的一个号码,称做进程ID号,简称PID。在使用 fork 或 clone 系统调用时产生的进程均会由内核分配一个新的唯一的PID值)
这个文件允许的值的范围是-1000~+1000之间,值越小,进程越重要。当内存紧张时,系统就是遍历进程,根据这个值来确定杀死哪些进程以回收内存
ProcessRecord中,下面这属性反映了oom_score_adj
的值
1 | 92 int maxAdj; // Maximum OOM adjustment for this process |
maxAdj
制定了该进程允许oom_score_adj
最大值,这个属性主要是给系统应用和常驻内存的进程使用,这些进程的优先级的计算方法与应用进程的计算方法不一样,通过设置maxAdj
保证这些应用一直拥有较高的优先级
curAdj
开头的这一组,记录的一次优先级计算的结果,在计算完成以后,会将curAdj
赋值给setAdj
.
××RawAdj
记录了没有经过限定(指其中的值可能超过了oom_score_adj文件所规定的-1000~~1000)的adj值
为了便于管理,在ProcessList中,预定义了oom_score_adj的可能取值,其实这种预定义也是对应用进程的一种分类
1 | 58 static final int UNKNOWN_ADJ = 1001; |
FOREGROUND_APP_ADJ = 0
,这个是前台应用进程的优先级,是用户正在进行交互的应用,他们很重要,系统不应当回收。这也是普通应用能够获取到的最高优先级
VISIBLE_APP_ADJ
是具有可见Activity进程的优先级:同一时刻,不一定只有一个Activity是可见的,如果前台Activity设置了透明属性,那么背后的Activity也是可见的
PERCEPTIBLE_APP_ADJ
是指用户可感知的进程
- 进程中包含了处于Pause状态的或者正在pause的activity
- 进程中包含了正在stop的activity
- 进程中包含了前台的Service
PREVIOUS_APP_ADJ
描述的是前一个应用的优先级。所谓“前一个应用”是指:在启动新的Activity时,如果新启动的Activity是属于一个新的进程的,那么当前即将被stop的Activity所在的进程便会成为“前一个应用”进程
HEAVY_WEIGHT_APP_ADJ
描述的重量级进程是指那些通过Manifest指明不能保存状态的应用进程
除了这些常见的应用以外,还有很多常驻内存的系统应用,他们是系统实现的一部分,例如状态栏、keyguard。所以这部分的优先级比所有的应用进程的优先等级更高。PERSISTENT_SERVICE_ADJ = -700
、PERSISTENT_PROC_ADJ = -800
然后就还有一些核心系统服务,如果这些系统服务不存在,那么系统就将无法工作,所这些应用的优先级最高,几乎任何时候都需要存在的:SYSTEM_ADJ = -900
、NATIVE_ADJ = -1000
Schedule Group
内核负责了进程的CPU调度,所有运行中的进程并非能平等的获取相等的时间片。在ProcessRecord中,通过Schedule Group来记录进程的调度租
1 | 98 int curSchedGroup; // Currently desired scheduling class |
他们可能取值定义在ProcessList.java中
1 | 127 // Activity manager's version of Process.THREAD_GROUP_BG_NONINTERACTIVE |
Process State
进程的状态会影响虚拟机对于进程的内存分配和垃圾回收的策略,PrecessRecord中的这几个属性记录了进程的状态
1 | 101 int curProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state |
这些属性可能的取值定义在ActivityManager
中
1 | 330 /** @hide Process does not exist. */ |
优先级的更新
.系统对处于不同状态的进程设置不同的优先级,但实际上,进程的状态是一直在变化的。并且Activity可能会使用其他的Service或者ContentProvider。当Activity的进程优先级发生变化时,它所使用的Service或者ContentProvider的优先级也会随之发生变化
ActivityManagerService
中,有两个方法用来更新进程的优先级
final boolean updateOomAdjLocked(ProcessRecord app)
:针对单个的进程更新优先级在许多情况下需要对指定应用的进程更新优先级
- 当有一个新的进程开始使用本进程的ContentProvider
- 当本进程中的一个Service被其他进程bind或者unbind
- 当本进程中的Service执行完成或者退出了
- 当本进程中一个BroadcastReceive正在接受广播
- 当本进程中的BackUpAgent启动或者退出了
final void updateOomAdjLocked()
:针对所有的进程更新优先级在某些情况下,系统需要对所有的应用进程的优先级进行更新
- 当有一个新的进程启动时
- 当有一个进程退出时
- 当系统正在清理后台进程时
- 当有一个进程被标记为前台进程时
- 当有一个进程进入或者退出cached状态时
- 当系统锁屏或者解锁时
- 当有一个Activity启动或者退出时
- 当系统正在处理一个广播事件时
- 当前台Actiity发生改变时
- 当有一个Service启东时