Android P (9.0) 之Zygote進程源碼分析

概述

轉載請註明出處:https://blog.csdn.net/wangzaieee/article/details/85003806

init進程是用戶空間的第一個進程,而zygote進程則是第一個java進程。zygote進程是init進程的子進程,init進程通過解析rc文件,運行了zygote進程。

zygote是Android系統中一個相當重要的進程,它的主要功能就是執行Android應用程序。在Android系統中運行新的應用,如同卵子受精分裂一樣,需要跟Zygote進程(擁有應用程序運行時所需要的各種元素和條件,如:虛擬機等)結合才能執行。

Zygote進程

zygote進程運行時,會初始化Art(或者Dalvik)虛擬機,並啓動它。Android的應用程序是由Java編寫的,它們不能直接以本地進程的形式運行在linux上,只能運行在虛擬機中。並且,每個應用程序都運行在各自的虛擬機中,如果應用程序每次運行都要重新初始化並啓動虛擬機,這個過程會耗費相當長時間。因此,在Android中,應用程序運行前,zygote進程通過共享已運行的虛擬機的代碼與內存信息,縮短應用程序運行所耗費的時間。並且,它會事先將應用程序要使用的Android Framework中的類和資源加載到內存中,並組織形成所用資源的鏈接信息。新運行的Android應用程序在使用所需資源時不必每次重新形成資源的鏈接信息,這回節省大量時間,提高程序運行速度。

那麼,在Linux系統中創建並運行一個進程,與在Android系統中通過zygote來創建並運行一個應用程序,兩者究竟有何不同呢?

以下圖片均參考自《Android框架揭祕》一書(實在不想自己畫了 0.0)

linux_fork.png-278.2kB
如上圖所示,父進程A調用fork()函數創建新的子進程A’。新創建的進程A’共享父進程的內存結構信息與庫連接信息(COW 寫時拷貝技術)。而後子進程A’調用exec(‘B’),將新進程B的代碼加載到內存中。此時,將重新分配內存(因爲之前是共享的),以便運行被裝載的B程序,接着形成新的庫連接信息,以供B進程單獨使用。

android_fork.png-278.9kB
如上圖所示,zygote進程調用fork()函數創建出zygote’子進程,子進程zygote’ 共享父進程zygote的代碼區與鏈接信息。注意,新的Android應用程序A 並非通過exec() 來重新裝載已有進程的代碼區,而是動態的加載到複製的虛擬機上(虛擬空間的複製,物理空間還是同一個)。而後,zygote’進程將執行流程交給應用程序A類的方法,Android應用程序開始運行。新生成的應用程序A會使用已有zygote進程的庫與資源的鏈接信息,所以運行速度很快。

android_zygote_fork.png-631.9kB
如上圖所示,zygote啓動後,初始化並運行art(或者dalvik)虛擬機,而後將需要的類與資源加載到內存中。隨後調用fork()創建出zygote’子進程,接着zygote’子進程動態加載並運行Android應用程序A。運行的應用程序A會使用zygote已經初始化並啓動運行的虛擬機代碼,通過使用已記載至內存中的類與資源來加快運行速度。

COW (Copy on write)
在創建新進程後,新進程會共享父進程的內存空間,即新的子進程會複製所有與父進程內存空間相關的信息並使用它。COW就是針對內存複製的一種技術。一般來說,複製內存的開銷非常大,因此創建的子進程在引用父進程的內存空間時,不要進行復制,而要直接共享父進程的內存空間。而當需要修改共享內存中的信息時,子進程纔會將父進程中相關的內存信息複製到自身的內存空間,並進行修改,這就是COW(Copy on write)技術。

在fork之後exec之前兩個進程用的是相同的物理空間(內存區),子進程的代碼段、數據段、堆棧都是指向父進程的物理空間,也就是說,兩者的虛擬空間不同,但其對應的物理空間是同一個

Zygote進程源碼分析

由app_process運行ZygoteInit class

