Android启动过程详解(3)——Zygote

来源:互联网 发布:龙献文天下数据 编辑:程序博客网 时间:2024/05/29 10:54

由于Android系统是基于Linux的,所以在Android系统存在两个不一样的空间,Android空间(Java空间)以及Native空间。系统启动的时候当然是Native空间,所以必须有一个进程来打开Android空间。同时,尽管Android的设计者在淡化进程的概念,强化Activity,Service, BroadcastReceiver等等组件的概念,但从Linux的视角来看,每一个应用都是寄生在一个进程上的,那么创建进程也同样需要从Native空间去创建。在Android世界中Zygote就担任了这个角色,所以所有应用程序进程的父进程都是Zygote。Zygote的意思是受精卵,所以从名字上就能看出来它的作用。今天就来讨论一下Zygote的作用。

1.Zygote的启动

在上一篇博客介绍init进程启动的时候讲到了init.rc文件的解析,其中就有关于Zygote的启动:

#zygote serviceservice zygote /system/bin/app_process -Xzygote/system/bin –zygote \ --start-system-server    socketzygote stream 666  #socket关键字表示OPTION   onrestart write /sys/android_power/request_state wake #onrestart也是OPTION   onrestart write /sys/power/state on   onrestart restart media

从中可以看到zygote的启动是通过/system/bin/app_process进程启动的。所以zygote最初的名字叫“app_process”,这个名字是在Android.mk文件中被指定的,但app_process在运行过程中,通过Linux下的pctrl系统调用将自己的名字换成了“zygote”,所以我们通过ps命令看到的进程名是“zygote”。接下来看看app_process的启动:

