书接上文
上一篇文章分享了app_main的主流程,最后会调用AndroidRuntime的start()方法执行ZygoteInit。这里从native code运行Java code。
本文涉及到zygote进程和system_server进程。
ZygoteInit简介
ZygoteInit.java经过编译最终生成到framework.jar,生成到系统的/system/framework/目录。系统启动的时候,会进行加载。
这个类是zygote进程的启动类。预初始化一些类,然后会等待sockets传来的命令。根据传来的命令,创建子进程,子进程会继承虚拟机的初始状态。
源码分析
源码在下面的路径,一共1067行。我们直接看ZygoteInit的main()方法。
Frameworks/base/core/java/com/android/internal/os/ZygoreInit.java
根据传入的参数设置局部变量的值。startSystemServer=true、abiList=armeabi-v7a,armeabi、socketName使用默认值zygote。
创建socket,并启动listen模式。这里还有一个细节比较模糊:fd的由来是怎样一个逻辑?涉及到init进程一些细节,后面再详细分析。
preload()方法进行预加载,依次包括ICU data、java class文件、resources、openGL、sharedLibraries、TextResources。
这里是启动子进程system_server。system_server进程,在下面的章节分析。这里只看父进程zgote的逻辑。在父进程zygote这里调用fork()返回的pid是大于零的整数,所以这个函数会直接返回true,进行下面的流程。
这里会进入无限循环,等待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()方法
直接看关键部分,Zygote.forkSystemServer()返回的pid是0,然后会调用handleSystemServerProcess()。这里的关键逻辑就是调用fork()方法,从zygote分裂出system_server进程。
a:system_server进程不需要socket进行IPC,关闭zygote创建的socket。
b:设置进程名字。niceName是上一步传入的"system_server"。
c:从环境变量读取systemserver jar包的路径,并进行加载。通过命令env可以查看系统的环境变量。
d:把剩余的参数传给SystemServer类
a:进行一些简单的初始化设置。时区设置,日志复位,代理设置,网络设置等等。
b:这是一个native的方法,暂时不分析。
c:字面意思,应用程序初始化。
这个方法进行一些虚拟机设置,参数解析,然后调用invokeStaticMain()。
通过Java的反射机制取得SystemServer类的main()方法,然后主动抛出一个预设的异常,传入main()方法和main()方法的参数。
现在回到zygoteInit.main(),捕获MethodAndArgsCaller异常,然后调用run()方法。这是一个很巧妙的设计,达到执行目标方法的同时,可以绕过zygote的loop逻辑,也可以摆脱zygote的堆栈的束缚。
这里已经很明显了,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,只是检查子进程的状态,不会阻塞。)
程序运行结果:
閱讀更多 做事情的幻想家 的文章