Android系列:认识zygote

928 阅读5分钟

一、Zygote的启动

1.1 启动进程的方式

  • fork + handle
  • fork + execve

pid 子进程返回的pid是0 父进程返回的pid是子进程的pid。

信号处理:==SIGCHLD==

当子进程挂了之后父进程就会收到这个信号,然后父进程就会做一些处理,比如释放资源或者重新fork子进程。

2.1 Zygote启动

2.1.1 Zygote的native层

  • 启动Android虚拟机
  • 注册Android的JNI函数
  • 利用JNI调用ZygoteInit的main函数 进入java层面
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ...
    /* start the virtual machine */第一步
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {//1
        return;
    }
    //这个时候env里面就有值了
    onVmCreated(env);
    if (startReg(env) < 0) {//2 这里是第二步 注册JNI函数
        ALOGE("Unable to register all android natives\n");
        return;
    }
    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;

    stringClass = env->FindClass("java/lang/String");
    assert(stringClass != NULL);
    //创建数组
    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
    assert(strArray != NULL);
    //从app_main的main函数得知className为com.android.internal.os.ZygoteInit
    classNameStr = env->NewStringUTF(className);
    assert(classNameStr != NULL);
    env->SetObjectArrayElement(strArray, 0, classNameStr);

    for (size_t i = 0; i < options.size(); ++i) {
        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
        assert(optionsStr != NULL);
        env->SetObjectArrayElement(strArray, i + 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 {
    //找到ZygoteInit的main函数
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");//3 第三步
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
        //通过JNI调用ZygoteInit的main函数 进入java世界
            env->CallStaticVoidMethod(startClass, startMeth, strArray);//4

#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }
  ...
}

2.1.2 Zygote的Java层

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

public static void main(String argv[]) {
      ...
       try {
        ...       
           //注册Zygote用的Socket 创建服务端的socket
           registerZygoteSocket(socketName);//1
          ...
          //预加载类和资源
          preload();//2
          ...
          if (startSystemServer) {
                Runnable r = forkSystemServer(abiList, socketName, zygoteServer);

                // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
                // child (system_server) process.
                if (r != null) {
                    r.run();
                    return;
                }
            }
           Log.i(TAG, "Accepting command socket connections");
           //等待客户端请求
           runSelectLoop(abiList);//4
           closeServerSocket();
       } catch (MethodAndArgsCaller caller) {
           caller.run();
       } catch (RuntimeException ex) {
           Log.e(TAG, "Zygote died with exception", ex);
           closeServerSocket();
           throw ex;
       }
   }
   
   //创建server端的socket name 为zygote
    void registerServerSocket(String socketName) {
        if (mServerSocket == null) {
         ...
            try {
                FileDescriptor fd = new FileDescriptor();
                fd.setInt$(fileDesc);
                mServerSocket = new LocalServerSocket(fd);
            } catch (IOException ex) {
                
            }
        }

小结:

  • 创建server端的socket
  • ==预加载== 这里是加载一些给应用进程共享的一些资源,也就是==系统资==源(sdk中大部分类和资源文件),应用进程只需要加载自己的资源就可以了。
  • forkSystemServer 创建SystemServer进程
  • 开启loop 等待AMS来请求
启动SystemServer
    public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
        //启动Binder进程
        ZygoteInit.nativeZygoteInit();
        //从native层启动SystemServer
        return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
    }

进入SystermServer的main方法

void main(){
     // Initialize native services
            System.loadLibrary("android_servers");

            // Check whether we failed to shut down last time we tried.
            // This call may not return.
            performPendingShutdown();

            // Initialize the system context.
            createSystemContext();

            // Create the system service manager.
            mSystemServiceManager = new SystemServiceManager(mSystemContext);
            mSystemServiceManager.setRuntimeRestarted(mRuntimeRestart);
            LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
            // Prepare the thread pool for init tasks that can be parallelized
            SystemServerInitThreadPool.get();
        } finally {
            traceEnd();  // InitBeforeStartServices
        }

           // Start services.启动一些系统服务
            startBootstrapServices();
            startCoreServices();
            startOtherServices();
        
    }

SystemServer启动总结:

  • 启动Binder线程池
  • 创建SystemServiceManager 用于管理系统服务
  • 启动系统各种服务(AMS就在这里启动)

==两个细节==:

  • Zygote fork进程要在单线程中进行,否则容易造成死锁和资源不一致
  • Zygote里面的通信是采用的LocalSocket

两个问题:

==问题一==:fork进程的任务为什么不交给SystermServer进程,而是单独交给Zygote进程?

我们知道,应用在启动的时候需要做很多准备工作,==包括启动虚拟机,加载各类系统资源等等,这些都是非常耗时的==,==如果能在zygote里就给这些必要的初始化工作做好,子进程在fork的时候就能直接共享,那么这样的话效率就会非常高。这个就是zygote存在的价值==,这一点呢SystemServer是替代不了的,主要是因为SystemServer里跑了一堆系统服务,这些是不能继承到应用进程的。而且我们应用进程在启动的时候,内存空间除了必要的资源外,最好是干干净净的,不要继承一堆乱七八糟的东西。所以呢,不如给SystemServer和应用进程里都要用到的资源抽出来单独放在一个进程里,也就是这的zygote进程,然后zygote进程再分别孵化出SystemServer进程和应用进程。孵化出来之后,SystemServer进程和应用进程就可以各干各的事了。

==问题二==:为什么Zygote的IPC不采用Binder机制?

第一个原因,我们可以设想一下采用binder调用的话该怎么做,首先zygote要启用binder机制,需要打开binder驱动,获得一个描述符,再通过mmap进行内存映射,还要注册binder线程,这还不够,还要创建一个binder对象注册到serviceManager,另外AMS要向zygote发起创建应用进程请求的话,要先从serviceManager查询zygote的binder对象,然后再发起binder调用,这来来回回好几趟非常繁琐,相比之下,zygote和SystemServer进程本来就是父子关系,对于简单的消息通信,用管道或者socket非常方便省事。第二个原因,如果zygote启用binder机制,再fork出SystemServer,那么SystemServer就会继承了zygote的描述符以及映射的内存,这两个进程在binder驱动层就会共用一套数据结构,这显然是不行的,所以还得先给原来的旧的描述符关掉,再重新启用一遍binder机制,这个就是自找麻烦了。==第三:zygote fork进程要在单线程中进行,而Binder恰好是多线程的==。

面试题:==谈谈你对Zygote进程的理解==

我们采用三段式去回答,这也是回答问题的套路

  • ==What== :zygote的作用是什么?
  • ==How==:zygote的启动流程?
  • ==Why==:zygote的启动原理?