zygote的分裂
1 zygote的分裂
前面已经讲了zygote分裂出了长子system_server,然后SS(system_server)就通过initAndLoop进行消息循环处理了。那么谁会向zygote发送消息呢?这里以一个activity的启动为例,进行具体分析zygote的分裂和繁殖。
1.1 ActivityManagerService发送请求
ActivityManagerService也是SS创建的(在ServerThread里面)。假设通过startActivity来启动一个新的activity,而这个activity附属于一个还未启动的进程,那么这个进程该如何启动呢?先看看ActivityManagerService中的startProcessLocked函数:
这个函数很复杂,主要是判断启动activity的进程是否孤立进程,是否死亡等,并做好进程记录,放入processRecode类中~,核心部分在最后的函数调用startProcessLocked中。 startProcessLocked(ProcessRecord app,String hostingType, String hostingNameStr)的代码也比较多,这里就不详细列举了,只列出它完成的功能: ①设置pid并更新cpu状态。 ②如果该进程不是孤立进程的话就通过PackageManager获取该进程对应的uid所对应的gid;否者直接跳到第四步。 ③增加共享APP的 gid,这样的话,这些共享APP就可以想访问so库一样访问一些共享资源了。 ④再设置该进程的一些属性——是否启用checkjni,安全模式之类的。 ⑤然后调用Process.start("android.app.ActivityThread"……)来启动进程,这个函数要么正确执行并返回包含有新进程pid的结果,要么就失败抛出Runtime异常。这个process类是android提供的,并非jdk中的process类。 ⑥最后就是一些不太重要的扫尾工作。 |
接下来看android.os.Process的start函数,详细代码在Process.java中:
public static final ProcessStartResult start(final String processClass, final String niceName,int uid, int gid, int[] gids,int debugFlags, int mountExternal, int targetSdkVersion,String seInfo,String[] zygoteArgs) { try { //调用startViaZygote函数 return startViaZygote(processClass, niceName, uid, gid, gids, debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); throw new RuntimeException( "Starting VM process through Zygote failed", ex); } } startViaZygote函数代码如下: private static ProcessStartResult startViaZygote(final String processClass, final String niceName, final int uid, final int gid, final int[] gids, int debugFlags, int mountExternal, int targetSdkVersion, String seInfo, String[] extraArgs) throws ZygoteStartFailedEx { synchronized(Process.class) { ArrayList<String> argsForZygote = new ArrayList<String>(); // --runtime-init, --setuid=, --setgid=, // and --setgroups= must go first argsForZygote.add("--runtime-init"); //这个参数很重要 ……一些参数处理 //最后调用zygoteSendArgsAndGetResult return zygoteSendArgsAndGetResult(argsForZygote); } } |
zygoteSendArgsAndGetResult函数主要功能:向zygote进程发送一个参数列表,zygote进程会启动一个新的子进程并返回该子进程的pid号。详细代码如下:
private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList<String> args) throws ZygoteStartFailedEx { //★这是关键函数!打开了同zygote进程的socket通信。 openZygoteSocketIfNeeded(); try { /** * See com.android.internal.os.ZygoteInit.readArgumentList() * Presently the wire format to the zygote process is: * a) a count of arguments (argc, in essence) * b) a number of newline-separated argument strings equal to count * * After the zygote process reads these it will write the pid of * the child or -1 on failure, followed by boolean to * indicate whether a wrapper process was used. */ sZygoteWriter.write(Integer.toString(args.size())); sZygoteWriter.newLine(); int sz = args.size(); for (int i = 0; i < sz; i++) { String arg = args.get(i); if (arg.indexOf('\n') >= 0) { throw new ZygoteStartFailedEx( "embedded newlines not allowed"); } sZygoteWriter.write(arg); sZygoteWriter.newLine(); } //读取zygote处理完的结果,便可得到zygote返回的子进程的pid sZygoteWriter.flush(); // Should there be a timeout on this? ProcessStartResult result = new ProcessStartResult(); result.pid = sZygoteInputStream.readInt(); if (result.pid < 0) { throw new ZygoteStartFailedEx("fork() failed"); } result.usingWrapper = sZygoteInputStream.readBoolean(); return result; } catch (IOException ex) { try { if (sZygoteSocket != null) { sZygoteSocket.close(); } } catch (IOException ex2) { // we're going to fail anyway Log.e(LOG_TAG,"I/O exception on routine close", ex2); } sZygoteSocket = null; throw new ZygoteStartFailedEx(ex); } } |
下面对openZygoteSocketIfNeeded函数进行详细分析:
/** * Tries to open socket to Zygote process if not already open. If * already open, does nothing. May block and retry. */ /*此函数尝试打开同zygote进程的socket通信*/ private static void openZygoteSocketIfNeeded() throws ZygoteStartFailedEx { int retryCount; if (sPreviousZygoteOpenFailed) { /*如果上一次打开失败的话,就估计这一次打开也会失败,就直接返回失败状态*/ retryCount = 0; } else { retryCount = 10; //允许尝试10次 } /* 在bug#811181:某些时候runtime可能会使得本函数在zygote准备好之前就被调用,那么就肯定会失败的~这里系统暂时不做处理~~ */ for (int retry = 0 ; (sZygoteSocket == null) && (retry < (retryCount + 1)) ; retry++ ) { if (retry > 0) { try { Log.i("Zygote", "Zygote not up yet, sleeping..."); Thread.sleep(ZYGOTE_RETRY_MILLIS); //sleep 500毫秒 } catch (InterruptedException ex) { // should never happen } } try { //创建同zygote的socket链接! sZygoteSocket = new LocalSocket(); sZygoteSocket.connect(new LocalSocketAddress(ZYGOTE_SOCKET, LocalSocketAddress.Namespace.RESERVED)); sZygoteInputStream=new DataInputStream(sZygoteSocket.getInputStream()); sZygoteWriter = new BufferedWriter( new OutputStreamWriter( sZygoteSocket.getOutputStream()), 256); Log.i("Zygote", "Process: zygote socket opened"); sPreviousZygoteOpenFailed = false; break; } catch (IOException ex) { if (sZygoteSocket != null) { try { sZygoteSocket.close(); } catch (IOException ex2) { Log.e(LOG_TAG,"I/O exception on close after exception", ex2); } } sZygoteSocket = null; } } if (sZygoteSocket == null) { sPreviousZygoteOpenFailed = true; throw new ZygoteStartFailedEx("connect failed"); } } |
好了,ActivityManagerService终于想zygote发送请求了~~。请求的参数中有一个字符串,它的值是“android.app.ActivityThread”。现在该回到zygote处理请求那块去看看了——就是runSelectLoop函数!
1.2 zygote接收并处理子进程的请求
在runSelectLoop函数中,一旦有来自客户端的连接,就调用zygoteConnection的runonce函数来处理客户端的连接请求,该函数的详细如下:
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller { String args[]; try { args = readArgumentList(); //读取SS发送过来的参数 descriptors = mSocket.getAncillaryFileDescriptors(); } ……… int pid = -1; ………. //在这里zygote又分裂出了一个子进程 pid=Zygote.forkAndSpecialize(parsedArgs.uid,parsedArgs.gid,parsedArgs.gids, parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, parsedArgs.niceName, fdsToClose); } …… try { if (pid == 0) { // 子进程处理函数 handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr); return true; } else { // in parent...pid of < 0 means failure IoUtils.closeQuietly(childPipeFd); childPipeFd = null; return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs); } } } |
接下来看看子进程处理函数handleChildProc干了些什么。具体的代码就不贴了,这里直接用文字加以描述:
①首先关闭zygote的sockets;
②然后根据传入的参数设置新进程的一些属性,因为我们传递进来的参数中有“runtime-init”所以会调用RuntimeInit.ZygoteInit函数,这个函数的功能我们已经分析过了,主要就是调用子进程类的main函数,这里就是调用android.app.ActivityThread类的main函数。实际上Android中APK程序所对应的进程就是这个类,它的main函数就是apk程序的main函数!!
③zygote子进程分裂成功后,就做一些扫尾工作,然后返回到runSelectLoop函数中继续等待请求进行下一次分裂。
所以说zygote是所有apk进程的祖先!
1.3 总结
这里以启动一个activity进程为例:
①ActivityManagerServer进程向zygote进程发送消息;
②zygote进程在runSelectLoop函数中接收到这个消息,fork创建子进程;
③子进程调用RuntimeInit.ZygoteInit函数,这个函数会调用子进程类的main函数,即android.app.ActivityThread的main函数,这样就启动了一个acivity进程。
zygote的分裂的更多相关文章
- Zygote进程【2】——Zygote的分裂
在Zygote的诞生一文中init进程是如何一步步创建Zygote进程的,也了解了Zygote的进程的作用.Zygote进程的诞生对于整个Java世界可以说有着"开天辟地"的作用, ...
- Android源码阅读 – Zygote
@Dlive 本文档: 使用的Android源码版本为:Android-4.4.3_r1 kitkat (源码下载: http://source.android.com/source/index.ht ...
- Zygote进程【3】——SystemServer的诞生
在ZygoteInit的main()方法中做了几件大事,其中一件便是启动Systemserver进程,代码如下: @/frameworks/base/core/Java/com/Android/int ...
- Zygote过程【3】——SystemServer诞生
欢迎转载.转载请注明:http://blog.csdn.net/zhgxhuaa 在ZygoteInit的main()方法中做了几件大事.当中一件便是启动Systemserver进程.代码例如以下: ...
- 认识Zygote
概述 在java中不同的虚拟机实例会为不同的应用分配不同内存,为了使Android系统尽快启动,使用了Zygote来预加载核心类库和一些加载时间长的类(超过1250ms),让Dalvik虚拟机共享代码 ...
- [深入理解Android卷一全文-第四章]深入理解zygote
由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该由于纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的所有内容. ...
- Zygote启动及其作用
目录 1.Zygote简介 2.Zygote进程如何启动 2.1 init.zygote64_32.rc文件 2.2 查看ps信息 2.3 启动 3.Zygote作用 3.1 启动system_ser ...
- Android系统启动分析(Init->Zygote->SystemServer->Home activity)
整个Android系统的启动分为Linux Kernel的启动和Android系统的启动.Linux Kernel启动起来后,然后运行第一个用户程序,在Android中就是init程序. ------ ...
- Android开发之漫漫长途 XI——从I到X的小结
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
随机推荐
- JavaScript 获取对象中第一个属性
使用 Object.keys(object) 可以取出属性名为数组,但会打乱顺序 严格意义上对象中是只有映射关系而没有顺序的,但是在存储结构里是有顺序的,如果想获取存储结构里的第一个属性可以使用for ...
- 分类回归树(CART)
概要 本部分介绍 CART,是一种非常重要的机器学习算法. 基本原理 CART 全称为 Classification And Regression Trees,即分类回归树.顾名思义,该算法既 ...
- 树莓派 - 修改pi账号密码,开启root账号
1.修改PI账号的密码 password pi 2.开启root账号 树莓派使用的Linux是debian系统,所以树莓派启用root和debian是相同的. debian里root账户默认没有密码, ...
- 爬虫遇到HTTP Error 403的问题
# coding=gbk from bs4 import BeautifulSoup import requests import urllib x = 1 y = 1 def crawl(url): ...
- MySql数据库中where的使用
SELECT * from runoob_tbl WHERE runoob_author='菜鸟教程'; MySQL 的 WHERE 子句的字符串比较是不区分大小写的. 你可以使用 BINARY 关键 ...
- Python——字典dict()详解
一.字典 字典是Python提供的一种数据类型,用于存放有映射关系的数据,字典相当于两组数据,其中一组是key,是关键数据(程序对字典的操作都是基于key),另一组数据是value,可以通过key来进 ...
- 四种UNIX实现
四种UNIX实现:FreeBSD 5.2.1 Linux 2.4.22 mac OS X 10.3 Solaris 9 ubuntu 属于哪一种呢?
- Framework的简单创建
如果你想将你开发的控件与别人分享,一种方法是直接提供源代码文件.然而,这种方法并不是很优雅.它会暴露所有的实现细节,而这些实现你可能并不想开源出来.此外,开发者也可能并不想看到你的所有代码,因为他们可 ...
- 51nod——2502最多分成多少块
数据范围好小... 题目中没说要升序降序,不过样例解释里可以看出是要升序. #include <bits/stdc++.h> using namespace std; ],b[],visi ...
- 【AC自动机】bzoj4327: JSOI2012 玄武密码
题目思路没话讲:主要是做题时候的细节和经验 Description 在美丽的玄武湖畔,鸡鸣寺边,鸡笼山前,有一块富饶而秀美的土地,人们唤作进香河.相传一日,一缕紫气从天而至,只一瞬间便消失在了进香河中 ...