zygote由java編寫而成,不能直接由init進程啓動運行。若想執行zygote類,必須先創建虛擬機,然後在虛擬機上運行ZygoteInit類。執行這一任務的就是app_process程序。
下面我們開始分析zygote進程的啓動流程:
/system/core/rootdir/init.rc

import /init.$(ro.zygote).rc

如果是64位系統,$(ro.zygote)的值爲"zygote64"
/system/core/rootdir/init.zygote64.rc

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    ...
    class main
    user root
    socket zygote stream 660 root system //創建/dev/socket/zygote套接字
    ...

init進程解析上面的init.zygote64.rc文件後,會執行/system/bin/app_process64進程,並且生成了/dev/socket/zygote套接字,ZygoteInit會用該套接字來接收AMS發來的創建新應用程序的請求。
init進程分析並執行rc文件流程可以看看我的上一篇文章Init進程的啓動流程
app_process64程序的main函數入口如下:
frameworks/base/cmds/app_process/app_main.cpp

int main(int argc, char* const argv[])
{
    ......
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    ......
    // Parse runtime arguments.  Stop at first unrecognized option.
    bool zygote = false;
    bool startSystemServer = false;
    ......

    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        } 
        ......
    }

    ......
    if (zygote) {
        //zygote進程
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        //普通java進程
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    }
}

如果是zygote進程就走ZygoteInit代碼,如果是普通java進程就走RuntimeInit代碼,例如常用的am命令,/system/bin/am實際上是一個shell腳本,查看裏面的代碼可知是通過app_process來啓動普通java進程,然後和AMS進行通信。
frameworks/base/core/jni/AndroidRuntime.cpp

//AppRuntime繼承自AndroidRuntime
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    /* start the virtual machine */
    JniInvocation jni_invocation;
    // ① 加載指定的虛擬機的庫(art或者dalvik)
    jni_invocation.Init(NULL); 
    JNIEnv* env;
    // ② 創建java虛擬機
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }

    // ③ 註冊JNI函數
    if (startReg(env) < 0) {
        return;
    }

    ......
    // ④ 正式進入java的世界,調用ZygoteInit.java的main方法
    jclass startClass = env->FindClass(slashClassName);
    ......
    jmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");
    ......
    env->CallStaticVoidMethod(startClass, startMeth, strArray);
    ......
}

①: 加載虛擬機的庫(art或者dalvik),並且函數指針指向庫對應的函數(如:JNI_CreateJavaVM函數)
libnativehelper/JniInvocation.cpp

bool JniInvocation::Init(const char* library) {
  //獲取要加載庫的名稱
  library = GetLibrary(library, buffer);

  //動態加載對應的虛擬機庫
  handle_ = dlopen(library, RTLD_NOW);
  ......
  //JNI_CreateJavaVM_函數指針指向虛擬機庫中的JNI_CreateJavaVM函數
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
                  "JNI_CreateJavaVM")) {
    return false;
  }
  .......
  return true;
}

GetLibraray()函數獲取動態庫的名稱(libart.so或者libdalvik .so),dlopen()函數動態加載虛擬機庫,RTLD_NOW表示返回之前立即鏈接所有未定位的符號,FindSysmbol()函數指針指向對應的函數。
libnativehelper/JniInvocation.cpp

static const char* kLibrarySystemProperty = "persist.sys.dalvik.vm.lib.2";
static const char* kDebuggableSystemProperty = "ro.debuggable";
static const char* kDebuggableFallback = "0";  // Not debuggable.
static const char* kLibraryFallback = "libart.so";

const char* JniInvocation::GetLibrary(const char* library, char* buffer) {
    char debuggable[PROPERTY_VALUE_MAX];
    //獲取"ro.debuggable"的屬性值
    property_get(kDebuggableSystemProperty, debuggable,kDebuggableFallback);

    if (strcmp(debuggable, "1") != 0) {
        //如果不是debug設備,library的值爲"libart.so"
        library = kLibraryFallback;
    } else {
        //如果是debug設備,library的值爲"persisit.sys.dalvik.vm.lib.2"的屬性值
        property_get(kLibrarySystemProperty, buffer, kLibraryFallback);
    }

    return library;
}

