4.1深入理解Android卷I---深入理解zygote---Zygote进程的学习

深入理解Zygotehttps://blog.csdn.net/Innost/article/details/47207845

一、 Zygote分析
Zygote本身是一个Native的应用程序,和驱动、内核等均无关系。根据第3章对于init的介绍我们可以知道,Zygote(Zygote进程由init通过fork而来)是由init进程根据init.rc文件中的配置项而创建的。在分析它之前,我们有必要先简单介绍一下“zygote”这个名字的来历。zygote最初的名字叫“app_process”,这个名字是在Android.mk文件中被指定的,但app_process在运行过程中,通过Linux下的pctrl系统调用将自己的名字换成了“zygote”,所以我们通过ps命令看到的进程名是“zygote”。
zygote玩的这一套“换名把戏”并不影响我们的分析,它的原型app_process所对应的源文件是App_main.cpp。

   int main(int argc, const char* const argv[]) { ...
    if (i< argc) {    
       arg = argv[i++];    
        if(0 == strcmp("--zygote", arg)) {    
          //我们传入的参数满足if的条件,而且下面的startSystemServer的值为true    
           bool startSystemServer = (i < argc) ?   
                    strcmp(argv[i],"--start-system-server") == 0 : false;    
           setArgv0(argv0, "zygote");    
          //设置本进程名为zygote,这正是前文所讲的“换名把戏”。    
           set_process_name("zygote");
          //①调用runtime的start,注意第二个参数startSystemServer为true
           runtime.start("com.android.internal.os.ZygoteInit", startSystemServer);    
        } ...}

Zygote的这个main函数虽很简单,但其重要功能却是由AppRuntime的start来完成的.
1. AppRuntime分析
AppRuntime类的声明和实现均在App_main.cpp中,它是从AndroidRuntime类派生出来的,AppRuntime重载了onStarted、onZygoteInit和onExit函数。
[–>AndroidRuntime.cpp]中主要做:① 创建虚拟机②注册JNI函数
③通过JNI调用Java函数,注意调用的函数是main,所属的类是 com.android.internal.os.ZygoteInit,传递的参数是“com.android.internal.os.ZygoteInit true”,调用ZygoteInit的main函数后,Zygote便进入了Java世界!也就是说,Zygote是开创Android系统中Java世界的盘古。
这三个关键点共同组成了开创Android系统中Java世界的三部曲。下面来具体分析它们:
1). 创建虚拟机——startVm
这个函数没有特别之处,就是调用JNI的虚拟机创建函数,但是虚拟机创建时的一些参数却是在startVm中被确定的:
该函数中大部分代码都是设置虚拟机参数的,学习到了JNIcheck和虚拟机heapsize.
关于dalvik虚拟机的详细参数,读者可以参见Dalvik/Docs/Dexopt.html中的说明。这个Docs目录下的内容,或许可帮助我们更深入地了解dalvik虚拟机。
2). 注册JNI函数——startReg
前面已经介绍了如何创建虚拟机,下一步则需要给这个虚拟机注册一些JNI函数。正是因为后续Java世界用到的一些函数是采用native方式来实现的,所以才必须提前注册这些函数。
register_jni_procs内部仅仅是一个封装,调用数组元素的mProc函数;全局数组的gRegJNI变量中则是 REG_JNI(register_android_util_Log), ...//共有100项.REG_JNI是一个宏,宏里边包括的就是那个mProc函数,最后通过分析宏里面的一个实例可知mProc就是为Java类注册JNI函数!
至此,虚拟机已创建好,JNI函数也已注册,下一步就要分析CallStaticVoidMethod了。通过这个函数,我们将进入Android所精心打造的Java世界,而且最佳情况是,永远也不回到Native世界。

