【Android源码阅读】zygote启动流程之native部分

安卓Xposed框架交流2018-06-20 08:26:46

如果对本文感兴趣,强烈建议下载源码,对照源码阅读本文。


大神请绕行。?


—— 00 前言 ——


Android的zygote是一个什么东东,对于Android开发者来说,已经完全没有必要解释。关于zygote的启动流程分析的博客文章已经有很多,但是我发现完全有必要亲自来一波源码阅读操作,来为自己答疑解惑。最终通过分析发现,zygote确实不是原来想象中的zygote,纠正了自己以往的错误看法;于是,将这波分析,记录整理,并分享给朋友们。?

Android源码阅读是基于Nougat版本的android-7.1.1_r1代码分支。如果阅读本文时需要对照代码,记得使用该分支代码。我将Android源码放在了~/Android/AndroidCode/aosp目录下。本文列出的所有源码文件路径都是该目录下的相对路径。

关于代码下载,此处尽提供最简单的下载方法,就是通过百度网盘:http://pan.baidu.com/s/1ngsZs。关于官方下载方法,请自行阅读:https://source.android.google.cn/setup/。

Android源码阅读过程中使用的手机是Google亲儿子Nexus 5X,搭载的是64-bit?ARMv8-A CPU。

Android源码阅读过程中使用的PC操作系统是Ubuntu 16.04。


—— 01 序幕 ——


我们首先使用ag视讯注册|平台将手机链接到电脑,查看基本信息,以便有个感性的认识。?

图(1)

通过ps命令查看运行中的zygote,发现竟然有两个,和原来认识的zygote不一样啊。

之前使用的都是搭载Android 4.4.4系统和32位CPU的手机,现在看到有一个zygote64,猜测可能这两个zygote分别是对64位和32位程序的支持,毕竟Nexus 5X搭载的是64位的CPU。带着疑问和猜测,我们来检查是不是会有两个app_process程序呢?

图(2)

上图显示,猜测可能是正确的。为了进一步确认,我把这个程序pull到电脑上,使用readelf查看程序的属性。

图(3)

图(4)

至此,确认了我的猜测,zygote64和zygote分别是对64位和32位程序的支持,来孵化出64位和32位Android程序进程。

既然如此,我们就来看看两个zygote都是怎么启动的,以及它们的差别吧。


—— 02 系统启动 ——


Android系统衍生自Linux系统,用户空间的第一个程序进程是init进程,其进程ID是1,负责设置系统参数并启动其他系统程序和应用程序。

init程序源码文件路径:system/core/init/init.cpp

init程序其中一项工作是读取配置文件/init.rc并执行/init.rc文件中配置的任务。

图(5)

init.rc源码文件路径:system/core/rootdir/init.rc

图(6)

init.rc配置文件通过import又导入了一些其他初始化相关的的配置文件,其中一项导入是:

import /init.${ro.zygote}.rc

很明显这个文件名包含了一个和zygote相关的环境变量或系统属性,我们来看看这个ro.zygote变量的值是什么:

图(7)

所以,init.rc文件导入了init.zygote64_32.rc这个配置文件。

init.zygote64_32.rc源码文件路径:system/core/rootdir/init.zygote64_32.rc

从这里可以看出,zygote的初始化配置文件是可变的。Google做的真周到,为了适配不同的硬件环境,实现并提供了不同的zygote配置文件。那到底有多少zygote配置文件可以选择呢?

图(8)

init.zygote64_32.rc文件内容如下:

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
writepid /dev/cpuset/foreground/tasks

service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
class main
socket zygote_secondary stream 660 root system
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks

可以清楚的看到,Android系统初始化过程中,会先后执行/system/bin/app_process64/system/bin/app_process32两个程序。这两个程序执行后,产生两个进程,分别对应zygote64zygote

app_process64app_process32的源码文件是同一个,只是编译生成了64位和32位的两个程序而成而已。

app_process源码文件路径:frameworks/base/cmds/app_process/app_main.cpp

上边啰嗦了这么多,是我对Android系统一次重新认识的整理,知道了zygote是怎么来的,虽然没什么软用

接下来我们正式进入framework层,开始zygote的源码阅读,看看zygote到底干了些什么重要的事情。因为涉及到两个zygote进程,为了清晰二者的差异,我会分开整理,这个虽然啰嗦,应该会更容易理解。


—— 03 app_process.cpp ——


init.zygote64_32.rc文件内容显示,Android系统会先执行/system/bin/app_process64程序启动zygote64进程。打开源码文件frameworks/base/cmds/app_process/app_main.cpp,找到程序入口main方法,如下图所示:

图(9)

备注:(1)为了和源码注释区分,我添加的注释会包含fooree标记。(2)左侧行号,与Google源码原始行号可能不同,因为我添加的注释可能改变代码行号。

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote

根据init.zygote64_32.rc配置,执行app_process64程序时,main方法参数如下:

argc = 6
argv = ["/system/bin/app_process64", "-Xzygote", "/system/bin", "--zygote", "--start-system-server", "--socket-name=zygote"]

197行:创建AppRuntime类型变量runtime。AppRuntime类扩展了AndroidRuntime类,该类表示Android运行时,其实可以简单认为运行时就是dalvik虚拟机(VM),虽然这样不正确。

200、201行:第0个参数是"/system/bin/app_process64",该行表示不需要这个参数,直接跳过丢弃。

接下来是一个for循环,将第1个参数"-Xzygote"传递给runtime变量。具体逻辑很简单。

图(10)

再接下来就是解析其他参数,过程如下:

图(11)

以上解析参数的过程,注释写的很详细,相信一看就懂。不过还有一处需要单独说明一下,那就是252行的ZYGOTE_NICE_NAME,其定义代码位于main方法的上边,意思是:

如果是64位程序,ZYGOTE_NICE_NAME = zygote64。

如果是32位程序,ZYGOTE_NICE_NAME = zygote。

#if defined(__LP64__)
static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist64";
static const char ZYGOTE_NICE_NAME[] = "zygote64";
#else
static const char ABI_LIST_PROPERTY[] = "ro.product.cpu.abilist32";
static const char ZYGOTE_NICE_NAME[] = "zygote";
#endif

int main(int argc, char* const argv[])

通过图(11)展示的解析参数过程,得出结论如下:

zygote = true; ? ? ? ? ? 
startSystemServer = true;
application = false; ? ? ?
niceName = "zygote64"; ? ? ? ? ? ? ?
className 没有赋值,className.isEmpty()=true;

根据这些结论,又把这些参数和其他参数保存到一个名为args的字符串集合中,过程如下:

图(12)

图(13)

最终args集合内容是:

["start-system-server", "--abi-list=arm64-v8a", "--socket-name=zygote"]

接下来我们就会知道,为什么图(1)中看到有一个zygote64进程。

图(14)

图(15)


然后,代码调用执行了runtimestart方法,这个方法定义在源码frameworks/base/core/jni/AndroidRuntime.cpp文件中。


—— 04 AndroidRuntime.cpp ——


通过查看注释,AndroidRuntime::start()方法的功能是:启动Android运行时,具体来说,就是启动dalvik虚拟机(DVM),并且调用"className"这个参数表示的Java类的"static void main(String args)"方法。

图(16)

这里我们似乎看到了一个我们一直很熟悉而又不太理解的东东:为什么Java的HelloWorld程序,要实现一个"public static void main(String args)"方法才能够被执行?

AndroidRuntime::start()方法内容不多,干活很干脆,上来就直接调用startVm()方法启动DVM。startVM()的内容其实很多,这里不再讨论,好奇的小伙伴自己阅读吧。

图(17)

然后注册一些Android系统Java类(143个)的native方法,包括刚才在app_process.cpp中的main方法结尾处看到的两个类:

com.android.internal.os.ZygoteInit
com.android.internal.os.RuntimeInit

dalvik虚拟机已经启动,并且注册好了native方法,接下来就是调用com.android.internal.os.ZygoteInitpublic static void main(String args)方法进入Java世界了。请看下图中的1057行。

图(18)

还记得AndroidRuntime::start()方法的参数是什么吗?如果不记得,就返回去看看图(14)。不过我还是整理如下看的清楚:

className = "com.android.internal.os.ZygoteInit"
options = ["start-system-server", "--abi-list=arm64-v8a", "--socket-name=zygote"]
zygote = true

在图(18)中,1028-1044行代码是在创建java.lang.String[] args对象,数组的长度是options的长度加一,所以args.length=4

1057行是进入Java世界的C++代码,这一行代码执行后不会返回,知道虚拟机退出,1057行之后的代码才会执行。

// fooree 进入Java世界, 该方法不会返回, 本行之后的代码不会执行, 直到虚拟机退出
env->CallStaticVoidMethod(startClass, startMeth, strArray);

等价于以下的Java代码:

String[] args = {"com.android.internal.os.ZygoteInit", "start-system-server", "--abi-list=arm64-v8a", "--socket-name=zygote"};
com.android.internal.os.ZygoteInit.main(args);

所以,接下来我们就要通过com.android.internal.os.ZygoteInit类进入Java世界了。native代码分析告一段落。


—— 05 结束语 ——


进入了Java世界,该向native世界说再见了。

小弟我才疏学浅,免不了存在理解错误之处,长篇大论免不了书写错误。希望各位大佬以及路过的小伙伴,不吝赐教,予以指点。

Copyright ? 品牌ag视讯注册|平台价格联盟@2017