1. 程式人生 > >android6.0原始碼分析之Zygote程序分析

android6.0原始碼分析之Zygote程序分析

android6.0原始碼分析之Runtime的初始化一文中,對Zygote程序的初期的Runtime初始化過程進行了分析,在Runtime啟動結束後,會對Zygote程序進行初始化,其它Java程序都需要從Zygote程序來fork,而Zygote的初始化是從ZygoteInit的main函式開始的:

//ZygoteInit.java
public static void main(String argv[]) {
    try {
        ...
        //註冊zygote socket
        registerZygoteSocket(socketName);
        ...
//載入資源以及類 preload(); ... // Disable tracing so that forked processes do not inherit stale tracing tags from // Zygote. Trace.setTracingEnabled(false); //啟動system server程序 if (startSystemServer) { startSystemServer(abiList, socketName); } Log.i(TAG, "Accepting command socket connections"
); //迴圈等待建立程序的socket請求 runSelectLoop(abiList); //關閉server socket closeServerSocket(); } catch (MethodAndArgsCaller caller) {//擷取MethodAndArgsCaller異常 //執行新程序的main函式 caller.run(); } catch (RuntimeException ex) { Log.e(TAG, "Zygote died with exception"
, ex); closeServerSocket(); throw ex; } }

首先它會對啟動引數進行解析,得到是否需要啟動systemserver,或者得到ABI_LIST_ARG引數和socketName引數等,然後則會呼叫registerZygoteSocket來建立一個LocalServerSocket來與以後需要systemServer建立程序時進行通訊,接著呼叫preload來對VM虛擬機器中的DEX類等資源進行載入,然後會呼叫runSelectLoop來進行迴圈等待,最後會擷取ZygoteInit中丟擲的MethodAndArgsCaller異常,並在異常處理時,呼叫新建程序的main方法實現新程序的啟動。下面將對這四個主要的呼叫分別進行分析。

1、registerZygoteSocket方法分析

//ZygoteInit.java
private static void registerZygoteSocket(String socketName) {
    if (sServerSocket == null) {//判斷Zygote socket是否已建立
        int fileDesc;
        final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
        try {
            String env = System.getenv(fullSocketName);
            //獲取檔案描述符
            fileDesc = Integer.parseInt(env);
        } catch (RuntimeException ex) {
            throw new RuntimeException(fullSocketName + " unset or invalid", ex);
        }

        try {
            FileDescriptor fd = new FileDescriptor();
            fd.setInt$(fileDesc);
            //建立Zygote socket,它會在runSelectLoop中進行迴圈等待
            sServerSocket = new LocalServerSocket(fd);
        } catch (IOException ex) {
            throw new RuntimeException("Error binding to local socket '" + fileDesc + "'", ex);
        }
    }
}

由程式碼可知,它建立了一個LocalServerSocket,並且它會在runSelectLoop中阻塞等待socket請求,至於runSelectLoop稍後會進行分析。

2、preload方法分析

//ZygoteInit.java
static void preload() {
    Log.d(TAG, "begin preload");
    //載入類
    preloadClasses();
    //載入資源
    preloadResources();
    //載入OpenGL
    preloadOpenGL();
    //載入公共庫
    preloadSharedLibraries();
    //載入文字資源
    preloadTextResources();
    // Ask the WebViewFactory to do any initialization that must run in the zygote process,
    // for memory sharing purposes.
    WebViewFactory.prepareWebViewInZygote();
    Log.d(TAG, "end preload");
}

由程式碼可知,preload方法主要就是進行類,資源,公共庫以及相關的文字資源的載入,主要分析其中的preloadClasses以及preloadSharedLibraries方法:

//ZygoteInit.java
private static void preloadClasses() {
    //獲取虛擬機器執行時
    final VMRuntime runtime = VMRuntime.getRuntime();

    InputStream is;
    try {
        //初始化檔案輸入流,路徑為"/system/etc/preloaded-classes"
        is = new FileInputStream(PRELOADED_CLASSES);
    } catch (FileNotFoundException e) {
        return;
    }
    ...
    // Alter the target heap utilization.  With explicit GCs this
    // is not likely to have any effect.
    float defaultUtilization = runtime.getTargetHeapUtilization();
    runtime.setTargetHeapUtilization(0.8f);

    try {
        //通過BufferReader來讀取輸入流
        BufferedReader br = new BufferedReader(new InputStreamReader(is), 256);

        int count = 0;
        String line;
        //逐行處理
        while ((line = br.readLine()) != null) {
            // Skip comments and blank lines.
            line = line.trim();
            if (line.startsWith("#") || line.equals("")) {
                continue;
            }

            try {
                ...
                //載入並顯示地初始化給定的類
                Class.forName(line, true, null);
                count++;
            } catch (ClassNotFoundException e) {
                ...
            }
        }
    } catch (IOException e) {
        Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
    } finally {
        IoUtils.closeQuietly(is);
        // Restore default.
        runtime.setTargetHeapUtilization(defaultUtilization);
        //將類,域,方法等填充到dex快取中
        runtime.preloadDexCaches();
    }
}

