首页 > 移动平台 > 详细

android启动之zygote启动

时间:2014-05-15 06:11:19      阅读:481      评论:0      收藏:0      [点我收藏+]
上一博文介绍了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完成的。



android启动之zygote启动,布布扣,bubuko.com

android启动之zygote启动

原文:http://blog.csdn.net/w2865673691/article/details/25718329

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!