②: 創建java虛擬機
frameworks/base/core/jni/AndroidRuntime.cpp

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{
    JavaVMInitArgs initArgs;
    ......
    initArgs.version = JNI_VERSION_1_4;
    initArgs.options = mOptions.editArray();
    initArgs.nOptions = mOptions.size();
    initArgs.ignoreUnrecognized = JNI_FALSE;

    ......
    //創建java虛擬機
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
        ALOGE("JNI_CreateJavaVM failed\n");
        return -1;
    }

    return 0;
}

JNI_CreateJavaVM()是JniInvocation.cpp中的函數,會調用到①中所說的JNI_CreateJavaVM_()函數指針,最後調用到相應虛擬機動態庫中的JNI_CreateJavaVM()函數,創建對應的虛擬機。initArgs表示傳入的虛擬機參數。
③: 註冊JNI本地函數
我們先來熟悉一下幾個數據結構:
frameworks/base/core/jni/AndroidRuntime.cpp

#define REG_JNI(name)     { name }

struct RegJNIRec {
    int (*mProc)(JNIEnv*);
};
......
static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_com_android_internal_os_RuntimeInit),
    REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
    REG_JNI(register_android_os_SystemClock),
    REG_JNI(register_android_util_EventLog),
    REG_JNI(register_android_util_Log),
    ......
};

gRegJNIRegJNIRec結構體數組,然後數組通過REG_JNI宏定義初始化。例如gRegJNI第一個元素的初始化,等同於:

gRegJNI[0] = {register_com_android_internal_os_RuntimeInit};

那麼gRegJNI[0]中RegJNIRec結構體的mProc函數指針就指向上面的函數register_com_android_internal_os_RuntimeInit,數組中的其它元素也同理。下面我們繼續分析JNI本地函數的註冊。
frameworks/base/core/jni/AndroidRuntime.cpp

int AndroidRuntime::startReg(JNIEnv* env)
{
    ......
    //註冊JNI本地函數,將gRegJNI數組傳入
    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        return -1;
    }
    ......
    return 0;
}

static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
    //遍歷執行gRegJNI數組中元素的mProc函數
    //(mProc是函數指針,數組初始化的時候已經指向指定的函數)
    for (size_t i = 0; i < count; i++) {
        if (array[i].mProc(env) < 0) {
            return -1;
        }
    }
    return 0;
}

startReg會調用register_jni_procs遍歷調用gRegJNI數組中的mProc函數,以第一個元素爲例,gRegJNI[0].mProc(env)由上面的分析可知調用的實際是register_com_android_internal_os_RuntimeInit(env)函數。
frameworks/base/core/jni/AndroidRuntime.cpp

int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
{
    const JNINativeMethod methods[] = {
        { "nativeFinishInit", "()V",
            (void*) com_android_internal_os_RuntimeInit_nativeFinishInit },
        ......
    };
    //最後會調用到(*env)->RegisterNative()註冊
    return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
        methods, NELEM(methods));
}

用上面的代碼可知,nativeFinishInit函數映射了com_android_internal_os_RuntimeInit_nativeFinishInit函數,當java調用nativeFinishInit函數時,實際會調用到c/c++中的com_android_internal_os_RuntimeInit_nativeFinishInit函數。有讀者可能會問,java調用jni函數時,虛擬機會自動映射,爲什麼要自己映射呢?如果jni函數比較少,這麼做確實可行,但是我們可以看到gRegJNI數組是很龐大的,需要映射的函數也很多,如果全部交給虛擬機映射,會大大降低虛擬機的執行性能,所以我們提前註冊JNI函數,虛擬機就可以直接找到對應的函數進行調用。
④: 通過反射調用ZygoteInit.java的main函數,正式進入java的世界。env->FindClass獲取ZygoteInit類的類型,env->GetStaticMethodID獲取函數main的函數id,env->CallStaticVoidMethod調用ZygoteInit.java的靜態函數main。

