ZygoteInit.java" />
当前位置: 代码迷 >> Android >> ZygoteInit.java
  详细解决方案

ZygoteInit.java

热度:53   发布时间:2016-04-28 05:38:26.0
android启动之zygote启动
上一博文介绍了init进程启动,在解析init.rc 的时候会把zygote加到service列表中,并最终启动,zygote启动的实际是app_process程序。zygote是init进程的子进程。在Android系统中,所有的应用程序以及系统服务,包括SystemServer都是由Zygote fork出来的,这就是为什么它叫zygote(受精卵)的原因。我们再来看一下.rc文件的描述:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server      class main      socket zygote stream 660 root system      onrestart write /sys/android_power/request_state wake      onrestart write /sys/power/state on      onrestart restart media      onrestart restart netd  
服务名称为:                           zygote 
启动该服务执行的命令:                 /system/bin/app_process 
命令的参数:                           -Xzygote /system/bin --zygote --start-system-server 
socket zygote stream 660: 创建一个名为:/dev/socket/zygote 的 socket ,类型为:stream

app_main.cpp

Zygote的main函数在\frameworks\base\cmds\app_process\app_main.cpp,如下:
int main(int argc, const char* const argv[]){    ...    int i = runtime.addVmArguments(argc, argv);    //返回的i一般会是1    while (i < argc) {        const char* arg = argv[i++];        if (!parentDir) {	    //取值应该是/system/bin            parentDir = arg;        } else if (strcmp(arg, "--zygote") == 0) {            zygote = true;            niceName = "zygote";        } else if (strcmp(arg, "--start-system-server") == 0) {            startSystemServer = true;        } else if (strcmp(arg, "--application") == 0) {            application = true;        } else if (strncmp(arg, "--nice-name=", 12) == 0) {            niceName = arg + 12;        } else {            className = arg;            break;        }    }    if (niceName && *niceName) {        setArgv0(argv0, niceName);        //把app_process的进程名改为zygote        set_process_name(niceName);    }    runtime.mParentDir = parentDir;    if (zygote) {        //核心代码        runtime.start("com.android.internal.os.ZygoteInit",startSystemServer ? "start-system-server" : "");    } else if (className) {        // Remainder of args get passed to startup class main()        runtime.mClassName = className;        runtime.mArgC = argc - i;        runtime.mArgV = argv + i;        runtime.start("com.android.internal.os.RuntimeInit",                application ? "application" : "tool");    } else {        fprintf(stderr, "Error: no class name or --zygote supplied.\n");        app_usage();        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");        return 10;    }}
如果运行正常的话,会走到runtime.start("com.android.internal.os.ZygoteInit",startSystemServer ? "start-system-server" : ""),如下:
void AndroidRuntime::start(const char* className, const char* options){    ALOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",            className != NULL ? className : "(unknown)");    //创建一个信号管道    blockSigpipe();    ...    //启动虚拟机    JNIEnv* env;    if (startVm(&mJavaVM, &env) != 0) {        return;    }    //空函数    onVmCreated(env);    //注册android的native函数(JNI的接口),注册了一个用于创建线程的函数(javaCreateThreadEtc)    if (startReg(env) < 0) {        ALOGE("Unable to register all android natives\n");        return;    }    //创建java.lang.String数组,其内容值是:[ “com.android.internal.os.ZygoteInit”,“true”]    jclass stringClass;    jobjectArray strArray;    jstring classNameStr;    jstring optionsStr;    stringClass = env->FindClass("java/lang/String");    assert(stringClass != NULL);    strArray = env->NewObjectArray(2, stringClass, NULL);    assert(strArray != NULL);    classNameStr = env->NewStringUTF(className);    assert(classNameStr != NULL);    env->SetObjectArrayElement(strArray, 0, classNameStr);    optionsStr = env->NewStringUTF(options);    env->SetObjectArrayElement(strArray, 1, optionsStr);        /*     * Start VM.  This thread becomes the main thread of the VM, and will     * not return until the VM exits.     */    char* slashClassName = toSlashClassName(className);    //查找com.android.internal.os.ZygoteInit类    jclass startClass = env->FindClass(slashClassName);    if (startClass == NULL) {        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);        /* keep going */    } else {	//查找ZygoteInit类的main函数        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",            "([Ljava/lang/String;)V");        if (startMeth == NULL) {            ALOGE("JavaVM unable to find main() in '%s'\n", className);            /* keep going */        } else {	    //调用ZygoteInit类的main函数,参数是strArray。虚拟机执行的第一个Java类就是ZygoteInit.java            env->CallStaticVoidMethod(startClass, startMeth, strArray);#if 0            if (env->ExceptionCheck())                threadExitUncaughtException(env);#endif        }    }    free(slashClassName);    ALOGD("Shutting down VM\n");    if (mJavaVM->DetachCurrentThread() != JNI_OK)        ALOGW("Warning: unable to detach main thread\n");    if (mJavaVM->DestroyJavaVM() != 0)        ALOGW("Warning: VM did not shut down cleanly\n");}

ZygoteInit.java

虚拟机执行的第一个Java类就是ZygoteInit.java,它的main函数如下:
public static void main(String argv[]) {        try {            // Start profiling the zygote initialization.            SamplingProfilerIntegration.start();	    //创建了一个socket接口,用来和ActivityManagerService通讯            registerZygoteSocket();	    //装载Framework大部分类及资源,并和子进程共享            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,SystemClock.uptimeMillis());            preload();            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis());            // Finish profiling the zygote initialization.            SamplingProfilerIntegration.writeZygoteSnapshot();            // Do an initial gc to clean up after startup            gc();            // If requested, start system server directly from Zygote            if (argv.length != 2) {                throw new RuntimeException(argv[0] + USAGE_STRING);            }	    //启动SystemServer,此处会启动一个新的进程            if (argv[1].equals("start-system-server")) {                startSystemServer();            } else if (!argv[1].equals("")) {                throw new RuntimeException(argv[0] + USAGE_STRING);            }            Log.i(TAG, "Accepting command socket connections");            if (ZYGOTE_FORK_MODE) {                runForkMode();            } else {		//无限循环,在前面创建的socket接口上等待ActivityManagerService请求创建新的应用程序进程                runSelectLoopMode();            }            closeServerSocket();        } catch (MethodAndArgsCaller caller) {            caller.run();        } catch (RuntimeException ex) {            Log.e(TAG, "Zygote died with exception", ex);            closeServerSocket();            throw ex;        }    }

创建socket接口

 private static void registerZygoteSocket() {        if (sServerSocket == null) {            int fileDesc;            try {		//获取socket描述符                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 {                //在Linux系统中,所有的系统资源都可以看成是文件,甚至包括内存和CPU,因此,像标准的磁盘文件或者网络Socket自然也被认为是文件,这就是为什么LocalServerSocket构造函数的参数是一个文件描述符。                sServerSocket = new LocalServerSocket(                        createFileDescriptor(fileDesc));            } catch (IOException ex) {                throw new RuntimeException(                        "Error binding to local socket '" + fileDesc + "'", ex);            }        }    }

Socket编程中有两种方式去触发Socket数据读操作。一种是使用listen()监听某个端口,然后调用read()去从这个端口上读数据,这种方式被称为阻塞式读操作,因为当端口没有数据时,read()函数将一直等待,直到数据准备好后才返回;另一种是使用select()函数将需要监测的文件描述符作为select()函数的参数,然后当该文件描述符上出现新的数据后,自动触发一个中断,然后在中断处理函数中再去读指定文件描述符上的数据,这种方式被称为非阻塞式读操作。LocalServerSocket中使用的正是后者,即非阻塞读操作。

预加载类和资源

在Android源码编译的时候,会最终把preload-classes文件打包到framework.jar中。ZygoteInit中通过调用preloadClasses()完成装载这些类。装载的方法很简单,就是读取preload-classes列表中的每一行,因为每一行代表了一个具体的类,然后调用Class.forName()装载目标类。

preloadResources()函数中分别调用preloadDrawables()和preloadColorStateLists()加载两类资源。加载的原理很简单,就是把这些资源读出来放到一个全局变量中,只要该类对象不被销毁,这些全局变量就会一直保存。保存Drawable资源的全局变量是mResources,该变量的类型是Resources类,由于该类内部会保存一个Drawable资源列表,因此,实际上缓存这些Drawable资源是在Resources内部;保存Color资源的全局变量也是mResources,同样,Resources类内部也有一个Color资源的列表。

  static void preload() {        preloadClasses();        preloadResources();    }

循环等待服务

private static void runSelectLoopMode() throws MethodAndArgsCaller {        //首先将sServerSocket加入到被监测的文件描述符列表中        ArrayList<FileDescriptor> fds = new ArrayList();        ArrayList<ZygoteConnection> peers = new ArrayList();        FileDescriptor[] fdArray = new FileDescriptor[4];        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--;            }	    //selectReadable()函数的返回值有三种。一种是-1,代表着内部错误;第二种是0,代表着没有可处理的连接,因此会以Socket服务端口重新建立一个ZygoteConnection对象,并等待客户端的请求;第三种是大于0,	代表着还有没处理完的连接请求,因此需要先处理该请求,而暂时不需要建立新的连接等待。            try {                fdArray = fds.toArray(fdArray);                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) {                //接受Android应用的socket命令                ZygoteConnection newPeer = acceptCommandPeer();                peers.add(newPeer);                fds.add(newPeer.getFileDesciptor());            } else {                boolean done;	        //基于zygote进程孵化出新的应用进程                done = peers.get(index).runOnce();                if (done) {                    peers.remove(index);                    fds.remove(index);                }            }        }    }

fork创建进程

fork是Linux系统的一个系统调用,其作用是复制当前进程,产生一个新的进程。新进程将拥有和原始进程完全相同的进程信息,除了进程id不同。进程信息包括该进程所打开的文件描述符列表、所分配的内存等。当新进程被创建后,两个进程将共享已经分配的内存空间,直到其中一个需要向内存中写入数据时,操作系统才负责复制一份目标地址空间,并将要写的数据写入到新的地址中,这就是所谓的copy-on-write机制,即“仅当写的时候才复制”,这种机制可以最大限度地在多个进程中共享物理内存。这就是我们会先预加载framework的类和资源的原因,这样子进程就可以与zygote共享framework资源了。在操作系统内部,启动新的进程包含三个过程。
第一个过程,内核创建一个进程数据结构,用于表示将要启动的进程。
第二个过程,内核调用程序装载器函数,从指定的程序文件读取程序代码,并将这些程序代码装载到预先设定的内存地址。
第三个过程,装载完毕后,内核将程序指针指向到目标程序地址的入口处开始执行指定的进程。当然,实际的过程会考虑更多的细节,不过大致思路就是这么简单。
由于fork()函数是Linux的系统调用,Android中的Java层仅仅是对该调用进行了JNI封装而已。ZygoteConnection
类的runOnce()函数就是通过JNI调用fork()函数创建子进程的。

fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
    1)在父进程中,fork返回新创建子进程的进程ID;
    2)在子进程中,fork返回0;
    3)如果出现错误,fork返回一个负值;

zygote已经创建了一个Socket服务端,而这个服务端是不应该被新进程使用的,否则系统中会有多个进程接收Socket客户端的命令。因此,新进程被创建好后,首先需要在新进程中关闭该Socket服务端,并调用新进程中指定的Class文件的main()函数作为新进程的入口点。而这些正是在调用forkAndSpecialize()函数后根据返回值pid完成的。



  相关解决方案