2. Welcome to Java World
这个Java世界的入口在哪里?根据分析,CallStaticVoidMethod最终将调用com.android.internal.os.ZygoteInit的main函数.
ZygoteInit.java的main函数主要做了下列①注册Zygote用的socket②预加载类和资源③启动system_server进程;④zygote调用runSelectLoopMode()函数⑤很重要的caller.run()函数五件事情,下面一一进行分析。
1). 建立IPC通信服务端——registerZygoteSocket
Zygote以及系统中其他程序的通信没有使用Binder,而是采用了基于AF_UNIX类型的Socket。registerZygoteSocket函数的使命正是建立这个Socket。
registerZygoteSocket很简单,就是创建一个服务端的Socket。该Socket将listen并accept Client。
不过读者应该提前想到下面两个问题:
· 谁是客户端?
· 服务端会怎么处理客户端的消息?
建议:读者要好好学习与Socket相关的知识,这些知识对网络编程或简单的IPC使用,是会有帮助的。

好的,我会学习一下Socket相关的知识的。

2). 预加载类和资源(preloadClasses和preloadResources函数)
preloadClasses方法读起来比较简单。用coolfind在framework中搜索名为“preloaded-classes”的文件,最后会在framework/base目录下找到。这个preload-class一共有1268行,试想,加载这么多类得花多少时间!
说明:preload_class文件由framework/base/tools/preload工具生成,它需要判断每个类加载的时间是否大于1250微秒,超过这个时间的类就会被写到preload-classes文件中,最后由zygote预加载。这方面的内容,读者可参考有关preload工具中的说明,这里就不再赘述。
preloadClass函数的执行时间比较长,这是导致Android系统启动慢的原因之一。对这一块可以做一些优化,但优化是基于对整个系统有比较深入了解才能实现的。
注意:在拓展思考部分中,我们会讨论Android启动速度问题。
preloadResources和preloadClass类似,它主要是加载framework-res.apk中的资源。这里就不再介绍它了。
说明:在UI编程中常使用的com.android.R.XXX资源,是系统默认的资源,它们就是由Zygote加载的。
3). 启动system_server
我们现在要分析的是第三个关键点:startSystemServer。这个函数会创建Java世界中系统Service所驻留的进程system_server,该进程是framework的核心。如果它死了,就会导致zygote自杀。

  关于fork的知识,请读者务花些时间去研究。如果对fork具体实现还感兴趣,可参考《Linux内核源代码情景分析》一书。
  (作者为毛德操、胡希明)下面代码中,如果pid为零,则表示处于子进程中,也就是处于system_server进程中。

有时间会看一下fork的知识。
这里出现了一个分水岭,即Zygote进行了一次无性繁殖,分裂出了一个system_server进程。
4). 有求必应之等待请求——runSelectLoopMode
当Zygote从startSystemServer返回后,将进入第四个关键函数:runSelectLoopMode。前面,在第一个关键点registerZygoteSocket中注册了一个用于IPC的Socket,不过那时还没有地方用到它。它的用途将在这个runSelectLoopMode中体现出来。sServerSocket是我们先前在registerZygoteSocket建立的Socket
runSelectLoopMode比较简单,就是:
· 处理客户连接和客户请求。其中客户在Zygote中用ZygoteConnection对象来表示。
· 客户的请求由ZygoteConnection的runOnce来处理。
建议:runSelectLoopMode比较简单,但它使用的select的背后所代表的思想却并非简单。建议读者以此为契机,认真学习常用的I/O模型,包括阻塞式、非阻塞式、多路复用、异步I/O等,掌握这些知识,对于未来编写大型系统很有帮助。
关于Zygote是如何处理请求的(caller.run),将单独用一节内容进行讨论。
4.2.3 关于 Zygote的总结
Zygote是创建Android系统中Java世界的盘古,它创建了第一个Java虚拟机,同时它又是女娲,它成功地繁殖了framework的核心system_server进程。做为Java语言的受益者,我们理应回顾一下Zygote创建Java世界的步骤:
· 第一天:创建AppRuntime对象,并调用它的start。此后的活动则由AppRuntime来控制。
· 第二天:调用startVm创建Java虚拟机,然后调用startReg来注册JNI函数。
· 第三天:通过JNI调用com.android.internal.os.ZygoteInit类的main函数,从此进入了Java世界。然而在这个世界刚开创的时候,什么东西都没有。
· 第四天:调用registerZygoteSocket。通过这个函数,它可以响应子孙后代的请求。同时Zygote调用preloadClasses和preloadResources,为Java世界添砖加瓦。
· 第五天:Zygote觉得自己工作压力太大,便通过调用startSystemServer分裂一个子进程system_server来为Java世界服务。
· 第六天:Zygote完成了Java世界的初创工作,它已经很满足了。下一步该做的就是调用runSelectLoopMode后,便沉沉地睡去了。
· 以后的日子:Zygote随时守护在我们的周围,当接收到子孙后代的请求时,它会随时醒来,为它们工作。
如果支持中文编码的话,我一定要为Zygote取名为盘古_女娲。

4.4 Zygote的分裂
前文已经讲道,Zygote分裂出嫡长子system_server后,就通过runSelectLoopMode等待并处理来自客户的消息,那么,谁会向Zygote发送消息呢?这里,以一个Activity的启动为例,具体分析Zygote是如何分裂和繁殖的。
4.4.1 ActivityManagerService发送请求
ActivityManagerService也是由SystemServer创建的。假设通过startActivit来启动一个新的Activity,这个Activity附属于一个还未启动的进程,那么这个进程该如何启动呢?先来看看ActivityManagerService中的startProcessLocked函数。函数中调用了android.os.Process类的start方法,码在Process.java中。
注意,这里start的参数processClass的值是"android.app.ActivityThread",其中调用startViaZygote方法。该方法做了一些参数处理,最后调用zygoteSendArgsAndGetPid函数。其中调用了下面的方法
argsForZygote.add("–runtime-init");//这个参数很重要。
zygoteSendArgsAndGetPid函数调用openZygoteSocketIfNeeded方法打开了和Zygote通信的Socket,并在try块中把请求的参数发到Zygote,然后读取Zygote处理完的结果,便得知是某个进程的pid!

从上面的文章可以看出ActivityManagerService终于向Zygote发送请求了。请求的参数中有一个字符串,它的值是“android.app.ActivityThread”。
注意:由于ActivityManagerService驻留于SystemServer进程中,所以正是SS向Zygote发送了消息。

4.4.2 有求必应之响应请求(接下来分析Zygote处理请求这里回到ZygoteInit.java)
分析runSelectLoopMode()可知每当有请求数据发来时,Zygote都会调用ZygoteConnection的runOnce函数。ZygoteConnection代码在ZygoteConnection.java。runOnce方法中Zygote又分裂了一个子进程,然后在调用handleChildProc方法在子进程中进行处理。handleChildProc会根据传入的参数设置新进程的一些属性,由于SS发来的参数中有“–runtime-init“,所以parsedArgs.runtimeInit为true。故条件成立,调用RuntimeInit.zygoteInit方法,在该方法中重定向了标准输出和错误输出、调用了native函数zygoteInitNative()最终会调用AppRuntime的onZygoteInit,在onZygoteInit中建立了和Binder的关系。RuntimeInit.zygoteInit方法最终还是调用invokeStaticMain函数。

从上面一系列的函数调用可知:Zygote分裂子进程后,自己将在handleParentProc中做一些扫尾工作,然后继续等待请求进行下一次分裂。
这个android.app.ActivityThread类,实际上是Android中apk程序所对应的进程,它的main函数就是apk程序的main函数。从这个类的命名(android.app)中也可以看出些端倪。
通过这一节的分析,读者可以想到,Android系统运行的那些apk程序,其父都是zygote。这一点,可以通过adb shell登录后,用ps命令查看进程和父进程号来确认。
4.4.3 关于 Zygote分裂的总结(Zygote的分裂由SS控制)
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值