ZygoteInit類的功能

至此,我們已經創建好了虛擬機,並且將ZygoteInit類裝載到了虛擬機。接下來,ZygoteInit類將會被運行,那麼ZygoteInit類具體有哪些功能呢?大致概括爲如下幾點:

  • 綁定套接字,用來接收新Android應用程序運行請求
  • 預加載Android Application Framework 使用的類與資源
  • 啓動並運行SystemServer
  • 處理新Android應用程序運行請求

ZygoteInit的main函數:
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

public static void main(String argv[]) {
    ZygoteServer zygoteServer = new ZygoteServer();
    ......
    try{
        boolean startSystemServer = false;
        String socketName = "zygote";//套接字默認名稱zygote
        for (int i = 1; i < argv.length; i++) {
            if ("start-system-server".equals(argv[i])) {
                startSystemServer = true;
           }
            ......
        }

        // ① 綁定/dev/socket/zygote套接字,用來接收Android應用程序運行請求
        zygoteServer.registerServerSocketFromEnv(socketName);
        ......
          
        if (!enableLazyPreload) {
            ......
            // ② 預加載類與資源
            preload(bootTimingsTraceLog);
            ......
        }

        ......
        if (startSystemServer) {
            // ③ fork出system_server子進程
           Runnable r = forkSystemServer(abiList, socketName, zygoteServer);

            // ③ {@code r == null}表示是父進程(zygote),{@code r != null}在子進程(system_server)
            if (r != null) {
                // ③ 如果是子進程(system_server)就執行run()方法,並返回,父進程(zygote)就繼續往下執行
                r.run();
                return;
            }
        }

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

        // ④ 這輪詢會在zygote進程中無限循環,而fork出的子進程(Android應用進程)會退出來
        caller = zygoteServer.runSelectLoop(abiList);
    } catch (Throwable ex) {
        Log.e(TAG, "System zygote died with exception", ex);
        throw ex;
    } finally {
        //system_server進程和android應用進程會關閉socket,zygote仍然在runSelectLoop中輪詢監聽socket
        zygoteServer.closeServerSocket();
    }
    

    // ④ Android應用進程會走到這兒,執行相應的命令
    if (caller != null) {
        caller.run();
    }
}

①: 綁定套接字,用來接收運行Android應用運行請求
frameworks/base/core/java/com/android/internal/os/ZygoteServer.java

void registerServerSocketFromEnv(String socketName) {
    if (mServerSocket == null) {
        int fileDesc;
        //fullSocketName爲“ANDROID_SOCKET_zygote”
        final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
        try {
            //獲取ANDROID_SOCKET_zygote的壞境變量(即爲/dev/socket/zygote的文件描述符的值)
            //是init進程在啓動zygote進程時保存到環境變量中的
            String env = System.getenv(fullSocketName);
            fileDesc = Integer.parseInt(env);
        } catch (RuntimeException ex) {
            throw new RuntimeException(fullSocketName + " unset or invalid", ex);
        }
        
        try {
            //綁定socket,在後面用來接收Android應用啓動請求
            FileDescriptor fd = new FileDescriptor();
            fd.setInt$(fileDesc);
            mServerSocket = new LocalServerSocket(fd);
            mCloseSocketFd = true;
        } catch (IOException ex) {
            ......
        }
    }
}

②: 預加載類和資源,後面從zygote進程fork出的應用進程可以直接共享,加快應用進程啓動速度。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

static void preload(TimingsTraceLog bootTimingsTraceLog) {
    ......
    preloadClasses();
    ......
    preloadResources();
    ......
    nativePreloadAppProcessHALs();
    ......
    preloadOpenGL();
    preloadSharedLibraries();
    preloadTextResources();
    .......
    }