首先,根據”/system/etc/preloaded-classes”來,逐行解析裡面的類,最後再將預載入的類填充到VM的dex快取中,它會呼叫VMRuntime庫的本地方法preloadDexCaches來填充,此方法的具體實現在VM庫中。
接下來看preloadSharedLibraries方法:

//ZygoteInit.java
private static void preloadSharedLibraries() {
    Log.i(TAG, "Preloading shared libraries...");

    System.loadLibrary("android");
    System.loadLibrary("compiler_rt");
    System.loadLibrary("jnigraphics");
}

如程式碼,它主要進行shared庫android,compiler_rt以及jnigraphics的載入。

3、 startSystemServer方法分析

System Server程序是android系統的非常重要的程序,它在Zygote的啟動之後就必須啟動,所以startSystemServer方法也是Zygote初始化中非常重要的呼叫:

//ZygoteInit.java
private static boolean startSystemServer(String abiList, String socketName)
            throws MethodAndArgsCaller, RuntimeException {
    ...
    ZygoteConnection.Arguments parsedArgs = null;
    int pid;

    try {
        parsedArgs = new ZygoteConnection.Arguments(args);
        ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
        ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

        //fork一個systemServer程序
        pid = Zygote.forkSystemServer(parsedArgs.uid, parsedArgs.gid,parsedArgs.gids,
                parsedArgs.debugFlags,null,parsedArgs.permittedCapabilities,
                parsedArgs.effectiveCapabilities);
    } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
    }

    //處理子程序
    if (pid == 0) {
        if (hasSecondZygote(abiList)) {
            waitForSecondaryZygote(socketName);
        }
        //處理system server程序
        handleSystemServerProcess(parsedArgs);
    }
    return true;
}

它首先fork一個systemserver程序,然後再對子程序systemserver進行相關處理,首先來看forkSystemServer方法:

//Zygote.java
public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags,int[][] rlimits, 
        long permittedCapabilities, long effectiveCapabilities) {
    VM_HOOKS.preFork();
    //呼叫native方法
    int pid = nativeForkSystemServer(uid, gid, gids, debugFlags, rlimits, permittedCapabilities, 
            effectiveCapabilities);
    // Enable tracing as soon as we enter the system_server.
    if (pid == 0) {
        Trace.setTracingEnabled(true);
    }
    VM_HOOKS.postForkCommon();
    return pid;
}

這裡將通過JNI呼叫,進入Native程式碼層,接著分析nativeForkSystemServer方法,它對應的是Native方法是com_android_internal_os_Zygote_nativeForkSystemServer:

//com_android_internal_os_Zygote.cpp
static jint com_android_internal_os_Zygote_nativeForkSystemServer(JNIEnv* env, jclass, uid_t uid, 
        gid_t gid, jintArray gids,jint debug_flags, jobjectArray rlimits, jlong
        permittedCapabilities,jlong effectiveCapabilities) {
    //fork 相應程序
    pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,debug_flags, rlimits,
        permittedCapabilities, effectiveCapabilities,MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, 
        NULL,NULL, NULL);
    if (pid > 0) {
        gSystemServerPid = pid;

        int status;
        if (waitpid(pid, &status, WNOHANG) == pid) {
            //system server程序died,終止執行時,重啟zygote程序
            RuntimeAbort(env);
        }
    }
    return pid;
}

它呼叫了ForkAndSpecializeCommon方法來進行程序的建立,最後對建立結果進行了相應的處理,這裡與建立普通程序的區別就是,需要對子程序進行檢查,判斷system server程序是否died,如果died,則會終止runtime,並重啟Zygote程序,接著分析ForkAndSpecializeCommon方法:

//com_android_internal_os_Zygote.cpp
static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
        jint debug_flags, jobjectArray javaRlimits,jlong permittedCapabilities, jlong 
        effectiveCapabilities,jint mount_external,jstring java_se_info, jstring java_se_name,
        bool is_system_server, jintArray fdsToClose,jstring instructionSet, jstring dataDir) {
    SetSigChldHandler();
    //fork一個程序
    pid_t pid = fork();

    if (pid == 0) {//處理子程序
        // The child process.
        gMallocLeakZygoteChild = 1;
        ...
        SetGids(env, javaGids);

        SetRLimits(env, javaRlimits);

        ...
        rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
        ...
        if (se_info_c_str != NULL) {
            //設定執行緒名稱
            SetThreadName(se_name_c_str);
        }
        ...
        //此處會post一個forkChild的訊息
        env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, debug_flags,
            is_system_server ? NULL : instructionSet);
    } else if (pid > 0) {
        // the parent process
    }
    return pid;
}