[-->App_main.cpp]int main(int argc, const char* const argv[]){  /*   Zygote进程由init通过fork而来,我们回顾一下init.rc中设置的启动参数:    -Xzygote/system/bin --zygote --start-system-server  */    mArgC= argc;    mArgV= argv;   mArgLen = 0;    for(int i=0; i<argc; i++) {       mArgLen += strlen(argv[i]) + 1;    }   mArgLen--;   AppRuntime runtime;    // 调用Appruntime的addVmArguments,这个函数很简单,读者可以自行分析    int i= runtime.addVmArguments(argc, argv);    if (i< argc) {       //设置runtime的mParentDir为/system/bin       runtime.mParentDir = argv[i++];    }    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);        }        ......    }         ......}

在这个函数中一共做了两件重要的事:
- 创建Android运行时环境,AppRuntime
- 启动SystemServer
SystemServer是管理Android所有服务的部件,这一部分放到下一篇博客讲,重点来讲一下AppRumtime的创建。
这里写图片描述
从上面这幅图可以看到,AppRuntime继承自AndroidRumtime,所以在app_process进程中调用的runtime.start其实是调用的AndroidRumtime的start函数:

void AndroidRuntime::start(const char*className, const bool startSystemServer){    //className的值是"com.android.internal.os.ZygoteInit"    //startSystemServer的值是true    char*slashClassName = NULL;    char*cp;   JNIEnv* env;    blockSigpipe();//处理SIGPIPE信号     ......    constchar* rootDir = getenv("ANDROID_ROOT");if (rootDir == NULL) {//如果环境变量中没有ANDROID_ROOT,则新增该变量,并设置值为“/system"       rootDir = “/system";        ......       setenv("ANDROID_ROOT", rootDir, 1);    }    //① 创建虚拟机    if(startVm(&mJavaVM, &env) != 0)        goto bail;     //②注册JNI函数    if(startReg(env) < 0) {        goto bail;    }   jclass stringClass;   jobjectArray strArray;   jstring classNameStr;   jstring startSystemServerStr;   stringClass = env->FindClass("java/lang/String");    //创建一个有两个元素的String数组,即Java代码 String strArray[] = new String[2]   strArray = env->NewObjectArray(2, stringClass, NULL);  classNameStr = env->NewStringUTF(className);   //设置第一个元素为"com.android.internal.os.ZygoteInit"   env->SetObjectArrayElement(strArray, 0, classNameStr);   startSystemServerStr = env->NewStringUTF(startSystemServer ?                                                "true" : "false");   //设置第二个元素为"true",注意这两个元素都是String类型,即字符串。   env->SetObjectArrayElement(strArray, 1, startSystemServerStr);   jclass startClass;   jmethodID startMeth;   slashClassName = strdup(className);   /*     将字符串“com.android.internal.os.ZygoteInit”中的“. ”换成“/”,     这样就变成了“com/android/internal/os/ZygoteInit”,这个名字符合JNI规范,     我们可将其简称为ZygoteInit类。   */    for(cp = slashClassName; *cp != '\0'; cp++)        if(*cp == '.')           *cp = '/';   startClass = env->FindClass(slashClassName);    ......    //找到ZygoteInit类的static main函数的jMethodId。   startMeth = env->GetStaticMethodID(startClass, "main",                                             "([Ljava/lang/String;)V");     ......     /*        ③通过JNI调用Java函数,注意调用的函数是main,所属的类是          com.android.internal.os.ZygoteInit,传递的参数是          “com.android.internal.os.ZygoteInit true”,          调用ZygoteInit的main函数后,Zygote便进入了Java世界!          也就是说,Zygote是开创Android系统中Java世界的盘古。     */      env->CallStaticVoidMethod(startClass,startMeth, strArray);    //Zygote退出,在正常情况下,Zygote不需要退出。    if(mJavaVM->DetachCurrentThread() != JNI_OK)       LOGW("Warning: unable to detach main thread\n");    if(mJavaVM->DestroyJavaVM() != 0)       LOGW("Warning: VM did not shut down cleanly\n");bail:   free(slashClassName);}

上面这段代码主要做了三件事:
- startVM:注册Java虚拟机,并设置虚拟机相关参数
- startReg:注册JNI函数,因为后续Java世界用到的一些函数是采用native方式来实现的,所以才必须提前注册这些函数。
- 调用ZygoteInit.main正式进入Java世界

接下来重点讲一下第三点。首先来看看源码:

[-->ZygoteInit.java]public static void main(String argv[]) {  try {       SamplingProfilerIntegration.start();       //①注册Zygote用的socket       registerZygoteSocket();       //②预加载类和资源       preloadClasses();       preloadResources();       ......       // 强制一次垃圾收集       gc();      //我们传入的参数满足if分支      if (argv[1].equals("true")) {          startSystemServer();//③启动system_server进程       }else if (!argv[1].equals("false")) {          thrownew RuntimeException(argv[0] + USAGE_STRING);        }      // 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) {          closeServerSocket();           throw ex;        }     ......    }

在这个函数中主要做了四件事:
- 创建IPC通信Socket
- 预加载类和资源
- 启动SystemServer
- 监听其他进程发出来的请求

1.1 创建IPC通信接口Socket——registerZygoteSocket

Zygote和其他进程之间的IPC并不是通过binder机制,而是通过一个localsocket来完成的,所以要首先创建一个socket:

[-->ZygoteInit.java]private static void registerZygoteSocket() {    if(sServerSocket == null) {        intfileDesc;        try{           //从环境变量中获取Socket的fd,还记得第3章init中介绍的zygote是如何启动的吗?//这个环境变量由execv传入。          String env = System.getenv(ANDROID_SOCKET_ENV);          fileDesc = Integer.parseInt(env);       }       try{         //创建服务端Socket,这个Socket将listen并accept Client         sServerSocket= new LocalServerSocket(createFileDescriptor(fileDesc));       }       }}

1.2 预加载类和资源

这部分就不细讲了,逻辑比较简单,就是提前加载好一些运行时经常会用到的类和资源,提升系统运行的效率,但这同时会导致系统在启动的时候时间过长,所以这是一个取舍的问题。

1.3 启动SystemServer——startSystemServer

这是Zygote非常重要的一个功能,因为Android启动的所有Service(ActivityManagerService,WindowManagerService,PackageManagerService)都是通过SystemServer来启动的。

private static boolean startSystemServer()           throws MethodAndArgsCaller, RuntimeException {        //设置参数       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);           int debugFlags = parsedArgs.debugFlags;          //fork一个子进程,看来,这个子进程就是system_server进程。           pid = Zygote.forkSystemServer(                    parsedArgs.uid,parsedArgs.gid,                    parsedArgs.gids,debugFlags, null);        }catch (IllegalArgumentException ex) {           throw new RuntimeException(ex);        }        if(pid == 0) {         //① system_server进程的工作           handleSystemServerProcess(parsedArgs);        }       //zygote返回true       return true;    }

逻辑还是比较简单的,就是通过fork创建出SystemServer进程,具体SystemServer启动如何工作下一篇博客会详细介绍。

1.4 监听其他进程发出来的请求——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];      //sServerSocket是我们先前在registerZygoteSocket建立的Socket       fds.add(sServerSocket.getFileDescriptor());       peers.add(null);       int loopCount = GC_LOOP_COUNT;       while (true) {           int index;             try {               fdArray = fds.toArray(fdArray);        /*          selectReadable内部调用select,使用多路复用I/O模型。          当有客户端连接或有数据时,则selectReadable就会返回。        */              index = selectReadable(fdArray);           }          else if (index == 0) {             //如有一个客户端连接上,请注意客户端在Zygote的代表是ZygoteConnection               ZygoteConnection newPeer = acceptCommandPeer();               peers.add(newPeer);               fds.add(newPeer.getFileDesciptor());           } else {               boolean done;              //客户端发送了请求,peers.get返回的是ZygoteConnection             //后续处理将交给ZygoteConnection的runOnce函数完成。               done = peers.get(index).runOnce();        }    }

runSelectLoopMode比较简单,就是:

  • 处理客户连接和客户请求。其中客户在Zygote中用ZygoteConnection对象来表示;
  • 客户的请求由ZygoteConnection的runOnce来处理。

2.处理请求

Zygote在启动过程中的最后一步是进入runSelectLoopMode来监听socket,接收请求。比如当ActivityManagerService在接收到一个打开Activity的请求,并发现该Activity所属的应用并没有启动时就会创建一个进程,具体的过程可以参考我的另一篇博客:Android应用框架之应用启动过程

0 0
原创粉丝点击