preloadClassespreloadResourcesnativePreloadAppProcessHALs預加載類和資源等等,有興趣的同學可以深入瞭解。
③: forkSystemServer fork出system_server子進程,並返回可以調用SystemServer中main方法的Runnable r,並執行對應的run方法,而父進程zygote則繼續往下執行runSelectLoop,監聽Android應用運行執行請求。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

private static Runnable forkSystemServer(String abiList, String socketName,ZygoteServer zygoteServer) {
    ......
    /* Hardcoded command line to start the system server */
    String args[] = {
        "--setuid=1000", //用戶id
        "--setgid=1000", //組id
        ......
        "--nice-name=system_server",
        ......
        "com.android.server.SystemServer",
    };
    
    ZygoteConnection.Arguments parsedArgs = null;
    int pid;
    try {
        parsedArgs = new ZygoteConnection.Arguments(args);
        ......
        //fork出system_server子進程,並且設置對應的參數
        pid = Zygote.forkSystemServer(
                parsedArgs.uid, parsedArgs.gid,
                parsedArgs.gids,
                parsedArgs.runtimeFlags,
                null,
                parsedArgs.permittedCapabilities,
                parsedArgs.effectiveCapabilities);
    } catch (IllegalArgumentException ex) {
        throw new RuntimeException(ex);
    }
    //子進程(system_server)
    if (pid == 0) {
        ......
        //所以返回的r不爲null,直接執行r.run
        return handleSystemServerProcess(parsedArgs);
    }
    //父進程(zygote),返回的是null,繼續往下執行
    return null;
}

ZygoteInit的forkSystemServer方法會調用Zygote的forkSystemServer方法,如果是子進程(system_server)就返回handleSystemServerProcess(),父進程(zygote)就返回null。
frameworks/base/core/java/com/android/internal/os/Zygote.java

public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
    ......
    int pid = nativeForkSystemServer(uid, gid, gids, runtimeFlags, 
                rlimits, permittedCapabilities, effectiveCapabilities);
    ......
    return pid;
    }

調用nativeForkSystemServer fork出子進程,nativeForkSystemServer是本地方法,在前面已經通過startReg方法中的register_com_android_internal_os_ZygotenativeForkSystemServer方法映射到com_android_internal_os_Zygote_nativeForkSystemServer方法上,有興趣的同學可以深入瞭解一下。
fork出子進程之後,子進程就開始調用handleSystemServerProcess()方法
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

private static Runnable handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) {
    ......
    //從環境變量SYSTEMSERVERCLASSPATH獲取到SystemServer類文件相應jar包的路徑
    final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
    if (systemServerClasspath != null) {
        //對相應的jar包做dex優化處理
        performSystemServerDexOpt(systemServerClasspath);
        ......
    }

    ......
    ClassLoader cl = null;
    if (systemServerClasspath != null) {
        //創建類加載器classloader
        cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);

        Thread.currentThread().setContextClassLoader(cl);
    }

    return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
}

先從SYSTEMSERVERCLASSPATH環境中獲取到SystemServer的classpath,然後使用performSystemServerDexOpt對classpath對應的jar包做dex優化處理。然後創建對應的classloader,後續用來加載SystemServer類,ZygoteInit.zygoteInit()繼續往下執行:
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
    ......
    //將標準輸出流和標準錯誤流重定向到Android log中
    RuntimeInit.redirectLogStreams();

    ......
    //本地方法,startReg映射,主要是開啓ProcessState線程池,用來進行binder通信
    ZygoteInit.nativeZygoteInit();
    
    return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}

redirectLogStreams會將標準輸入,錯誤流重定位到Android log中,nativeZygoteInit JNI函數(startReg映射)會開啓ProcessState線程池,用來進行binder通信。
繼續往下執行RuntimeInit.applicationInit():
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

protected static Runnable applicationInit(int targetSdkVersion, String[] argv,ClassLoader classLoader) {
    ......
    // Remaining arguments are passed to the start class's static main
    return findStaticMain(args.startClass, args.startArgs, classLoader);
}

繼續往下執行:

沒有翻譯的英文註釋也值得一看哦

/**
 * Invokes a static "main(argv[]) method on class "className".
 * Converts various failing exceptions into RuntimeExceptions, with
 * the assumption that they will then cause the VM instance to exit.
 *
 * @param className Fully-qualified class name
 * @param argv Argument vector for main()
 * @param classLoader the classLoader to load {@className} with
 */
protected static Runnable findStaticMain(String className, String[] argv,ClassLoader classLoader) {
    Class<?> cl;
    ......
    //獲取到SystemServer的類類型
    cl = Class.forName(className, true, classLoader);
    ......
    Method m;
    try {
        //獲取到main方法的方法id
        m = cl.getMethod("main", new Class[] { String[].class });
    } catch (NoSuchMethodException ex) {
        ......
    } catch (SecurityException ex) {
        ......
    }
    //這個就是③中forkSystemServer的返回值r
    return new MethodAndArgsCaller(m, argv);
}

findStaicMain獲取到SystemServer的類類型,並且獲取到SystemServer中的main方法的方法id。然後new MethodAndArgsCaller(m,argv)就是③中forkSystemServer的返回值r,讓我們看看r.run做了一些什麼。
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

static class MethodAndArgsCaller implements Runnable {
    /** method to call */
    private final Method mMethod;
    /** argument array */
    private final String[] mArgs;

    public MethodAndArgsCaller(Method method, String[] args) {
        mMethod = method;
        mArgs = args;
    }

    public void run() {
        try {
            //通過反射調用mMethod靜態方法
            mMethod.invoke(null, new Object[] { mArgs });
        } catch (IllegalAccessException ex) {
           ......
        } catch (InvocationTargetException ex) {
            ......
        }
    }
}

由上可知,就是通過反射執行SystemServer類的main方法。衆所周知,system_server進程註冊和運行着AMS、PMS、PKMS等核心系統服務,因爲這不是本文講述的重點,所以有興趣的同學可以繼續深入瞭解一波~
④: zygoteServer.runSelectLoop()這輪詢會在zygote進程中無限循環,而fork出的子進程(Android應用進程)會退出並繼續往下執行
frameworks/base/core/java/com/android/internal/os/ZygoteServer.java

//開啓zygote進程輪詢監聽。接收新的socket連接(會創建新的ZygoteConnection)
//並且從這些鏈接中中讀取命令,並且執行
Runnable runSelectLoop(String abiList) {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

    fds.add(mServerSocket.getFileDescriptor());
    peers.add(null);

    while (true) {
        StructPollfd[] pollFds = new StructPollfd[fds.size()];
        for (int i = 0; i < pollFds.length; ++i) {
            pollFds[i] = new StructPollfd();
            pollFds[i].fd = fds.get(i);
            pollFds[i].events = (short) POLLIN;
        }
        try {
            //開啓輪詢
            Os.poll(pollFds, -1);
        } catch (ErrnoException ex) {
            throw new RuntimeException("poll failed", ex);
        }
        for (int i = pollFds.length - 1; i >= 0; --i) {
            if ((pollFds[i].revents & POLLIN) == 0) {
                continue;
            }
            
            if (i == 0) {//如果是新的socket鏈接請求(建立新連接)
            //新建ZygoteConnection鏈接
            ZygoteConnection newPeer = acceptCommandPeer(abiList);
            //添加到鏈接數組中
            peers.add(newPeer);
            //添加到文件描述符數組中
            fds.add(newPeer.getFileDesciptor());
            } else {//如果是之前已經建立的socket鏈接(在已有連接上)
                try {
                    //獲取對應的ZygoteConnection
                    ZygoteConnection connection = peers.get(i);
                    //會執行ZygoteConnection發送過來的命令
                    final Runnable command = connection.processOneCommand(this);

                    if (mIsForkChild) {//子進程走這兒
                        ......
                        //退出,command就是④中的caller
                        return command; 
                    } else {//父進程走這兒,上面是while無限循環,zygote進程永遠不會退出
                        ......
                        if (connection.isClosedByPeer()) {
                            connection.closeSocket();
                            peers.remove(i);
                            fds.remove(i);
                        }
                    }
                } catch (Exception e) {
                    ......
                } finally {
                    mIsForkChild = false;
                }
            }
        }
    }
}

