基于Android10.0,分析进程的创建过程
一、概述
每个app在启动前都必须创建一个进程,这个进程是由zygote fork而来,进程具有独立的资源空间,用于app上运行的各种Activity、Service等组件。大多数情况下一个应用运行在一个进程中,除非在AndroidManifest.xml中配置Android:process属性,或者通过Native代码fork进程。相对于线程,线程没有独立的地址空间,与其所在进程之间资源共享。
Android系统根据进程的重要性主要分为前台进程、可见进程、服务进程、后台进程、空进程。为了回收系统资源,系统会根据重要性高低来清除进程。
下图是进程的创建过程
1.APP发起进程:当从桌面启动应用,则发起进程是Launcher所在进程;当从某App内启动远程进程,则发送进程是该app所在的进程。发送进程通过binder发送消息给systemserver进程。
2.systemserver进程:调用Process.start方法,通过socket向zygote进程发送创建新进程的请求。
3.zygote进程:执行ZygoteInit.main进入runSelectLoop循环体内,当有客户端连接时,便会执行ZygoteConnection.processOneCommand方法,经过层层调用fork新的应用进程。
4.新进程:执行handleChildProc方法,最后调用ActivityThread.main方法。
下面将从进程的角度分析进程创建的过程。
二、systemserver发起请求
1. Process.start
[->Process.java]
1 | public static final ProcessStartResult start(final String processClass, |
1 | public final Process.ProcessStartResult start(final String processClass, |
2. Process.startViaZygote
[->ZygoteProcess.java]
1 | private Process.ProcessStartResult startViaZygote(final String processClass, |
该过程主要是配置argsForZygote列表信息,列表信息保存了uid,gid,targetSdkVersion,appDataDir等信息。
3. zygoteSendArgsAndGetResult
[->ZygoteProcess.java]
1 | private static Process.ProcessStartResult zygoteSendArgsAndGetResult( |
这个方法主要是通过socket通道向zygote进程发送一个参数列表信息,然后进入阻塞等待状态,直到远端的socket服务端发送回来新创建的进程pid才返回。
3.1 openZygoteSocketIfNeeded
[->ZygoteProcess.java]
1 | private ZygoteState openZygoteSocketIfNeeded(String abi) |
这个方法主要是根据abi来选择与zygote还是zygote64来进行通信。
在systemserver进程的zygoteSendArgsAndGetResult方法通过socket向zygote发送消息,这时会唤醒Zygote进程,来响应systemserver客户端的请求,下面是zygote来创建进程。
三、zygote创建进程
在systemserver启动过程上中有介绍zygote进程启动,它是由init进程创建,进程启动之后调用ZygoteInit.main方法,经过socket管道,预加载资源后,进入runSelectLoop方法。
4.ZygoteInit.main
[->ZygoteInit.java]
1 | public static void main(String argv[]) { |
5.ZS.runSelectLoop
[->ZygoteServer.java]
1 | /** |
第三小节zygoteSendArgsAndGetResult发送请求来建立连接,根据不同的请求做相应的处理:
pollIndex小于blastulaPoolEventFDIndex为来自Zygote server socket的请求;
执行acceptCommandPeer,创建ZygoteConnection对象,并添加socketFDs数值,建立连接后,可以和客户端进行通信,进入processOneCommand方法接受客户端数据,并执行进程创建工作。
pollIndex大于等于blastulaPoolEventFDIndex为胚胎池事件FD或者胚胎上报管道;
读取管道信息,调用fillBlastulaPool方法,创建胚胎(blastula)进程。
5.1 ZC.acceptCommandPeer
1 | /** |
客户端发送过来的connect操作,服务端zygote执行accept操作,然后客户端写数据,zygote服务端读数据。
在没有连接的情况下会进入休眠状态,当有新创建的进程发送连接请求时,唤醒zygote进程,创建socket通道,然后执行processOneCommand方法。
6. ZC.processOneCommand
[->ZygoteConnection.java]
1 | /** |
这里主要是处理客户端返回过来的参数,并调用forkAndSpecialize方法创建进程,返回pid
7.Zygote.forkAndSpecialize
[->Zygote.java]
1 | /** |
Zygote进程有4个子线程分别为ReferenceQueueD、FinalizerDaemon、FinalizerWatchd、HeapTaskDaemon,图中的线程名显示并不完整,是由于底层的进程结构体task_struct是由长度16的char型数组保存。
8. ZygoteHooks.preFork
[->ZygoteHooks.java]
1 | public void preFork() { |
8.1 Daemons.stop
[->Daemons.java]
1 | public static void stop() { |
此守护线程stop方式是先调用目标线程的interrupt方法,然后再调用目标线程join方法,等待线程执行完成。
1 | /** |
8.2 waitUntilAllThreadsStopped
[->ZygoteHooks.java]
1 | private static void waitUntilAllThreadsStopped() { |
8.3 nativePreFork
[->dalvik_system_ZygoteHooks.cc]
nativePreFork通过JNI最终调用如下方法:
1 | static jlong ZygoteHooks_nativePreFork(JNIEnv* env, jclass) { |
ZygoteHooks.preFork主要功能是停止Zygote的4个Daemon子线程的运行,等待并确保Zygote是单线程(用于fork效率),并等待这些线程的停止,初始化gc堆得工作,并将线程转换为long型并保存到token。
9. nativeForkAndSpecialize
[->com_android_internal_os_Zygote.cpp]
nativeForkAndSpecialize通过JNC调用如下方法
1 | static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( |
10. ForkCommon
[->com_android_internal_os_Zygote.cpp]
1 | // Utility routine to fork a process from the zygote. |
fork采用copy on write技术,这是linux创建进程的标准方法,调用一次,返回两次,返回值有3中类型。
父进程中,fork返回新创建的子进程pid
子进程中,fork返回0
当出现错误(进程数超过上限或者系统内存不足)时,fork返回负数。
fork主要工作是寻找空闲的进程号pid,然后从父进程中拷贝进程信息,例如代码段和数据段,fork后子进程要执行的代码。Zygote进程是所有Android进程的母体,包括systemserver和各个app进程。zygote利用fork方法生成新的进程,对于新进程A复用Zygote进程本身的资源,再加上新进程A相关的资源,构成新的应用进程A.
copy on write过程:当父子进程任何一方修改内存数据时(on-write时机),才发生缺页中断,从而分配新的物理内存(copy操作)。原理:写时拷贝是指子进程与父进程的页表都指向同一物理内存,fork过程只拷贝父进程的页表,并标记这些页表是可读的。父子进程共用同一份物理内存,如果父子进程任一方想要修改这块物理内存,那么就会触发缺页异常,linux收到改中断便会创建新的物理内存,并将这个两个物理内存都设置为可写状态,从而父子进程各自用用独立的物理内存。
10.1 fork
[->bionic/fork.cpp]
1 | int fork() { |
在执行clone前后都有相应的回调方法
__bionic_atfork_run_prepare() : fork完成执行子进程回调方法
__bionic_atfork_run_child() : fork完成执行子进程回调方法
__bionic_atfork_run_parent() : fork完成执行父进程回调方法
这个三个方法bionic/pthread_atfork.cpp中,如果有业务需求,可以拓展回调方法
11. SpecializeCommon
[->com_android_internal_os_Zygote.cpp]
1 | // Utility routine to specialize a zygote child process. |
11.1 callPostForkChildHooks
[->Zygote.java]
private static void callPostForkChildHooks(int runtimeFlags, boolean isSystemServer,
boolean isZygote, String instructionSet) {
ZygoteHooks.postForkChild(runtimeFlags, isSystemServer, isZygote, instructionSet);
}
11.2 postForkChild
[->ZygoteHooks.java]
1 | public void postForkChild(int runtimeFlags, boolean isSystemServer, boolean isZygote, |
11.3 nativePostForkChild
[->dalvik_system_ZygoteHooks.cc]
1 | static void ZygoteHooks_nativePostForkChild(JNIEnv* env, |
11.4 InitNonZygoteOrPostFork
[->runtime.cc]
1 | void Runtime::InitNonZygoteOrPostFork( |
12. postForkCommon
[->ZygoteHooks.java]
1 | public void postForkCommon() { |
ZygoteHooks的postForkCommon主要功能是fork新进程后,启动zygote的四个Daemon线程,引用队列线程,析构线程,析构监控线程、java堆整理线程。
13. 小结
1.ZygoteInit.main方法,经过socket管道,预加载资源后,进入runSelectLoop方法。
2.runSelectLoop执行acceptCommandPeer,创建ZygoteConnection对象,并添加socketFDs数值,建立连接后,可以和客户端进行通信,进入processOneCommand方法接受客户端数据,并调用forkAndSpecialize方法创建进程。
3.forkAndSpecialize主要功能是
preFork:停止Zygote的4个Daemon线程的运行,初始化gc堆;
nativeForkAndSpecialize:调用fork创建新进程,设置新进程的主线程id,重置gc性能数据,设置信号处理函数等功能、启动JDWP线程。
postForkCommon:启动4个Daemon线程。
到此App进程完成创建的所有工作,执行forkAndSpecialize后,新创建的App进程进入了handleChildProc,后面就是App进程的工作了。
四、新进程运行
在第6小节中processOneCommand过程中调用forkAndSpecialize创建完新进程后,返回值pid=0即运行在子进程,继续开始执行handleChildProc方法。
14. ZC.handleChildProc
[->ZygoteConnection.java]
1 | private Runnable handleChildProc(ZygoteArguments parsedArgs, FileDescriptor[] descriptors, |
15. ZygoteInit.zygoteInit
[->ZygoteInit.java]
1 | public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, |
15.1 commonInit
[-> RuntimeInit.java]
1 | protected static final void commonInit() { |
15.2 nativeZygoteInit
[->AndroidRuntime.cpp]
1 | static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz) |
[->app_main.cpp]
1 | virtual void onZygoteInit() |
ProcessState::self()主要工作是调用open打开dev/binder驱动设备,再利用mmap映射内核的地址空间,将Binder驱动的fd赋值给ProcessState对象中的mDriverFD,用于交互操作。
startThreadPool用于创建一个binder线程池,不断进行talkWithDriver。
15.3 applicationInit
[-> RuntimeInit.java]
1 | protected static Runnable applicationInit(int targetSdkVersion, String[] argv, |
16. findStaticMain
[-> RuntimeInit.java]
1 | protected static Runnable findStaticMain(String className, String[] argv, |
MethodAndArgsCaller方法中的m是指main方法,argv是指ActivityThread,根据流程4可知,下一步进入到 caller.run()方法,也即MethodAndArgsCaller.run。
17. MethodAndArgsCaller
[-> RuntimeInit.java]
1 | static class MethodAndArgsCaller implements Runnable { |
最后进入ActivityThread.main方法
18. AT.main
[->ActivityThread]
1 | public static void main(String[] args) { |
五、总结
Process.start方法是阻塞操作,直到进程创建完成并返回相应的进程pid时,才完成该方法。
app第一次启动时,其启动所在的进程会通过binder发送消息给Systemserver进程。systemserver进程是从Process.start开始,执行创建进程的操作。
Systemserver进程:通过Process.start方法发起创建新进程请求,会先收集各种新进程uid,gid,nice-name等相关参数,然后通过socket通道发送给zygote进程。
Zygote进程:接收到systemserver进程发送过来的参数后封装成Arguments对象,forAnddSpecialize方法是进程创建过程中最关键的一个过程,具体主要执行下面3个方法。
preFork:停止Zygote的4个Daemon线程(java堆内存整理线程,引用队列线程、析构线程以及监控线程)的运行,初始化gc堆;
nativeForkAndSpecialize:调用fork创建新进程,设置新进程的主线程id,创建java堆处理的线程池,重置gc性能数据,设置信号处理函数等功能、启动JDWP线程。
postForkCommon:启动之前被停止4个Daemon线程。
新进程:进入handleChildProc方法,设置进程名,打开binder驱动,启动新的binder线程,然后设置虚拟机参数,再通过调用目标类的main方法,即ActivityThread.main.
新进程由于会调用nativeZygoteInit,这个过程会调用startThreadPool创建binder线程池,所以每个进程一定至少包含一个Binder线程。
附录
源码路径
1 | frameworks/base/core/java/android/os/ZygoteProcess.java |