Android源码分析--Zygote进程分析

众所周知,Android系统中存在着两个完全不同的世界:

1. Java世界,Google所提供的SDK就主要是针对这个世界的,在这个世界中运行的程序都是基于Dalvik虚拟机的Java程序。

2. native世界,也就是利用C或C++语言开发的程序。

那么问题来了,Android系统具体是如何将这两个世界联系起来的,这就是关系到本篇博文所讲的Zygote进程。Android是基于Linux内核构建的,这样Android最早进入的也就是native世界。Linux系统启动的第一个进程是init进程,Android系统中最早启动的进程也是init进程,接下来init进程根据init.rc文件中的配置选项创建了Zygote进程。

Zygote名字的来历

Zygote进程的名字最初时并不是“zygote”,而是“app_process”,这个名字是在Android.mk文件中指定的,但在运行过程中app_process通过Linux下pctrl系统调用把名字换成了“zygote”。其所对应的源文件是App_main.cpp。

App_main.cpp分析

(源码位置:frameworks\base\cmds\app_process\app_main.cpp)

被执行的方法是App_main.cpp中的main方法,该方法比较简单,它首先为虚拟机添加了一些参数,接下来解析了运行时的参数,如果我们的参数设定为执行Zygote进程,则会将bool值zygote置为true,接下来调用了AppRuntime中的start方法。

int main(int argc, char* const argv[])
{
    ........
    // 两个全局变量被定义在ProcessState.cpp中
    mArgC = argc;
    mArgV = argv;

    mArgLen = 0;
    for (int i=0; i<argc; i++) {
        mArgLen += strlen(argv[i]) + 1;
    }
    mArgLen--;

    AppRuntime runtime;
    const char* argv0 = argv[0];

    // Process command line arguments
    // ignore argv[0]
    argc--;
    argv++;

    // Everything up to ‘--‘ or first non ‘-‘ arg goes to the vm

    int i = runtime.addVmArguments(argc, argv);

    // 接下来解析一些运行时参数
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    const char* parentDir = NULL;
    const char* niceName = NULL;
    const char* className = NULL;
    while (i < argc) {
        const char* arg = argv[i++];
        if (!parentDir) {
            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);
        //在这里将进程的名称更换为zygote
        set_process_name(niceName);
    }

    runtime.mParentDir = parentDir;

    if (zygote) {
        // 调用AppRuntime的start方法以执行名称为ZygoteInit的类其中的main方法。
        // argv的参数为"--zygote" "--start-system-server",则startSystemServer为true
        runtime.start("com.android.internal.os.ZygoteInit", startSystemServer ? "start-system-server" : "");
    } else if (className) {

        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;
    }
}

AppRuntime中start方法分析

在App_main的main方法中我们最终进入到AppRuntime的start方法中去,接下来分析一下AppRuntime中的start方法,该类的实现和定义均在app_main.cpp中。

//传入参数为"com.android.internal.os.ZygoteInit","start-system-server"
void AndroidRuntime::start(const char* className, const char* options)
{
    ALOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
            className != NULL ? className : "(unknown)");

    /*
     * ‘startSystemServer == true‘ 我们在这里打印出启动引导事件
     */
    if (strcmp(options, "start-system-server") == 0) {
        /* track our progress through the boot sequence */
        const int LOG_BOOT_PROGRESS_START = 3000;
        LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
                       ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
    }
    //如果环境变量中没有ANDROID_ROOT则设置该环境变量
    const char* rootDir = getenv("ANDROID_ROOT");
    if (rootDir == NULL) {
        rootDir = "/system";
        if (!hasDir("/system")) {
            LOG_FATAL("No root directory specified, and /android does not exist.");
            return;
        }
        setenv("ANDROID_ROOT", rootDir, 1);
    }

    /* 开启虚拟机 */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env) != 0) {
        return;
    }
    onVmCreated(env);

    /*
     * 注册一些jni函数
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    /*
     * 接下来通过jni调用,调用ZygoteInit的main方法,并设置好传递参数
     */
    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);

    /*
     * 该线程将会成为虚拟机的主线程,并且伴随虚拟机一直存在
     */
    char* slashClassName = toSlashClassName(className);
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class ‘%s‘\n", slashClassName);
        /* keep going */
    } else {
        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 {
        //调用Java类中的静态main方法
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
        .......
    }
    free(slashClassName);
    ......
}

