zygote作为App开发者所熟知的孵化器进程,在App界的地位是绝对的龙头老大。而zygote服务也是通过init作为服务启动起来的,其服务声明如下(以64位为例):
其文件位置为:system/core/rootdir/init.zygote64.rc
在init.rc中,可以看到相关的启动动作:
而start zygote最后真是发生了什么呢?又要把目光挪回到init中。
在前文中,说到过init解析了一系列的*.rc文件,收集了大量的Action进入到ActionManager。
在init逐行解析rc文件时,Action的解析如上图,触发了action的**AddCommand()**方法。
这个function就是后边会被调用的真正内容,这个内容可以通过FindFunction()找到:
对于Action,大体上内建支持的集合有如这样:
而start所对应的:
一探do_start究竟:
到这里可以做一下简单的梳理:
在init解析出Actions给ActionManager的同时,也解析了Service给ServiceList。
再次强化一下整体理解:
on XXX
叫做Action,而Action下的叫做Command。Command的全集可以通过system/core/init/builtins.cpp中的**BuiltinFunctionMap::map()**进行检索service XXX
叫做Service,Service下的东西叫做Option。Option的全集可以通过system/core/init/service.cpp中的**Service::OptionParserMap::map()**进行检索start zygote
就是一条Action,而Action执行的路径:
首先需要补充一点:
Command怎么和Function绑定在一起的就要回头看一下ActionParser了。ActionParser在解析过程中,总会调用Action::AddCommand方法:
即,通过map找到对应命令的function,添加到**commands_**中去。
看下ExpandArgsAndExecv的本体:
可以发现两个问题:
而Service的构造是通过ServiceParser::ParseSection()来的:
具体rc文件的解析过程有兴趣的同学可以围观system/core/init/parser.cpp,这里就不做详细分析了,过于枯燥。
结论是,结合之前zygote的service声明,最终会执行的命令就是:
/system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
至此,init进程关于zygote的生涯结束。
源码位置:frameworks/base/cmds/app_process
通过Android.mk可以确定app_process64可执行文件就在此生成(新版本已改为Android.bp):
下面深入源码剖析。
调用C/C++可执行程序,众所周知要从main方法看起:
这是一个标准的main入口方法,值得注意的有两点:
至于AndroidRuntime,这是个非常重要的角色,是Android运行时真正的起点,整个framework的奠基人,后边做详细分析。
此时argv指针所指向的完整字符串是:-Xzygote /system/bin --zygote --start-system-server
在解析之前会有一个预检查:
不难发现,在经过这个预检查后,i作为字符串指针,所代表的参数变成了:/system/bin --zygote --start-system-server
下面就为真正启动runtime开始筹备新的args了:
在组装args后,真正发起了调用:
其中setArgv0是为当前线程(fork出来的进程的默认线程)设置名称(pthread_setname_np)。
当前args中包含的内容只有两个:
关于app_process还有什么场景使用,后续会拓展衍生篇
AppRuntime这个类存在于app_main.cpp中,它其实本身很薄。它的父类AndroidRuntime是背后的集大成者,AppRuntime更多的是针对于其父类所暴露的一些生命周期虚函数做了必要的实现,比如:
完整的类图:
其父类的详细代码可以在线查看:AndroidRuntime
mClass是jclass类型。这里做了NULL初始化,其他传入了父类:
zygote并不是通过init所启动的第一个service、第一个进程。通过源码可以看到,init.rc中early-init第一个启动其实是ueventd(实际上还是init),甚至在之后的late-init环节中,binder相关启动时机比zygote还要早
再回头看一下调用start的参数是什么:
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
AndroidRuntime的start过程略长,分四部分看:
jni_invocation.Init(NULL)内部去加载了libart.so(通过dlopen),并将操作句柄保存在了JniInvocation的**handler_**中:
handle_作为后续做调用dlsym的参数使用。同时还对ART虚拟机做了三个灵魂拷问:
这三个方法都存在,那么说明这是一个合格的libart.so。
此方法内部是大量的配置参数组装过程,目标是为了创建一个JVM出来,也就是实践上一步的第二个问题。zygote参数影响的是JVM的JDWP配置,其他两个就是透过ART Runtime赋值的了:
这里需要临时插入另一个问题,JniInvocation作为另一个so——libnativehelper.so中的方法,为什么在AndroidRuntime.cpp中可以直接使用?并没有见到先dlopen它的地方。这个可以通过frameworks/base/core/jni/Android.bp找到答案,AndroidRuntime.cpp就属于这里。在这个(Makefile)文件中,可以发现,libnativehelper是作为其shared_libs使用的,所以可以直接调用。
此时,虚拟机的实例有了,用于JNI调用的Env也有了。
AppRuntime复写了父类的虚函数:
zygote的case下,这里是不会做任何事情的。
至此,VM创建成功,也有了基础的JNI调用环境。第一步结束。
startReg的实现:
gRegJNI是非常大体量的JNI注册集合体:
以我的源码数下来,总共有155个注册JNI的方法,都是与Android系统各个API息息相关的内容
以RuntimeInit为例:
startReg中所传入的env参数会被作为入参传入到register*方法中,而最终会触发env->RegisterNatives方法(涉及虚拟机的部分不再深入了)。
而关于LocalFrame,官方解释如下:
Every “register” function calls one or more things that return
a local reference (e.g. FindClass). Because we haven’t really
started the VM yet, they’re all getting stored in the base frame
and never released. Use Push/Pop to manage the storage.
之前我们启动了VM,只是我们没有在这个VM中做真正的方法调用。每一个register函数都会通过env执行起来,而他们都会产生本地引用,如果不加操作,那么这些本地引用不会被自动销毁,造成无用数据的累积,所以这里使用LocalFrame收集他们,并最后释放。
总结:这一步就是将众多Android系统相关功能的JNI注册到JVM中去,便于后续Java环境中进行调用。
strArray会用来存储所组装出来的参数,如图中注解所写,参数的内容会是:
如官方注释所说,当前所在的这个线程将会是即将到达的Java世界的主线程。
当前线程的特点:
在发起JVM调用前,结合上边的各种梳理,各种参数为:
接下来,随着env->CallStaticVoidMethod()的调用,启动流程进入到以ZygoteInit为起点的Java领域。
ZygoteInit.java