它首先fork一個子程序,然後對子程序進行處理,最後會post一個forkChild的訊息出去,而在ZygoteInit中會對forksystemserver的子程序進行處理,所以,接著看handleSystemServerProcess方法:

//ZygoteInit.java
private static void handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs)
        throws ZygoteInit.MethodAndArgsCaller {
    ...
    if (parsedArgs.invokeWith != null) {
        String[] args = parsedArgs.remainingArgs;

        if (systemServerClasspath != null) {
            String[] amendedArgs = new String[args.length + 2];
            amendedArgs[0] = "-cp";
            amendedArgs[1] = systemServerClasspath;
            System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2,
                parsedArgs.remainingArgs.length);
        }

        WrapperInit.execApplication(parsedArgs.invokeWith,parsedArgs.niceName,
            parsedArgs.targetSdkVersion,VMRuntime.getCurrentInstructionSet(), null, args);
    } else {
        ClassLoader cl = null;
        if (systemServerClasspath != null) {
            //獲取類載入器
            cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
            Thread.currentThread().setContextClassLoader(cl);
        }

        //根據引數來啟動System server程序
        RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
    }
}

由程式碼可知,它主要會呼叫RuntimeInit類的zygoteInit方法來對子程序system server進行啟動的處理:

//RuntimeInit.java
public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
        throws ZygoteInit.MethodAndArgsCaller {
    ...
    commonInit();
    nativeZygoteInit();
    applicationInit(targetSdkVersion, argv, classLoader);
}

其中commonInit主要就是進行一些系統屬性的初始化或者重置,這裡重點分析nativeZygoteInit和applicationInit方法。

3.1 nativeZygoteInit方法分析

nativeZygoteInit是Native方法,通過JNI呼叫,它的實現是com_android_internal_os_RuntimeInit_nativeZygoteInit方法:

//AndroidRuntime.cpp
static void com_android_internal_os_RuntimeInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{
    gCurRuntime->onZygoteInit();
}

由程式碼可知,這裡會回撥App_main.cpp中的onZygoteInit方法:

//App_main.cpp
virtual void onZygoteInit(){
    sp<ProcessState> proc = ProcessState::self();
    ALOGV("App process: starting thread pool.\n");
    proc->startThreadPool();
}

這裡主要就是在程序裡面啟動執行緒池,該執行緒池,是為System server程序建立的,此執行緒池會為Binder提供支援等,這裡不做分析。

3.2 ApplicationInit方法分析

//RuntimeInit.java
private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
        throws ZygoteInit.MethodAndArgsCaller {

    nativeSetExitWithoutCleanup(true);

    VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
    VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);

    final Arguments args;
    try {
        args = new Arguments(argv);
    } catch (IllegalArgumentException ex) {
        return;
    }
    //invoke system server的main方法
    invokeStaticMain(args.startClass, args.startArgs, classLoader);
}

由程式碼可知,它會根據啟動的引數以及類載入器來呼叫InvokeStaticMain方法:

//RuntimeInit.java
private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
        throws ZygoteInit.MethodAndArgsCaller {
    Class<?> cl;

    try {
        cl = Class.forName(className, true, classLoader);
    } catch (ClassNotFoundException ex) {...}

    Method m;
    try {
        //獲取systemserver的main方法,main方法是用來開始systemserver程序的
        m = cl.getMethod("main", new Class[] { String[].class });
    } catch (NoSuchMethodException ex) {...}
    ...
    //丟擲MethodAndArgsCaller異常,而此異常在ZygoteInit的main方法中會進行撲捉,異常的處理稍後再分析
    throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}

由程式碼可知,首先,通過Java的反射機制,藉助傳入的引數以及類載入器,從而獲取systemserver程序的main方法,最後再丟擲一個MethodAndArgsCaller的異常,而此異常在ZygoteInit的main方法的最後會進行擷取,具體的異常處理稍後在第四節最後再分析,因為普通程序的建立也是通過丟擲MethodAndArgsCaller的方法來啟動的,至此startsystemserver方法就分析結束了。

4、runSelectLoop方法分析

