从源码的角度看安卓开机流程-ZygoteInit

从源码的角度看安卓开机流程-ZygoteInit

书接上文

上一篇文章分享了app_main的主流程,最后会调用AndroidRuntime的start()方法执行ZygoteInit。这里从native code运行Java code。


本文涉及到zygote进程和system_server进程。

ZygoteInit简介

从源码的角度看安卓开机流程-ZygoteInit

ZygoteInit.java经过编译最终生成到framework.jar,生成到系统的/system/framework/目录。系统启动的时候,会进行加载。

这个类是zygote进程的启动类。预初始化一些类,然后会等待sockets传来的命令。根据传来的命令,创建子进程,子进程会继承虚拟机的初始状态。

源码分析

源码在下面的路径,一共1067行。我们直接看ZygoteInit的main()方法。

Frameworks/base/core/java/com/android/internal/os/ZygoreInit.java

从源码的角度看安卓开机流程-ZygoteInit

根据传入的参数设置局部变量的值。startSystemServer=true、abiList=armeabi-v7a,armeabi、socketName使用默认值zygote。

从源码的角度看安卓开机流程-ZygoteInit

从源码的角度看安卓开机流程-ZygoteInit

创建socket,并启动listen模式。这里还有一个细节比较模糊:fd的由来是怎样一个逻辑?涉及到init进程一些细节,后面再详细分析。

从源码的角度看安卓开机流程-ZygoteInit

preload()方法进行预加载,依次包括ICU data、java class文件、resources、openGL、sharedLibraries、TextResources。

从源码的角度看安卓开机流程-ZygoteInit

从源码的角度看安卓开机流程-ZygoteInit

这里是启动子进程system_server。system_server进程,在下面的章节分析。这里只看父进程zgote的逻辑。在父进程zygote这里调用fork()返回的pid是大于零的整数,所以这个函数会直接返回true,进行下面的流程。

从源码的角度看安卓开机流程-ZygoteInit

从源码的角度看安卓开机流程-ZygoteInit

这里会进入无限循环,等待socket IPC,执行应用程序创建命令。其它应用程序就是通过这里fork()子进程,创建出来的。应用程序的启动流程后面会专门分析,此处不做展开。

至此,zygote进程的启动流程就分析完了。它是通过native的可执行程序app_process调用到Java层的ZygoteInit,完成进程创建。可执行程序app_process是配置在init.zygote.rc文件里面,由init进程解析执行。

system_server进程启动

system_server的启动涉及到多线程编程的知识,主要是fork()和waitpid()两个函数的使用,不了解这块知识的请先参看备注1。

system_server是zygote进程的子进程。上文分析了父进程zygote的启动流程。这里分析子进程system_server的启动流程。

了解了fork()的知识点后,我们知道system_server和zygote两个进程在前面的code是没有差异的,差异是在调用fork()函数之后,代码的执行逻辑才有了差异。

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java 的main()方法会调用startSystemServer()方法

从源码的角度看安卓开机流程-ZygoteInit

直接看关键部分,Zygote.forkSystemServer()返回的pid是0,然后会调用handleSystemServerProcess()。这里的关键逻辑就是调用fork()方法,从zygote分裂出system_server进程。

从源码的角度看安卓开机流程-ZygoteInit

a:system_server进程不需要socket进行IPC,关闭zygote创建的socket。

b:设置进程名字。niceName是上一步传入的"system_server"。

c:从环境变量读取systemserver jar包的路径,并进行加载。通过命令env可以查看系统的环境变量。

从源码的角度看安卓开机流程-ZygoteInit

d:把剩余的参数传给SystemServer类

从源码的角度看安卓开机流程-ZygoteInit

a:进行一些简单的初始化设置。时区设置,日志复位,代理设置,网络设置等等。

b:这是一个native的方法,暂时不分析。

c:字面意思,应用程序初始化。

从源码的角度看安卓开机流程-ZygoteInit

这个方法进行一些虚拟机设置,参数解析,然后调用invokeStaticMain()。

从源码的角度看安卓开机流程-ZygoteInit

通过Java的反射机制取得SystemServer类的main()方法,然后主动抛出一个预设的异常,传入main()方法和main()方法的参数。

从源码的角度看安卓开机流程-ZygoteInit

现在回到zygoteInit.main(),捕获MethodAndArgsCaller异常,然后调用run()方法。这是一个很巧妙的设计,达到执行目标方法的同时,可以绕过zygote的loop逻辑,也可以摆脱zygote的堆栈的束缚。

从源码的角度看安卓开机流程-ZygoteInit

这里已经很明显了,Java反射机制,执行SystemServer.main(),开始进入system_server的主逻辑。

下一篇文章分析system_server的主逻辑。

备注:

1 关于fork()和waitpid()

调用fork()函数,会创建一个新的进程,叫做子进程,对应的就是父进程。fork()函数的返回值是进程ID(pid),pid在父进程和子进程的值是不一样的。在父进程中得到的pid值是子进程的进程ID,是大于零的值;在子进程中,得到的pid的值是0。

父进程通过fork()可以卵化一个子进程,相当于一个进程变成两个进程,同时具有以下特点:

1. 这两个进程的代码一致,且代码执行到的位置一致。

2. 区别是进程ID(PID)不一样

3. 一次调用,两次返回。父进程返回的是子进程的PID,从而父进程可以跟踪子进程的状态,以子进程的PID作为函数参数。子进程返回的PID是0。

父进程调用waitpid()函数,传入子进程的pid作为参数,可以查看子进程的状态或是进行某种同步。

一个简单的例子:父进程通过fork()创建一个子进程,然后进入等待状态,等子进程执行完成,父进程再执行自己的任务。(system_server启动逻辑中,waitpid()第三个参数传入的是1,只是检查子进程的状态,不会阻塞。)

从源码的角度看安卓开机流程-ZygoteInit

程序运行结果:

从源码的角度看安卓开机流程-ZygoteInit


分享到:


相關文章: