深入理解zygote——2(代码源于GooGle)
2017-05-16 15:24
453 查看
深入理解zygote–2
1、welcome to Java World
这个Java世界的从入口在哪里?根据前面的分析,CallStaticVoidMethod最终将调用com.android.internal.os.ZygoteInit的main函数,下面就来就看看这个入口函数,代码如下所示:Zygoteinit.java :
public static void main(String argv[]) { try { // Start profiling the zygote initialization. SamplingProfilerIntegration.start(); //注册zygote用的socket registerZygoteSocket(); //预加载类和资源 EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); preloadClasses(); //cacheRegisterMaps(); preloadResources(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis()); if (SamplingProfilerIntegration.isEnabled()) { SamplingProfiler sp = SamplingProfiler.getInstance(); sp.pause(); SamplingProfilerIntegration.writeZygoteSnapshot(); sp.shutDown(); } // Do an initial gc to clean up after startup //强制一次性垃圾收集 gc(); // If requested, start system server directly from Zygote //我们传入的参数满足if分支 if (argv.length != 2) { throw new RuntimeException(argv[0] + USAGE_STRING); } if (argv[1].equals("true")) { startSystemServer();//启动system_server进程 } else if (!argv[1].equals("false")) { throw new RuntimeException(argv[0] + USAGE_STRING); } Log.i(TAG, "Accepting command socket connections"); //ZYGOTE_FORK_MODE被定义为false,所以满足else的条件 if (ZYGOTE_FORK_MODE) { runForkMode(); } else { runSelectLoopMode();//zygote调用这个函数 } closeServerSocket();//关闭socket } catch (MethodAndArgsCaller caller) { caller.run();//很重要的caller run函数,以后分析 } catch (RuntimeException ex) { Log.e(TAG, "Zygote died with exception", ex); closeServerSocket(); throw ex; } }
在ZygoteInit的main函数中,我们列举除了5大关键点:registerZygoteSocket(); preloadClasses(); startSystemServer(); runSelectLoopMode(); caller.run(),下面我们对其一一进行分析。
1.1、建立IPC通信服务端——-registerZygoteSocket
zygote 及系统中其他程序的通信没有使用binder,而是采用基于AF_UNIX类型的Socket。registerZygoteSocket的函数的使命正是建立这个socket。代码如下所示:
ZygoteInit.java :
private static void registerZygoteSocket(){} private static void registerZygoteSocket() { if (sServerSocket == null) { int fileDesc; try { //从环境中获取socket的fd,这个环境变量由execv传入 String env = System.getenv(ANDROID_SOCKET_ENV); fileDesc = Integer.parseInt(env); } catch (RuntimeException ex) { throw new RuntimeException( ANDROID_SOCKET_ENV + " unset or invalid", ex); } try { //创建服务端socket,这个socket将listen并入accept Client。 sServerSocket = new LocalServerSocket( createFileDescriptor(fileDesc)); } catch (IOException ex) { throw new RuntimeException( "Error binding to local socket '" + fileDesc + "'", ex); } } }
registerZygoteSocket 很简单,就是创建一个服务端的socket。不过我们应该想到下面两个问题:
谁是客户端。
服务端是怎么处理客户端的消息。
1.2、预加载类和资源
现在我们要分析的就是preloadClass和preloadResources函数,先来看看preloadClasses.ZygoteInit.java:
private static void preloadClasses() { final VMRuntime runtime = VMRuntime.getRuntime(); //预加载类的信息存储在PRELOADED_CLASSES变量中,他的值为“preloaded-classes” InputStream is = ZygoteInit.class.getClassLoader().getResourceAsStream( PRELOADED_CLASSES); if (is == null) { Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + "."); } else { //做一些统计和准备工作 Log.i(TAG, "Preloading classes..."); long startTime = SystemClock.uptimeMillis(); // Drop root perms while running static initializers. setEffectiveGroup(UNPRIVILEGED_GID); setEffectiveUser(UNPRIVILEGED_UID); // Alter the target heap utilization. With explicit GCs this // is not likely to have any effect. float defaultUtilization = runtime.getTargetHeapUtilization(); runtime.setTargetHeapUtilization(0.8f); // Start with a clean slate. runtime.gcSoftReferences(); runtime.runFinalizationSync(); Debug.startAllocCounting(); try { BufferedReader br = new BufferedReader(new InputStreamReader(is), 256); //读取文件的第一行,忽略#开头的注释行 int count = 0; String line; String missingClasses = null; while ((line = br.readLine()) != null) { // Skip comments and blank lines. line = line.trim(); if (line.startsWith("#") || line.equals("")) { continue; } try { //通过java反射来加载类,line中的存储是预加载的类名。 if (Config.LOGV) { Log.v(TAG, "Preloading " + line + "..."); } Class.forName(line); if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) { if (Config.LOGV) { 此处代码省略`````````` } }
preloadClass看起来是如此简单,但是你知道他有多少个类需要预先加载吗?
用coolfind工具程序在framework中搜索名为“”“preloaded-classes的文件,最后会在framework/base目录下找到,他是一个文本文件,内容如下:
preloaded-classes :
# Classes which are preloaded by com.android.internal.os.ZygoteInit. # Automatically generated by frameworks/base/tools/preload/WritePreloadedClassFile.java. # MIN_LOAD_TIME_MICROS=1250 //超时控制。 android.R$styleable android.accounts.AccountManager android.accounts.AccountManager$4 android.accounts.AccountManager$6 android.accounts.AccountManager$AmsTask android.accounts.AccountManager$BaseFutureTask android.accounts.AccountManager$Future2Task 此处代码省略``````
这个preloaded-class 一共有1267行,试想,加载这么多的类得花多长时间:
说明 preload_class 文件由framework/base/tool/preload 工具生成,他需要判断每一个类加载的时间是否大于1250微秒,超过这个时间的类就会被写到preload-classes文件中,最后由zygote预加载。这方面的内容读者可以参考有关preload工具中的内容说明。
preload_class 函数执行的时间比较长,这是导致Android系统启动慢的原因之一,可对比进行一些优化,但是是基于对系统有比较深得了解之后才能够实现的。
preloadResources和preloadClass类似,他主要是加载framework-res.apk中的资源,这里就不再介绍它了。
1.3、启动system_server
我们要分析的是第三个关键点:startSystemServer。这个函数会创建java世界中系统Service所驻留的进程system_service,该进程是framework的核心,如果他死了,就会导致zygote自杀,先来看看这个核心进程是如何启动的。zygoteInit.java :
private static void handleSystemServerProcess( ZygoteConnection.Arguments parsedArgs) throws Zy e4de goteInit.MethodAndArgsCaller { /* * First, set the capabilities if necessary */ if (parsedArgs.uid != 0) { try { setCapabilities(parsedArgs.permittedCapabilities, parsedArgs.effectiveCapabilities); } catch (IOException ex) { Log.e(TAG, "Error setting capabilities", ex); } } closeServerSocket(); /* * Pass the remaining arguments to SystemServer. * "--nice-name=system_server com.android.server.SystemServer" */ RuntimeInit.zygoteInit(parsedArgs.remainingArgs); /* should never reach here */ } /** * Prepare the arguments and fork for the system server process. */ private static boolean startSystemServer() throws MethodAndArgsCaller, RuntimeException { /* Hardcoded command line to start the system server */ //设置参数 String args[] = { "--setuid=1000", //uid,gid等设置 "--setgid=1000", "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003", "--capabilities=130104352,130104352", "--runtime-init", "--nice-name=system_server",//进程名,叫system_server "com.android.server.SystemServer",//启动的类名 }; ZygoteConnection.Arguments parsedArgs = null; int pid; try { //把上面的字符串数组转换成Arguments对象。 parsedArgs = new ZygoteConnection.Arguments(args); /* * Enable debugging of the system process if *either* the command line flags * indicate it should be debuggable or the ro.debuggable system property * is set to "1" */ int debugFlags = parsedArgs.debugFlags; if ("1".equals(SystemProperties.get("ro.debuggable"))) debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; /* Request to fork the system server process */ //fork一个子进程,看来这个进程就是system_server进程 pid = Zygote.forkSystemServer( parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, debugFlags, null); } catch (IllegalArgumentException ex) { throw new RuntimeException(ex); } /* For child process */ //下面的代码。如果pid为零,则表示处于子进程中,也就是处于system_server进程中 if (pid == 0) { //system_server进程的工作 handleSystemServerProcess(parsedArgs); } return true; }
这里出现了一个分水岭,即zygotye进行了一次无性繁殖,分裂出一个system_server
进程,(即代码中Zygote.forkSystemServer这句话),关于他的故事,我们会在后文中专门分析,这里先说zygote。
1.4、有求比应之等待请求 —–runSelectLoopMode
待zygote从startSystemServer 返回后,将进入第四个关键的函数;runSelectLoopMode.前面的地一个关键点registerZygoteSocket中注册了一个用于IPC的socket,不过那是还没有地方用到他,他的用途将在这个runselectLoopMode中体现出来,请看下面的代码:
ZygoteInit.java :
private static void runSelectLoopMode() throws MethodAndArgsCaller { ArrayList<FileDescriptor> fds = new ArrayList(); ArrayList<ZygoteConnection> peers = new ArrayList(); FileDescriptor[] fdArray = new FileDescriptor[4]; //sSserversocket是我们先前在registerZygoteSocket中建立起来的Socket。 fds.add(sServerSocket.getFileDescriptor()); peers.add(null); int loopCount = GC_LOOP_COUNT; while (true) { int index; /* * Call gc() before we block in select(). * It's work that has to be done anyway, and it's better * to avoid making every child do it. It will also * madvise() any free memory as a side-effect. * * Don't call it every time, because walking the entire * heap is a lot of overhead to free a few hundred bytes. */ if (loopCount <= 0) { gc(); loopCount = GC_LOOP_COUNT; } else { loopCount--; } try { fdArray = fds.toArray(fdArray); /* selectReadable 内部调用select,使用多路复用I/O模型,当有客户端链接或有数据时,则selectReadable就会返回。 */ index = selectReadable(fdArray); } catch (IOException ex) { throw new RuntimeException("Error in select()", ex); } if (index < 0) { throw new RuntimeException("Error in select()"); } else if (index == 0) { //有一个客户端链接上,请注意,客户端在Zygote的代表的是ZygoteConnection ZygoteConnection newPeer = acceptCommandPeer(); peers.add(newPeer); fds.add(newPeer.getFileDesciptor()); } else { boolean done; //客户端发送了一个请求,peer.get 返回的是ZygoteConnection。 //后续处理将交给ZygoteConnection的runOnce函数完成。 done = peers.get(index).runOnce(); if (done) { peers.remove(index); fds.remove(index); } } } }
runSelectLoopMode 比较简单,就是:
处理客户连接和客户请求,其中客户在zygote中用zygoteConnection对象来表示。
客户的请求由zygoteconnection的runOnce来处理。
2、关于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调用preloadClass和preloadResources,为Java世界填砖加瓦。
第五天:zygote觉得自己工作挺多,便通过调用startSystemServer分裂一个进程,system_server来为java世界服务。
第六天:zygote完成了java世界的处创工作,下一步该做的就是调用runselectloopmode后,便沉沉的睡去了。
以后zygote随时守候在我们的周围i,当接受到子孙后代的请求时,他会随时醒来,为他们工作。
文献参考:
整理抄录自 — 《深入理解Android卷1》,邓凡平著。
相关文章推荐
- 深入理解zygote——1(代码源于GooGle)
- 深入理解init_3 --------- 解析Zygote 的service(基于源码2.2,代码源自Google)
- 深入理解init_2-----解析配置文件init.rc(基于Android 2.2,代码源于Google)
- 深入理解init_5-----属性服务(基于Android 2.2,代码源自Google)
- 深入理解init_4``````````init控制service(基于Android2.2,代码源自Google)
- android启动--深入理解zygote (II)
- 深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点
- 深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点
- 深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点javascript
- uboot之relocate代码的深入理解
- 深入理解javascript学习笔记(一) 编写高质量代码
- android启动--深入理解zygote
- 深入理解 GNU GRUB - 03 diskboot.S 3.1 diskboot.S执行时的环境 & 3.2 diskboot.S代码结构
- javascript笔记:通过对作用域链和执行环境的深入理解所得出的提高javascript代码性能
- android启动--深入理解zygote
- 深入理解javascript系列(1):高质量JavaScript代码书写基本要点
- 深入理解JavaScript系列(45):代码复用模式(避免篇)
- javascript笔记:通过对作用域链和执行环境的深入理解所得出的提高javascript代码性能的建议
- 深入理解 GNU GRUB - 02 boot.S 2.2 MBR结构 2.3 boot.S代码结构
- 深入理解JavaScript系列(1) 编写高质量JavaScript代码的基本要点