1. zygote
1.1 zygote分析1.2 AppRuntime分析
1.2.0.1 创建虚拟机-startVm1.2.0.2 注册JNI函数-startReg 1.2.1 Welcome to Java World
1.2.1.1 建立IPC通信服务端-registerZygoteSocket1.2.1.2 预加载类和资源1.2.1.3 启动system_server1.2.1.4 有求必应之等待请求-runSelectLoopMode 1.2.2 关于zygote的总结 1.3 SystemServer分析
1.3.1 SystemServer的诞生1.3.2 SystemServer的重要使命
1.3.2.1 zygoteInitNative分析1.3.2.2 invokeStaticMain分析1.3.2.3 SystemServer的真面目 1.3.3 关于SystemServer的总结 1.4 zygote的分裂
1.4.1 ActivityManagerService发送请求1.4.2 响应请求1.4.3 关于zygote分裂的总结 1.5 拓展思考
1.5.1 虚拟机heapsize的限制1.5.2 开机速度优化1.5.3 Watchdog分析
1.5.3.1 创建和初始化Watchdog1.5.3.2 运行看门狗1.5.3.3 列队检查 1.6 本章小结
1. zygote 1.1 zygote分析 zygote最初的名字叫做app_process,这个名字在Android.mk文件中指定的,但是在运行过程中,app_process通过Linux下的pctrl系统调用将自己的名字换成了zygote,代码如下:
zygote的这个main很简单,其重要的功能是由AppRuntime的start来完成的
AppRuntime类的声明和实现均在App_main.cpp中,它是从AppRuntime类派生出来的,AppRuntime重载了onStarted、onZygoteInit和onExit函数
前面的diamagnetic调用了AppRuntime的start函数,start函数使用的是基类AppRuntime的start
通过上面的代码,有三个关键点:
1.创建虚拟机
2.注册JNI函数
3.通过JNI调用Java函数,在调用ZygoteInit的main函数之后,zygote便进入了Java世界
调用JNI的虚拟机创建函数,但是创建虚拟机时的一些参数是在startVm中确定的
关于dalvik虚拟机的详细参数,可以参见Dalvik/Docs/Dexopt.html中的说明
下一步需要给这个虚拟机注册一些JNI函数,因为后续Java世界用到的一些函数是采用native方式实现的,所以才必须提前注册这些函数,startReg函数如下:
register_jni_procs代码如下
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
for(size_t i = 0; i < count; i++)
{
if(array[i].mProc(env) < 0)
{
return -1;
}
}
return 0;
}
全局数组的gRegJNI变量
REG_JNI是一个宏,宏里面包括的就是那个mProc函数
mProc就是为了Java类注册了JNI函数,到目前为止,JNI函数已经注册,下一步就要分析CallStaticVoidMethod,通过这个函数,我们将进入Android打造的Java世界,并且最好情况就是永远不会回到Native世界
根据前面的分析可知,CallStaticVoidMethod最终将调用com.android.internal.os.ZygiteInit的main函数
在ZygoteInit的main函数中,我们列出了5大关键点,分别在上图中标记
zygote及系统中其他程序的通信没有使用Binder,而是采用了基于AF_UNIX类型的Socket,registerZygoteSocket函数的使命正是建立这个Socket,代码如下:
registerZygoteSocket就是创建一个服务端的Socket,考虑两个问题:1.谁是客户端?2.服务端会怎么处理客户端的消息?
我们现在要分析的就是preloadClasses和preloadResources函数,先看preloadClasses:
preloadClasses看起来非常简单,但是需要预先加载很多个类,preloadClass函数的执行时间比较长,这是导致Android系统启动慢的原因之一。
preloadResources和preloadClass类似,主要是加载framework-res.apk中的资源,在UI编程中常使用的com.android.R.XXX资源是系统默认的资源,它们就是由zygote加载的。
第三个关键点:startSystemServer,这个函数会创建Java世界中系统Service所驻留的进程system_server,该进程是framework的核心,如果它死了,就会导致zygote自杀,先来看看这个核心进程是如何启动的。
这里是一个分水岭,即zygote进行了一次无性繁殖,分裂出了一个system_server进程,即代码中的Zygote.forkSystemServer这句话。
等到zygote从startSystemServer返回后,将进入第四个关键函数,runSelectLoopMode,前面第一个关键点registerZygoteSocket中注册了一个用于IPC的Socket,不过那时还没有地方用,它的用途在runSelectLoopMode中体现出来。
runSelectLoopMode的逻辑:
1.处理客户链接和客户请求,其中客户在zygote中用ZygoteConnection对象来表示
2.客户的请求由ZygoteConnection的runOnce来处理
zygote在Android系统中创建了第一个Java虚拟机,同时成功的繁殖了framework的核心system_server进程,总结一下zygote创建java世界的步骤:
1.创建AppRuntime对象,并调用它的start,此后的活动由AppRuntime来控制
2.调用startVm创建Java虚拟机,然后调用startReg来注册JNI函数
3.通过JNI调用com.android.internal.os.ZygoteInit类的main函数,从此进入了Java世界,这时候的Java世界因为刚开创,啥都没有。
4.调用registerZygoteSocket,通过这个函数,它可以响应子孙后代的请求,同时zygote调用preloadClasses和preloadReesources,为java世界添砖加瓦。
5.zygote通过startSystemServer分裂了一个子进程system_server来为java世界服务
6.zygote完成了Java世界的初创工作,调用runSelectLoopMode后,便睡去了
7.以后的日子,zygote随时守护在周围,当接收到子孙后代的请求时,会随时醒来,为它们工作
SS的创建
SS是由Zygote通过Zygote.forkSystemServer函数fork诞生出来的,forkSystemServer是一个native函数,实现在dalvik_Zygote.c中,代码如下:
forkAndSpecializeCommon,代码如下:
setSignalHandler函数,它由Zygote在fork子进程前调用,代码如下:
作为Zygote的嫡长子,SS有着很高的的地位,可以到了与Zygote生死与共的地步。
SS代码如下:
SS调用handleSystemServerProcess来承担自己的职责
现在SS走到RuntimeInit中,代码在RuntimeInit.java中,如下:
zygoteInitNative是一个native函数,实现在AndroidRuntime.cpp中
由于SS是zygote fork出来的,所以它也拥有zygote进程中定义的这个gCurRuntime,也就是AppRuntime对象,onZygoteInit的代码在App_main.cpp中,如下:
SS调用zygoteInitNative后,将与Binder通信系统建立联系,这样SS就能够使用Binder了。
invokeStaticMain代码如下:
invokeStaticMain抛出了一个异常,在ZygoteInit的main函数中被截获,代码如下:
抛出的这个异常最后会导致com.android.server.SystemServer类的main函数被调用,为什么不在invokeStaticMain那里直接调用,而是采用抛出异常的方式,因为这个调用是在ZygoteInit.main中,相当于Native的main函数,即入口函数,位于堆栈的顶层,如果不采用抛出异常的方式,而是在invokeStaticMain那里调用,则会浪费之前函数调用所占用的一些调用堆栈。关于这个问题的深层次思考,可以利用fork和exec知识,可以认为这种抛出异常的方式是对exec的一种模拟,后续交给com.android.server.SystemServer类来处理。
ZygoteInit分裂产生的SS,其实就是为了调用com.android.server.SystemServer的main函数,代码如下:
其中main函数将加载libandroid_server.so库,这个库所包含的源文件在文件夹framework/base/services/jni下
(1)init1分析
init1是native函数,在com_android_server_SystemServer.cpp中实现,代码如下:
system_init的实现在system_init.cpp中,代码如下:
init1函数创建了一些服务,然后把调用调用线程加入到Binder通信中,不过其间还通过JNI调用了com.android.server.SystemServer类的init2函数
(2)init2分析
init2在Java层,代码在SystemServer.java中,代码如下:
启动了一个ServerThread线程,直接看run函数
init2函数就是单独创建了一个线程,用以启动系统的各项服务,Java世界的核心Service都在这里启动,所以非常重要。
SS的调用流程
zygote分裂出嫡长子system_server后,就通过runSelectLoopMode等待并处理来自客户的消息,以Activity的启动为例,具体分析下zygote是如何分裂的。
1.4.1 ActivityManagerService发送请求 ActivityManagerService也是由SystemServer创建的,假设通过startActivity来启动一个新的Activity,而这个Activity附属于一个还未启动的进程,那么这个还未启动的进程该如何启动?先看ActivityManagerService中的startProcessLocked函数,代码如下:
再看看Process的start函数,这个Process类是android.os.Process,代码在Process.java中,代码如下:
ActivityManagerService终于向zygote发送请求了,请求的参数中有一个字符串的值是"android.app.ActivityThread",因为ActivityManagerService驻留于SystemServer进程中,所以正是SS向Zygote发送了消息。
如何处理请求
每当有请求数据发来时,Zygote就会调用ZygoteConnection的runOnce函数,ZygoteConnection代码在Zygote Connection.java文件中,来看看它的runOnce函数:
看看新创建的子进程在handChildProc中做了啥
zygote分裂子进程后,自己将在handleParentProc中做一些扫尾工作,然后继续等待请求进行下一次分裂。
这个android.app.ActivityThread类,实际上是Android中apk程序所对应的进程,它的main函数就是apk程序的main函数,这个类的命名android.app中也可看出。
android系统运行的那些apk程序,其父进程都是zygote,这可以通过adb shell登陆后,用ps命令查看进程和父进程号来确认。
zygote的分裂由SS控制,这里用时序图表达zygote响应请求的过程:
在分析zygote创建虚拟机的时候说过系统默认的Java虚拟机队长最大为16MB,这个值对于需要使用较大内存的程序,如图片处理程序就远远不够,但是,我们可以修改这个默认值,这个改动是全局性的,也就是说所有的Java程序都会是这个32MB。
我们可以动态配置这个值吗?
设置一个配置文件,每个进程启动的时候根据配置文件中的参数来设置堆大小,这样做不太行,因为zygote是通过fork来创建子进程的,Zygote本身设置的信息会被子进程全部继承,例如Zygote设置的堆栈值为16MB,那么子进程也会是16MB。
关于这个问题的解决方案:
1.为Dalivk增加一个函数,这个函数允许动态调整最大堆的大小
2.zygote通过fork子进程后,调用exec家族的函数来加载另一个映像,该映像对应的程序会重新创建虚拟机,重新注册JNI函数,也就是模拟zygote创世界中前两天的工作,最后调用android.app.ActivityThread的main函数,这种方式应该是可行的,但是难度较大并且会影响运行速度。
Android的开机速度慢一直是个问题,从目前的情况看,有三个地方开机的时候耗时比较长:
1.ZygoteInit的main函数中preloadClasses加载了一千多个类,这个耗费时不少
2.开机启动时,会对系统内所有的apk文件进行扫描并收集信息,这个动作耗时非常长
3.SystemServer创建的那些Service会占用不少时间
如何减少preloadClasses的时间,其实,这个函数是可以去掉的,因为系统最终还是会在使用这个类的时候去加载,但是这样就破坏了Android采用fork机制来创建Java进程的本意,zygote预加载这些class,在fork子进程时,仅需要一个复制就行,这样就节约了子进程的启动时间。根据fork的copy-on-write机制可知,有些类如果不做改变,甚至都不用复制,他们就会直接和父进程共享数据,这样就会节省不少内存的占用。
最初存在的意义时因为早期嵌入式设备上的程序经常跑飞,所以专门设置了一个硬件看门狗,每隔一段时间,看门狗就去检查一下某个参数是不是被设置了,如果发现该参数没有被设置,则判断为系统出错,然后强制重启。
在软件层面上,Android对SystemServer的参数是否被设置也很谨慎,所以专门为它增加了一个看门狗,主要是看几个Service的门,一旦发现Service出现了问题,就会杀掉system_server,而这个会使zygote随其一起自杀,最后导致Java世界重启。
SS和Watchdog的交互流程可以总结为三个步骤:
1.Watchdog.getInstance().init()
2.Watchdog.getInstance().start()
3.Watchdog.getInstance().addMonitor()
getInstance用于创建Watchdog,代码如下:
Watchdog有了之后,看看init函数,代码如下:
SystemServer调用Watchdog的start函数,这将导致Watchdog的run在另一个线程中被执行,代码如下:
隔一段时间给另一个线程发送一条MONITOR消息,那个线程将检查各个Service的健康情况,看门狗等待检查结果,如果第二次还没有返回结果,那么就会杀掉SS
检查线程检查Service主要是有三个Service需要交给Watchdog检查:
1.ActivityManagerService
2.PowerManagerService
3.WindowManagerService
如果想要支持看门狗进行检查,就需要Service实现monitor接口,然后Watchdog就会调用monitor函数进行检查,检查的地方是在HeartbeatHandler类的handleMessage中,代码如下:
以PowerManagerService为例,看看是怎么把自己交给看门狗进行检查的,代码如下:
Watchdog调用各个monitor函数到底又检查了些什么,代码如下?
monitor检查的就是Service是不是发生了锁死,因为Watchdog最害怕系统服务死锁,对于这种情况只能采取杀死系统的方式。另外,Watchdog还能检查内存的使用情况。
1.zygote主要工作是开创了Java世界
2.本章介绍了创世界的七大步骤
3.介绍了zygote的嫡长子System_server进程(SS),这个进程是Java世界中的系统Service的驻留地
4.SS进程介绍了创建和初始化过程
5.分析了一个Activity所属进程的创建过程,进程是由ActivityManagerService发送请求给zygote,最后由zygote通过fork的方式创建的
6.讨论了Dalvik虚拟机对heap大小的设置,以及可能的修改方法
7.探讨Android系统的开机速度问题
8.分析了System_server中Watchdog的工作流程