我们来总结一下start方法的工作:

1. 设置ANDROID_ROOT环境变量

2. 调用AndroidRuntime类中的startVM方法创建虚拟机

3. 调用startReg函数注册JNI函数

4. 通过jni调用调用Java类ZygoteInit的main方法

至此,我们已经进入了Java的世界。

进入Java世界

通过上面的分析,我们的程序已经来到了Java类ZygoteInit的静态main方法中,该类被定义在frameworks\base\core\java\com\android\internal\os\ZygoteInit.java中。接下来我们看看她的工作:

    public static void main(String argv[]) {
        try {
            // 开始配置Zygote进程的初始化工作
            SamplingProfilerIntegration.start();

            //建立IPC通信的服务端
            registerZygoteSocket();
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                SystemClock.uptimeMillis());
            //预加载类和资源
            preload();
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                SystemClock.uptimeMillis());

            // 结束配置Zygote的初始化
            SamplingProfilerIntegration.writeZygoteSnapshot();

            // 进行一次垃圾回收
            gc();

            // 禁用跟踪
            Trace.setTracingEnabled(false);

            // If requested, start system server directly from Zygote
            if (argv.length != 2) {
                throw new RuntimeException(argv[0] + USAGE_STRING);
            }

            if (argv[1].equals("start-system-server")) {
                startSystemServer();//启动system_server进程
            } else if (!argv[1].equals("")) {
                throw new RuntimeException(argv[0] + USAGE_STRING);
            }

            Log.i(TAG, "Accepting command socket connections");

            runSelectLoop();//处理客户连接和客户请求

            closeServerSocket();//关闭服务端
        } catch (MethodAndArgsCaller caller) {
            caller.run();
        } catch (RuntimeException ex) {
            Log.e(TAG, "Zygote died with exception", ex);
            closeServerSocket();
            throw ex;
        }
    }

总结一下:

1. 调用registerZygoteSocket注册IPC通信服务端的socket

2. preload预加载类和资源

3. startSystemServer启动system_server

4. runSelectLoop处理客户连接和客户请求

在这里zygote进程建立了一个子进程system_server,该进程是framework的核心,并且和Zygote保持相同的生命周期。但是,system_server的作用将在runSelectLoop中体现出来。

runSelectLoop分析

在前面我们知道runSelectLoop被用来处理客户连接和客户请求,接下来我们来看看这个方法。

    private static void runSelectLoop() throws MethodAndArgsCaller {
        ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
        ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
        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--;
            }

            try {
                fdArray = fds.toArray(fdArray);
                //selectReadable是一个native方法,内部使用的是select使用多路复用I/O模型
                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) {
                ZygoteConnection newPeer = acceptCommandPeer();
                peers.add(newPeer);
                fds.add(newPeer.getFileDesciptor());
            } else {
                boolean done;
                done = peers.get(index).runOnce();

                if (done) {
                    peers.remove(index);
                    fds.remove(index);
                }
            }
        }
    }

在这段程序中我们可以发现每一次客户的连接中,客户是由ZygoteConnection对象来表示,客户的请求是由ZygoteConnection的runOnce方法来处理。

至此,我们对Zygote进程的分析以及结束,它运行起漫长的runSelectLoop方法就沉沉的睡去,接下来的重要任务将在system_server进程中处理执行。

参考书目:深入理解Android 卷I

时间: 2024-10-27 00:09:26

Android源码分析--Zygote进程分析的相关文章

Android源码分析--system_server进程分析

在上一篇博文中我们进行了有关Zygote进程的分析,我们知道Zygote进程创建了一个重要的进程–system_server进程后就进入了无限循环中,之后Android系统中的重要任务就交给了system_server进程,作为zygote的嫡长子进程,system_server进程的意义非凡,今天我们来分析一下system_server进程. 创建system_server进程 在ZygoteInit中main方法中,通过调用startSystemServer方法开启了system_serve