//ZygoteInit.java
private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
    //初始化檔案描述符組合ZygoteConnection連線組
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

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

    while (true) {//迴圈等待ZygoteSocket請求並進行相應的處理
        ...
        for (int i = pollFds.length - 1; i >= 0; --i) {
            if ((pollFds[i].revents & POLLIN) == 0) {
                continue;
            }
            if (i == 0) {
                //這裡阻塞等待建立程序的Zygote socket請求
                ZygoteConnection newPeer = acceptCommandPeer(abiList);
                peers.add(newPeer);
                fds.add(newPeer.getFileDesciptor());
            } else {
                //執行ZygoteConnection連線
                boolean done = peers.get(i).runOnce();
                if (done) {
                    //處理結束,則將此連線移除
                    peers.remove(i);
                    fds.remove(i);
                }
            }
        }
    }
}

此方法將會永無休止的進行執行,因為Zygote作為Java域的第一個程序,所有的程序都是由它進行fork的,在android的生命過程中,會不斷有ZygoteConnection請求,所以,看runSelectLoop方法,它首先是呼叫acceptCommandPeer方法來獲取一個ZygoteConnection連線,然後再呼叫ZygoteConnection的runOnce方法來執行連線處理,首先來看acceptCommandPeer方法:

//ZygoteInit.java
private static ZygoteConnection acceptCommandPeer(String abiList) {
    try {
        //建立ZygoteConnection連線,注意socket的accept阻塞接收請求
        return new ZygoteConnection(sServerSocket.accept(), abiList);
    } catch (IOException ex) {
        throw new RuntimeException("IOException during accept()", ex);
    }
}

由代麼可知,這裡是阻塞的,只有在ZygoteInit的main方法中最初註冊的socket有請求時,才會執行,並會根據得到的socket連線來建立一個ZygoteConnection,所以,來看它的建構函式:

// ZygoteConnection.java
ZygoteConnection(LocalSocket socket, String abiList) throws IOException {
    mSocket = socket;
    this.abiList = abiList;
    //獲取socket輸出流
    mSocketOutStream = new DataOutputStream(socket.getOutputStream());
    //獲取socket輸入流
    mSocketReader = new BufferedReader(new InputStreamReader(socket.getInputStream()), 256);
    //設定超時
    mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS);

    try {
        peer = mSocket.getPeerCredentials();
    } catch (IOException ex) {
        throw ex;
    }
}

此建構函式會建立socket的stream通道,至此ZygoteConnection就建立好了,接著看它的runOnce方法,socket請求一次,runOnce會執行一次:

boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
    ...
    int pid = -1;
    FileDescriptor childPipeFd = null;
    FileDescriptor serverPipeFd = null;

    try {
        parsedArgs = new Arguments(args);
        if (parsedArgs.abiListQuery) {
            return handleAbiListQuery();
        }
        ...
        int [] fdsToClose = { -1, -1 };
        FileDescriptor fd = mSocket.getFileDescriptor();
        ...
        fd = ZygoteInit.getServerSocketFileDescriptor();
        //fork 程序
        pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,parsedArgs.appDataDir);
    } catch (ErrnoException ex) {
        ...
    }

    try {
        if (pid == 0) {
            // in child
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;
            //對子程序做相應處理
            handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
            return true;
        } else {
            IoUtils.closeQuietly(childPipeFd);
            childPipeFd = null;
            //對父程序做相應處理
            return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
        }
    } finally {
        IoUtils.closeQuietly(childPipeFd);
        IoUtils.closeQuietly(serverPipeFd);
    }
}

這裡,就是建立普通程序的通道,它的分析類似於startSystemServer方法,它在最後同樣會進入Java層的子程序的處理handleChildProc方法,最後還是會獲取到建立程序的main方法,並且同樣也是會丟擲一個MethodAndArgsCaller的異常。
不管是建立systemServer程序還是建立普通程序,在處理其子程序時,都會獲取建立子程序的main方法,同時最後會丟擲一個MethodAndArgsCaller異常,為什麼要丟擲異常呢,此異常是在ZygoteInit的main函式中呼叫的,而main函式位於堆疊的最頂層,如果不採用拋異常的方式,而是在invokestaticMain方法中直接執行新建程序的main方法,則會浪費之前函式呼叫說佔用的呼叫堆疊。接下來對MethodAndArgsCaller的run方法進行分析:

//ZygoteInit.java
public void run() {
    try {
        mMethod.invoke(null, new Object[] { mArgs });
    } catch (IllegalAccessException ex) {
            ...
    }
}

其中,mMethod為構造MethodAndArgsCaller時,傳入的方法,即之前startSystemServer或者runSelectLoop函式中獲得的新建程序的main方法,所以run方法的主要作用就是執行此main方法,即進入新建程序。至此,Zygote程序分析結束,它的主要功能有初始化Runtime、fork system server程序以及通過迴圈等待Zygote socket的請求,來處理普通進行的建立。下面將給出時序圖:
這裡寫圖片描述