上面的流程概括來說,就是會輪詢/dev/socket/zygote的socket,如果有新的鏈接,就新建ZygoteConnection,並將對應的socket fd添加到fds(輪詢數組中),繼續輪詢,如果是新的鏈接,就重複上面的操作,如果是已經建立的鏈接,就執行該鏈接上讀取到的command,也就是connection.processOneCommand()方法。

Android P之前processOneCommand的方法名是runOnce

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

Runnable processOneCommand(ZygoteServer zygoteServer) {
    String args[];
    Arguments parsedArgs = null;
    FileDescriptor[] descriptors;
    
    try {
    //讀取命令
        args = readArgumentList();
        descriptors = mSocket.getAncillaryFileDescriptors();
    } catch (IOException ex) {
        ......
    }
    
    ......
    parsedArgs = new Arguments(args);
    ......
    
    //fork子進程
    pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
                parsedArgs.instructionSet, parsedArgs.appDataDir);

    try {
        if (pid == 0) {
            // 子進程中(應用進程中)
            zygoteServer.setForkChild();
            ......
            return handleChildProc(parsedArgs, descriptors, childPipeFd,
                    parsedArgs.startChildZygote);
        } else {
            //父進程中(zygote)
            ......
            return null;
        }
    } finally {
        ......
    }
}

從當前鏈接socket中讀取啓動命令。如果讀取成功,zygote將會fork出子進程,並且返回可以調用啓動類的main方法的runnable(也就是④中的caller)

如果是AMS請求啓動應用進程,啓動類就是ActivityThread.javacaller.run()就會反射調用ActivityThread的main方法。

zygoteServer.setForkChild()mIsForkChild全局變量設爲true。
我們接下來分析handleChildProc()方法
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java

private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,FileDescriptor pipeFd, boolean isZygote) {
    //關閉ZygoteConnection中的socket鏈接
    closeSocket();
    ......
    if (parsedArgs.niceName != null) {
        Process.setArgV0(parsedArgs.niceName);
    }
    ......
    if (!isZygote) {
        return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,null /* classLoader */);
    } else {
          ......
    }
}

ZygoteInit.zygoteInit()開始就和③中分析的代碼一模一樣了,這裏就不重複分析了,③中返回的是可以調用SystemServer的main方法的Runnable,而④中返回的是可以調用ActivityThread的main方法的Runnable。
自此,ZygoteInit也已經分析完畢。

總結

上面我們提到Zygote進程是第一個java進程,但整篇分析下來,java進程其實也是運行在c++進程之上的,只不過是java虛擬機屏蔽了這一切。zygote進程的啓動,是從c++世界一步一步過渡到java世界,每個世界做了自己的準備工作。

c++世界(app_main.cpp入口):

  • 動態加載虛擬機動態庫,啓動java虛擬機
  • 註冊JNI本地函數,減輕虛擬機負擔
  • 裝載ZygoteInit到java虛擬機,正式進入java世界

java世界(ZygoteInit.java入口):

  • 綁定套接字,用來接收新Android應用程序運行請求
  • 預加載Android資源,提高應用進程啓動速度
  • 啓動並運行SystemServer(運行AMS、PMS等核心服務)
  • 處理新Android應用程序運行請求

zygote進程啓動其實沒有特別難的難點,主要是繁瑣,源碼分析的過程是枯燥無味的,只有靜下心來,纔能有所收穫。
本文忽略了很多細節,主要是介紹大致的流程,如果有錯誤的地方,還請大家批評指出~ 覺得寫的不錯也請點個贊~

參考資料:
《Android框架揭祕》
Android4.4的zygote進程
Linux寫時拷貝技術(copy-on-write)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章