Android源码分析:Telephony部分–phone进程

Android源码分析:Telephony部分–phone进程红狼博客 com.android.phone进程 它就象个后台进程一样,开机即运行并一直存在.它的代码位于:packages/apps/Phone/src/com/android/phone 当有来电时,它会作出反应,如显示UI和铃声提示:当在通话过程中,它显示InCallScreen: 当要拨号时ITeleohony的接口调用最终到Phone进程,然后由它去与PhoneFactory创建的GSMPhone或CDMAPhone进行交互

Android源码分析:Telephony部分–GSMPhone

Android源码分析:Telephony部分–GSMPhone红狼博客 PhoneProxy/GSMPhone/CDMAPhone 如果说RILJ提供了工具或管道,那么Phone接口的子类及PhoneFactory则为packages/app/Phone这个应用程序进程使用RILJ这个工具或管道提供了极大的方便,它们一个管理整个整个手机的Telephony功能. GSMPhone和CDMAPhone实现了Phone中定义的接口.接口类Phone定义了一套API,这套API用于使用RILJ(见后

Android源码分析之SharedPreferences

在Android的日常开发中,相信大家都用过SharedPreferences来保存用户的某些settings值.Shared Preferences 以键值对的形式存储私有的原生类型数据,这里的私有的是指只对你自己的app可见的,也就是说别的app是无法访问到的. 客户端代码为了使用它有2种方式,一种是通过Context#getSharedPreferences(String prefName, int mode)方法, 另一种是Activity自己的getPreferences(int mo

Android源码分析--MediaServer源码分析(二)

在上一篇博客中Android源码分析–MediaServer源码分析(一),我们知道了ProcessState和defaultServiceManager,在分析源码的过程中,我们被Android的Binder通信机制中的各种复杂的类关系搞的眼花缭乱,接下来我们就以MediaPlayerService为例来分析一下Binder的通信机制.首先来回顾一下: BpBinder和BBinder都是Android中Binder通信的代表类,其中BpBinder是客户端用来与Server交互的代理类,p代

Cordova Android源码分析系列一(项目总览和CordovaActivity分析)

PhoneGap/Cordova是一个专业的移动应用开发框架,是一个全面的WEB APP开发的框架,提供了以WEB形式来访问终端设备的API的功能.这对于采用WEB APP进行开发者来说是个福音,这可以避免了原生开发的某些功能.Cordova 只是个原生外壳,app的内核是一个完整的webapp,需要调用的原生功能将以原生插件的形式实现,以暴露js接口的方式调用. Cordova Android项目是Cordova Android原生部分的Java代码实现,提供了Android原生代码和上层We

Android 源码分析工具

标 题: [原创]Android源码分析工具及方法作 者: MindMac时 间: 2014-01-02,09:32:35链 接: http://bbs.pediy.com/showthread.php?t=183278 在对 Android 源码进行分析时,如果有得力的工具辅助,会达到事半功倍的效果.本文介绍了一些在分析 Android 源码时使用的一些工具和方法,希望能够帮助到有需要的同学. Eclipse 在 Android 应用程序开发过程中,一般会使用 Eclipse,当然 Googl

Android源码分析之SparseArray

本来接下来应该分析MessageQueue了,可是我这几天正好在实际开发中又再次用到了SparseArray(之前有用到过一次,那次只是 大概浏览了下源码,没做深入研究),于是在兴趣的推动下,花了些时间深入研究了下,趁着记忆还是新鲜的,就先在这里分析了. MessageQueue的分析应该会在本周末给出. 和以往一样,首先我们来看看关键字段和ctor: private static final Object DELETED = new Object(); private boolean mGar

如何高效地分析Android_log中的问题?——查看Android源码

在日常解bugs时,需要通过log日志来分析问题,例如查看crash发生时的堆栈信息时,就会有Android的源码的调用,这是就要去查看Android源码. 1.进入Android源码网址查看,例如 http://androidxref.com/ 2.选择对应的Android版本,例如Android 4.4 3.选择framework,在File Path中输入类名,点击Search,选择对应的类名. 4.查看Android源码!