Android4.4的zygote進程


Android4.4的zygote進程 java

侯亮 android


1背景

前些天爲了在科室作培訓,我基於Android 4.4從新整理了一份關於zygote的文檔。從技術的角度看,這幾年zygote並無出現什麼大的變化,因此若是有人之前研究過zygote,應該不會對本文寫的內容感到陌生。 shell

2zygote進程的描述

在Android中,zygote是整個系統建立新進程的核心裝置。從字面上看,zygote是受精卵的意思,它的主要工做就是進行細胞分裂。 編程

zygote進程在內部會先啓動Dalvik虛擬機,繼而加載一些必要的系統資源和系統類,最後進入一種監聽狀態。在後續的運做中,當其餘系統模塊(好比AMS)但願建立新進程時,只需向zygote進程發出請求,zygote進程監聽到該請求後,會相應地「分裂」出新的進程,因而這個新進程在初生之時,就先天具備了本身的Dalvik虛擬機以及系統資源。 數組

系統啓動伊始,zygote進程就會被init進程啓動起來,init進程的詳情可參考我寫的《Android4.4的init進程》一文,此處再也不贅述。咱們直接來看init.rc腳本里的相關描述吧。在這個腳本中是這樣描述zygote的: app

能夠看到,zygote對應的可執行文件就是/system/bin/app_process,也就是說系統啓動時會執行到這個可執行文件的main()函數裏。 socket

3zygote進程的實現細節

zygote服務的main()函數位於frameworks\base\cmds\app_process\App_main.cpp文件中,其代碼截選以下: ionic

int main(int argc, char* const argv[])
{
    . . . . . .
    AppRuntime runtime;
    const char* argv0 = argv[0];    // /system/bin/app_process
    argc--;
    argv++;
    . . . . . .
    int i = runtime.addVmArguments(argc, argv); // 會跳過-Xzygote,i的位置對應/system/bin
    . . . . . .
    while (i < argc) {
        const char* arg = argv[i++];		// 應該是/system/bin目錄
        if (!parentDir) {
            parentDir = arg;
        } else if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = "zygote";
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        } 
        . . . . . .
    }

    if (niceName && *niceName) {
        setArgv0(argv0, niceName);
        set_process_name(niceName);     // 通常更名爲「zygote」
    }
    runtime.mParentDir = parentDir;
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit",
                startSystemServer ? "start-system-server" : "");
    } else if (className) {
        . . . . . .
    } else {
        . . . . . .
    }
}


3.1AppRuntime的start()

main()函數裏先構造了一個AppRuntime對象,即AppRuntime runtime;然後把進程名改爲「zygote」,並利用runtime對象,把工做轉交給java層的ZygoteInit類處理。 函數

這個AppRuntime類繼承於AndroidRuntime類,卻沒有重載其start(...)函數,因此main()函數中調用的runtime.start(...)其實走的是AndroidRuntime的start(...),並且傳入了類名參數,即字符串——「com.android.internal.os.ZygoteInit」。start()函數的主要代碼截選以下: oop

【frameworks/base/core/jni/AndroidRuntime.cpp】
void AndroidRuntime::start(const char* className, const char* options)
{
    . . . . . .
    const char* rootDir = getenv("ANDROID_ROOT");
    . . . . . .
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);				// 初始化JNI接口
    JNIEnv* env;
    if (startVm(&mJavaVM, &env) != 0) {		// 啓動虛擬機
        return;
    }
    onVmCreated(env);

    if (startReg(env) < 0) {				// 註冊系統須要的jni函數
        ALOGE("Unable to register all android natives\n");
        return;
    }
    . . . . . .
    jclass startClass = env->FindClass(slashClassName);
    . . . . . .
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        . . . . . .
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
    . . . . . .
}
代碼中會先初始化JNI接口,並啓動Dalvik虛擬機(startVm()),而後註冊一些系統須要的jni函數,接着將傳入的類名字符串參數展轉傳給FindClass(),最後調用env->CallStaticVoidMethod()一句。

拋開Java層和C++層的概念,上面的流程說白了就是,Zygote進程的main()函數在啓動Dalvik虛擬機後,會調用另外一個ZygoteInit類的main()靜態函數。調用示意圖以下:


3.1.1加載合適的虛擬機動態庫

一開始須要初始化JNI接口。

JniInvocation jni_invocation;
jni_invocation.Init(NULL);
這兩句是在Android 4.4上出現的,在Android 4.0上,尚未它們呢。

jni_invocation的init()的代碼以下:
【libnativehelper/JniInvocation.cpp】

bool JniInvocation::Init(const char* library) 
{
#ifdef HAVE_ANDROID_OS
  char default_library[PROPERTY_VALUE_MAX];
  property_get(kLibrarySystemProperty, default_library, kLibraryFallback);
#else
  const char* default_library = kLibraryFallback;
#endif
  if (library == NULL) {
    library = default_library;
  }

  handle_ = dlopen(library, RTLD_NOW);
  . . . . . .
  . . . . . .
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),
                  "JNI_GetDefaultJavaVMInitArgs")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
                  "JNI_CreateJavaVM")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),
                  "JNI_GetCreatedJavaVMs")) {
    return false;
  }
  return true;
}

由於咱們使用的是Android系統,因此已經定義了HAVE_ANDROID_OS宏,並且library參數爲NULL,因而在JniInvocation的Init()函數中,會走到

property_get(kLibrarySystemProperty, default_library, kLibraryFallback);
其中,kLibrarySystemProperty的定義是「persist.sys.dalvik.vm.lib」,這是個系統屬性,它記錄着系統實際須要用到的是哪一種虛擬機,是dalvik仍是ART,它們分別對應libdvm.so或libart.so。接着,dlopen()嘗試加載libdvm.so或libart.so。這兩個so中都export出了JNI_GetDefaultJavaVMInitArgs,JNI_CreateJavaVM和JNI_GetCreatedJavaVMs這三個接口函數。


具體加載動態庫的函數是dlopen(),代碼以下:
【bionic/linker/Dlfcn.cpp】

void* dlopen(const char* filename, int flags) {
  ScopedPthreadMutexLocker locker(&gDlMutex);
  soinfo* result = do_dlopen(filename, flags);
  if (result == NULL) {
    __bionic_format_dlerror("dlopen failed", linker_get_error_buffer());
    return NULL;
  }
  return result;
}

本文不需細究dlopen()的實現,你們只需知道,它是個強大的庫函數,能夠打開某個動態庫,並將之裝入內存。調用dlopen()時傳入的第二個參數是RTLD_NOW,它表示加載器會當即計算庫的依賴性,從而在dlopen()返回以前,解析出每一個未定義變量的地址。

3.1.2啓動Dalvik虛擬機,startVm()

初始化JNI環境後,就能夠啓動Dalvik虛擬機了。下面是startVm()的代碼截選:
【frameworks/base/core/jni/AndroidRuntime.cpp】

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
    . . . . . .
    JavaVMInitArgs initArgs;
    JavaVMOption opt;
    . . . . . .
    . . . . . .
    opt.extraInfo = (void*) runtime_exit;
    opt.optionString = "exit";
    mOptions.add(opt);
    . . . . . .
    // Increase the main thread's interpreter stack size for bug 6315322.
    opt.optionString = "-XX:mainThreadStackSize=24K";
    mOptions.add(opt);
    . . . . . .
    . . . . . .

    initArgs.version = JNI_VERSION_1_4;
    initArgs.options = mOptions.editArray();
    initArgs.nOptions = mOptions.size();
    initArgs.ignoreUnrecognized = JNI_FALSE;

    // 啓動dalvik虛擬機
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
        . . . . . .
    }
    . . . . . .
}

由於本文闡述的重點不是Dalvik虛擬機,因此就再也不深究JNI_CreateJavaVM()了。咱們大概知道該函數會調用到剛剛jni_invocation的init()中,FindSymbol()獲得的動態庫中相應的函數指針便可。

3.1.3註冊Android內部須要的函數,startReg()

當虛擬機成功啓動後,JNI環境也就創建好了,如今能夠把JNIEnv*傳遞給startReg()來註冊一些重要的JNI接口了。startReg()的代碼截選以下:
【frameworks/base/core/jni/AndroidRuntime.cpp】

int AndroidRuntime::startReg(JNIEnv* env)
{
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
    . . . . . .
    env->PushLocalFrame(200);
    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        env->PopLocalFrame(NULL);
        return -1;
    }
    env->PopLocalFrame(NULL);
    return 0;
}


static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
    for (size_t i = 0; i < count; i++) {
        if (array[i].mProc(env) < 0) { // 回調每一個RegJNIRec數組項的mProc
#ifndef NDEBUG
            ALOGD("----------!!! %s failed to load\n", array[i].mName);
#endif
            return -1;
        }
    }
    return 0;
}

註冊jni函數的動做很簡單,只是在遍歷array數組,並嘗試回調每一個數組項的mProc回調函數。具體數組項類型的定義以下,並且struct定義的上方還順帶定義了REG_JNI宏。:

#ifdef NDEBUG
    #define REG_JNI(name)      { name }
    struct RegJNIRec {
        int (*mProc)(JNIEnv*);
    };
#else
    #define REG_JNI(name)      { name, #name }
    struct RegJNIRec {
        int (*mProc)(JNIEnv*);
        const char* mName;
    };
#endif

gRegJNI的定義截選以下:

static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_android_debug_JNITest),
    REG_JNI(register_com_android_internal_os_RuntimeInit),   // 舉例
    REG_JNI(register_android_os_SystemClock),
    REG_JNI(register_android_util_EventLog),
    REG_JNI(register_android_util_Log),
    REG_JNI(register_android_util_FloatMath),
    REG_JNI(register_android_text_format_Time),
REG_JNI(register_android_content_AssetManager),
    . . . . . .
    . . . . . .

這些註冊動做內部,基本上就是爲本身關心的類註冊jni接口啦。好比上面的register_com_android_internal_os_RuntimeInit()函數,它的代碼以下:
【frameworks/base/core/jni/AndroidRuntime.cpp】

int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
{
    return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
                                         gMethods, NELEM(gMethods));
}


它關心的就是RuntimeInit類,如今爲這個類的native成員註冊對應的實現函數,這些實現函數就記錄在gMethods數組中:

static JNINativeMethod gMethods[] = {
    { "nativeFinishInit", "()V",
        (void*) com_android_internal_os_RuntimeInit_nativeFinishInit },
    { "nativeZygoteInit", "()V",
        (void*) com_android_internal_os_RuntimeInit_nativeZygoteInit },
    { "nativeSetExitWithoutCleanup", "(Z)V",
        (void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup },
};

其餘「註冊動做」的格局大概也都是這樣,咱們就不贅述了。

3.1.4加載ZygoteInit類

AppRuntime的start()最後會加載Java層次的ZygoteInit類,並利用JNI技術的CallStaticVoidMethod()調用其靜態的main()函數。

jclass startClass = env->FindClass(slashClassName);
    . . . . . .
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        . . . . . .
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

這是很關鍵的一步,就在這一步,控制權就轉移到Java層次了。

3.2走入Java層——ZygoteInit.java

隨着控制權傳遞到Java層次,ZygoteInit要作一些和Android平臺緊密相關的重要動做,好比建立LocalServerSocket對象、預加載一些類以及資源、啓動「Android系統服務」、進入核心循環等等。咱們先畫一張示意圖:


相應地,咱們還能夠把前文的調用關係也豐富一下,獲得下圖:


3.2.1registerZygoteSocket()

咱們先看ZygoteInit的main()函數調用的那個registerZygoteSocket()。這個函數內部其實會利用一個叫做「ANDROID_SOCKET_zygote」的環境變量。但是這個環境變量又是從哪裏來的呢?爲了解答這個問題,咱們須要先看一下init進程service_start()函數。

3.2.1.1先看一下init進程的service_start()

前文咱們已經列出了在init.rc腳本中,zygote服務是如何聲明的。如今咱們只關心其中和socket相關的部分:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    . . . . . .
    socket zygote stream 660 root system
    . .  . . . .

這個服務的socket選項代表,它須要一個名爲「zygote」的「流型(stream)」socket。

當init進程真的啓動zygote服務時,會走到service_start()。咱們如今只關心service_start()裏和socket相關的動做。

【system/core/init/Init.c】
void service_start(struct service *svc, const char *dynamic_args)
{
    . . . . . .
    pid = fork();   // 先fork出新的service進程
    if (pid == 0) 
    {
        struct socketinfo *si;
        struct svcenvinfo *ei;
        . . . . . .
        // 再爲service進程建立必須的socket接口
         for (si = svc->sockets; si; si = si->next) 
        {
            int socket_type = (
                    !strcmp(si->type, "stream") ? SOCK_STREAM :
                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
            int s = create_socket(si->name, socket_type,
                                      si->perm, si->uid, si->gid);
            if (s >= 0) {
                // 將socket接口記入ANDROID_SOCKET_zygote環境變量
                publish_socket(si->name, s);  
            }
        }
        . . . . . .
            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
            }
     }
     . . . . . .
 }

每次爲service建立新的子進程後,都會查看該service須要什麼socket。好比zygote服務,明確提出須要一個「流型(stream)」的socket。

create_socket()會在/dev/socket目錄中建立一個Unix範疇的socket,然後,publish_socket()會把新建的socket的文件描述符記錄在以「ANDROID_SOCKET_」打頭的環境變量中。好比zygote對應的socket選項中的socket名爲「zygote」,那麼該socket對應的環境變量名就是「ANDROID_SOCKET_zygote」

create_socket()的代碼截選以下:
【system/core/init/Util.c】

int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid)
{
    struct sockaddr_un addr;
    int fd, ret;
    . . . . . .
    fd = socket(PF_UNIX, type, 0);
. . . . . .
    // "/dev/socket/zygote"
    snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s", name);
    ret = unlink(addr.sun_path);
    . . . . . .
    ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));  // 給套接字命名
    . . . . . .
    chown(addr.sun_path, uid, gid);
    chmod(addr.sun_path, perm);
    . . . . . .
    return fd;
    . . . . . .
}

當用socket()函數建立套接字之後,使用bind()將「指定的地址」賦值給「用文件描述符表明的套接字」,通常來講,該操做被稱爲「給套接字命名」。一般狀況下,在一個SOCK_STREAM套接字接收鏈接以前,必須經過bind()函數用本地地址爲套接字命名。對於zygote,其套接字地址應該是「/dev/socket/zygote」。在調用bind()函數以後,socket()函數建立的套接字已經和指定的地址關聯起來了,如今向這個地址發送的數據,就能夠經過套接字讀取出來了。

接下來,service_start()還調用了個publish_socket()函數,該函數的代碼以下:
【system/core/init/Init.c】

static void publish_socket(const char *name, int fd)
{
    char key[64] = ANDROID_SOCKET_ENV_PREFIX;
    char val[64];

    strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
            name,
            sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
    snprintf(val, sizeof(val), "%d", fd);  // 將文件描述符轉爲字符串
    add_environment(key, val);

    /* make sure we don't close-on-exec */
    fcntl(fd, F_SETFD, 0);
}

這麼看來,所謂的「發佈」(publish),主要是把socket的文件描述符記錄進環境變量。
上面代碼中的ANDROID_SOCKET_ENV_PREFIX的定義以下:

#define ANDROID_SOCKET_ENV_PREFIX "ANDROID_SOCKET_"

那麼對於zygote而言,就是在環境變量「ANDROID_SOCKET_zygote」裏記錄文件描述符對應的字符串。

add_environment()的代碼以下:

int add_environment(const char *key, const char *val)
{
    int n;
    for (n = 0; n < 31; n++) {
        if (!ENV[n]) {
            size_t len = strlen(key) + strlen(val) + 2;
            char *entry = malloc(len);
            snprintf(entry, len, "%s=%s", key, val);
            ENV[n] = entry;
            return 0;
        }
    }
    return 1;
}

無非是把字符串記入一個靜態數組而已,在後續的代碼裏,service_start()會調用execve(),並把ENV環境變量傳遞給execve()。

3.2.1.2registerZygoteSocket()裏建立LocalServerSocket

OK,咱們已經看到init進程在新fork出的zygote進程裏,是如何記錄「ANDROID_SOCKET_zygote」環境變量的。如今咱們能夠回過頭來看zygote中的registerZygoteSocket()了,此處會切實地用到這個環境變量。

registerZygoteSocket()的代碼以下:
【frameworks/base/core/java/com/android/internal/os/ZygoteInit.java】

private static void registerZygoteSocket() {
    if (sServerSocket == null) {
        int fileDesc;
        try {
            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 {
            sServerSocket = new LocalServerSocket(createFileDescriptor(fileDesc));
        } catch (IOException ex) {
            throw new RuntimeException(
                    "Error binding to local socket '" + fileDesc + "'", ex);
        }
    }
}

先從環境變量裏讀出socket的文件描述符,而後建立LocalServerSocket對象,並記入靜態變量sServerSocket中。之後zygote進程會循環監聽這個socket,一旦accept到鏈接請求,就建立命令鏈接(Command Connection)。監聽動做的細節是在runSelectLoop()中,咱們會在後文闡述,這裏先放下。


如今咱們能夠畫一張建立zygote socket接口的示意圖,以下:

請注意,圖中明確畫出了兩個進程,一個add環境變量,另外一個get環境變量。

3.2.2預加載一些類——preloadClasses()

註冊完socket接口,ZygoteInit會預加載一些類,這些類記錄在frameworks/base/preloaded-classes文本文件裏。下面是該文件的一部分截選:

# Classes which are preloaded by com.android.internal.os.ZygoteInit.
# Automatically generated by frameworks/base/tools/preload/WritePreloadedClassFile.java.
# MIN_LOAD_TIME_MICROS=1250
# MIN_PROCESSES=10
android.R$styleable
android.accounts.Account
android.accounts.Account$1
android.accounts.AccountManager
android.accounts.AccountManager$12
android.accounts.AccountManager$13
android.accounts.AccountManager$6
android.accounts.AccountManager$AmsTask
android.accounts.AccountManager$AmsTask$1
android.accounts.AccountManager$AmsTask$Response
. . . . . .
. . . . . .

在Android4.4上,這個腳本文件已經長達兩千七百多行了,它裏面記錄着加載時間超過1250微秒的類,ZygoteInit嘗試在系統啓動時就把它們預加載進來,從而省去後續頻繁加載時帶來的系統開銷。

preloadClasses()的代碼截選以下:

private static void preloadClasses() 
{
    . . . . . .
    InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream(
                                            PRELOADED_CLASSES);  // 即"preloaded-classes"
    . . . . . .
    . . . . . .
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(is), 256);
            . . . . . .
            while ((line = br.readLine()) != null) 
            {
                line = line.trim();
                    . . . . . .
                    Class.forName(line);  // 使用加載當前類的類加載器來加載指定類
                    . . . . . .
                    count++;
                . . . . . .
            }
            . . . . . .
        } catch (IOException e) {
            . . . . . .
        } finally {
            . . . . . .
            runtime.preloadDexCaches();
            . . . . . .
        }
    . . . . . .
}

在一個while循環裏,每次讀取一行,而後調用Class.forName()方法來裝載類。這個工做量可不小,畢竟有兩千多行哩。


3.2.3預加載一些系統資源——preloadResources()

除了預加載一些類,zygote進程還要預加載一些系統資源。

private static void preloadResources() 
{
    . . . . . .
        mResources = Resources.getSystem();
        mResources.startPreloading();
        if (PRELOAD_RESOURCES) {
            . . . . . .
            TypedArray ar = mResources.obtainTypedArray(
                    com.android.internal.R.array.preloaded_drawables);
            int N = preloadDrawables(runtime, ar);
            ar.recycle();
            . . . . . .
            ar = mResources.obtainTypedArray(
                    com.android.internal.R.array.preloaded_color_state_lists);
            N = preloadColorStateLists(runtime, ar);
            ar.recycle();
            . . . . . .
        }
        mResources.finishPreloading();
    . . . . . .
}

首先,從preloaded_drawables數組資源中讀取一個類型數組(TypedArray),具體的資源文件可參考frameworks/base/core/res/res/values/arrays.xml,截選以下:

基本上有兩大類資源:
1)一類和圖片有關(preloaed_drawables)
2)另外一類和顏色有關(preloaded_color_state_lists)

加載第一類資源須要調用preloadDrawables(),逐個加載TypedArray裏記錄的圖片資源:

private static int preloadDrawables(VMRuntime runtime, TypedArray ar) 
{
    int N = ar.length();
    for (int i=0; i<N; i++) {
        . . . . . .
        int id = ar.getResourceId(i, 0);  // 得到i項對應的資源id
        . . . . . .
        if (id != 0) {
            if (mResources.getDrawable(id) == null) {
                throw new IllegalArgumentException(
                        "Unable to find preloaded drawable resource #0x"
                        + Integer.toHexString(id)
                        + " (" + ar.getString(i) + ")");
            }
        }
    }
    return N;
}

提及來以前mResources.obtainTypedArray()獲取TypedArray時,其內部用的是AssetManager。獲得TypedArray以後,咱們就能夠經過調用ar.getResourceId(i, 0)來獲得數組項對應的資源id了。

其中的mResources是ZygoteInit的私有靜態成員:

private static Resources mResources;

mResources的getDrawable()函數內部,會調用loadDrawable()。這樣,這些圖片資源就都加載到ZygoteInit的mResources裏了。

另外一些資源是顏色資源,是用preloadColorStateLists()加載的:

private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) {
    int N = ar.length();
    for (int i=0; i<N; i++) {
        . . . . . .
        int id = ar.getResourceId(i, 0);
        . . . . . .
        if (id != 0) {
            if (mResources.getColorStateList(id) == null) {
                throw new IllegalArgumentException(
                        "Unable to find preloaded color resource #0x"
                        + Integer.toHexString(id)
                        + " (" + ar.getString(i) + ")");
            }
        }
    }
    return N;
}

也是在一個for循環裏逐個加載顏色集,好比arrays.xml裏的
<item>@color/primary_text_dark</item>
這個顏色集的參考文件是frameworks/base/core/res/res/color/primary_text_dark.xml,

如今,咱們畫一張加載系統資源的調用關係圖:

3.2.4啓動Android系統服務——startSystemServer()

接下來就是啓動Android的重頭戲了,此時ZygoteInit的main()函數會調用startSystemServer(),該函數用於啓動整個Android系統的系統服務。其大致作法是先fork一個子進程,而後在子進程中作一些初始化動做,繼而執行SystemServer類的main()靜態函數。須要注意的是,startSystemServer()並非在函數體內直接調用Java類的main()函數的,而是經過拋異常的方式,在startSystemServer()以外加以處理的。

startSystemServer()的代碼以下:

private static boolean startSystemServer()
        throws MethodAndArgsCaller, RuntimeException 
{
    . . . . . .
    /* Hardcoded command line to start the system server */
    String args[] = {
        "--setuid=1000",
        "--setgid=1000",
        "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,
                        3001,3002,3003,3006,3007",
        "--capabilities=" + capabilities + "," + capabilities,
        "--runtime-init",
        "--nice-name=system_server",
        "com.android.server.SystemServer",
    };
    ZygoteConnection.Arguments parsedArgs = null;
    int pid;
    try {
        parsedArgs = new ZygoteConnection.Arguments(args);
        ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
        ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

        // fork出系統服務對應的進程
        pid = Zygote.forkSystemServer(parsedArgs.uid, parsedArgs.gid,
                                            parsedArgs.gids, parsedArgs.debugFlags, null,
                                            parsedArgs.permittedCapabilities,
                                            parsedArgs.effectiveCapabilities);
    } catch (IllegalArgumentException ex) {
        throw new RuntimeException(ex);
    }

    // 對新fork出的系統進程,執行handleSystemServerProcess()
    if (pid == 0) {
        handleSystemServerProcess(parsedArgs);
    }
    return true;
}
 args[]中的字符串  對應
 "--setuid=1000"  parsedArgs.uid
 "--setgid=1000"  parsedArgs.gid

 "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,
1009,1010,1018,1021,1032,3001,3002,3003,3006,3007"

 parsedArgs.gids
 "--capabilities=" + capabilities + "," + capabilities  capabilitiesSpecified = true;
permittedCapabilities = Long.decode(capStrings[0]);
effectiveCapabilites = Long.decode(capString[1]);
 "--runtime-init"  parsedArgs.runtimeInit設爲true
 "--nice-name=system_server"  parsedArgs.niceName
 "com.android.server.SystemServer"  parsedArgs.remainingArgs

3.2.4.1Zygote.forkSystemServer()

Zygote.forkSystemServer()的代碼以下:
【libcore/dalvik/src/main/java/dalvik/system/Zygote.java】

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

其中的nativeForkSystemServer()是個native成員函數,其對應的C++層函數爲Dalvik_dalvik_system_Zygote_forkSystemServer()。
【dalvik/vm/native/dalvik_system_Zygote.cpp】

const DalvikNativeMethod dvm_dalvik_system_Zygote[] = {
    { "nativeFork", "()I",
      Dalvik_dalvik_system_Zygote_fork },
    { "nativeForkAndSpecialize", "(II[II[[IILjava/lang/String;Ljava/lang/String;)I",
      Dalvik_dalvik_system_Zygote_forkAndSpecialize },
    { "nativeForkSystemServer", "(II[II[[IJJ)I",
      Dalvik_dalvik_system_Zygote_forkSystemServer },
    { NULL, NULL, NULL },
};


static void Dalvik_dalvik_system_Zygote_forkSystemServer(
        const u4* args, JValue* pResult)
{
    pid_t pid;
    pid = forkAndSpecializeCommon(args, true);

    if (pid > 0) {
        int status;

        ALOGI("System server process %d has been created", pid);
        gDvm.systemServerPid = pid;
        if (waitpid(pid, &status, WNOHANG) == pid) {
            ALOGE("System server process %d has died. Restarting Zygote!", pid);
            kill(getpid(), SIGKILL);
        }
    }
    RETURN_INT(pid);
}

forkAndSpecializeCommon()內部其實會調用fork(),然後設置gid、uid等信息。

3.2.4.2SystemServer的handleSystemServerProgress()函數

接着,startSystemServer()會在新fork出的子進程中調用handleSystemServerProgress(),讓這個新進程成爲真正的系統進程(SystemServer進程)。

// 對新fork出的系統進程,執行handleSystemServerProcess()
    if (pid == 0) {
        handleSystemServerProcess(parsedArgs);
    }

注意,調用handleSystemServerProcess()時,程序是運行在新fork出的進程中的。handleSystemServerProcess()的代碼以下:
【frameworks/base/core/java/com/android/internal/os/ZygoteInit.java】

private static void handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs)
                                           throws ZygoteInit.MethodAndArgsCaller 
{
    closeServerSocket();
    Libcore.os.umask(S_IRWXG | S_IRWXO);

    if (parsedArgs.niceName != null) {
        Process.setArgV0(parsedArgs.niceName);  // niceName就是」system_server」
    }

    if (parsedArgs.invokeWith != null) {
        WrapperInit.execApplication(parsedArgs.invokeWith,
                parsedArgs.niceName, parsedArgs.targetSdkVersion,
                null, parsedArgs.remainingArgs);
} else {
        // 此時的remainingArgs就是」com.android.server.SystemServer」
        RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs);
    }
}


3.2.4.2.1closeServerSocket()

由於當前已經不是運行在zygote進程裏了,因此zygote裏的那個監聽socket就應該關閉了。這就是closeServerSocket()的意義,其代碼以下:

static void closeServerSocket() 
{
    try {
        if (sServerSocket != null) {
            FileDescriptor fd = sServerSocket.getFileDescriptor();
            sServerSocket.close();
            if (fd != null) {
                Libcore.os.close(fd);
            }
        }
    } catch (IOException ex) {
        Log.e(TAG, "Zygote:  error closing sockets", ex);
    } catch (libcore.io.ErrnoException ex) {
        Log.e(TAG, "Zygote:  error closing descriptor", ex);
    }
    sServerSocket = null;
}

在handleSystemServerProcess()函數裏,parsedArgs.niceName就是「system_server」,並且由於parsedArgs.invokeWith沒有指定,因此其值爲null,因而程序會走到RuntimeInit.zygoteInit()。

3.2.4.2.2RuntimeInit.zygoteInit()

RuntimeInit.zygoteInit()的代碼以下:
【frameworks/base/core/java/com/android/internal/os/RuntimeInit.java】

public static final void zygoteInit(int targetSdkVersion, String[] argv)
        throws ZygoteInit.MethodAndArgsCaller 
{
    if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
    redirectLogStreams();
    commonInit();
    nativeZygoteInit();
    applicationInit(targetSdkVersion, argv);
}


3.2.4.2.2.1.調用redirectLogStreams()

首先,在新fork出的系統進程裏,須要從新定向系統輸出流。

public static void redirectLogStreams() 
{
    System.out.close();
    System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
    System.err.close();
    System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
}

3.2.4.2.2.2.調用commonInit()


private static final void commonInit() 
{
    . . . . . .
    Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());


    TimezoneGetter.setInstance(new TimezoneGetter() 
    . . . . . .
    . . . . . .
    String trace = SystemProperties.get("ro.kernel.android.tracing");
    . . . . . .
    initialized = true;
}

當前正處於系統進程的主線程中,能夠調用Thread.setDefaultUncaughtExceptionHandler()來設置一個默認的異常處理器,處理程序中的未捕獲異常。其餘的初始化動做,咱們暫不深究。

3.2.4.2.2.3.調用nativeZygoteInit()

接下來調用的nativeZygoteInit()是個JNI函數,在AndroidRuntime.cpp文件中能夠看到:
【frameworks/base/core/jni/AndroidRuntime.cpp】

static JNINativeMethod gMethods[] = {
    { "nativeFinishInit", "()V",
        (void*) com_android_internal_os_RuntimeInit_nativeFinishInit },
    { "nativeZygoteInit", "()V",
        (void*) com_android_internal_os_RuntimeInit_nativeZygoteInit },
    { "nativeSetExitWithoutCleanup", "(Z)V",
        (void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup },
};

nativeZygoteInit()對應的本地函數爲com_android_internal_os_RuntimeInit_nativeZygoteInit()。

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

gCurRuntime是C++層的AndroidRuntime類的靜態變量。在AndroidRuntime構造之時,
gCurRuntime = this。不過實際調用的onZygoteInit()應該是AndroidRuntime的子類AppRuntime的:
【frameworks/base/cmds/app_process/App_main.cpp】

class AppRuntime : public AndroidRuntime
{
    . . . . . .
    virtual void onZygoteInit()
    {
        // Re-enable tracing now that we're no longer in Zygote.
        atrace_set_tracing_enabled(true);


        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();
    }

裏面構造了進程的ProcessState全局對象,並且啓動了線程池。

ProcessState對象是典型的單例模式,它的self()函數以下:

sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) {
        return gProcess;
    }
    gProcess = new ProcessState;
    return gProcess;
}

ProcessState對於Binder通訊機制而言很是重要,如今system server進程的PrecessState算是初始化完畢了。

咱們整理一下思路,畫一張startSystemServer()的調用關係圖:

接下來咱們來說上圖中zygoteInit()調用的最後一行:applicationInit()。

3.2.4.2.2.4.調用applicationInit()

applicationInit()函數的代碼以下:
【frameworks/base/core/java/com/android/internal/os/RuntimeInit.java】

private static void applicationInit(int targetSdkVersion, String[] argv)
        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) {
        Slog.e(TAG, ex.getMessage());
        return;
    }
    invokeStaticMain(args.startClass, args.startArgs);
}

其中的invokeStaticMain()一句最爲關鍵,它承擔向外拋出「特殊異常」的做用。咱們先畫一張startSystemServer()的調用關係圖:

看到了吧,最後一步拋出了異常。這至關於一個「特殊的goto語句」!上面的cl = Class.forName(className)一句,其實加載的就是SystemServer類。這個類名是從前文的parsedArgs.remainingArgs得來的,其值就是「com.android.server.SystemServer」。此處拋出的異常,會被本進程的catch語句接住,在那裏纔會執行SystemServer類的main()函數。示意圖以下:

如上圖所示,新fork出的SystemServer子進程直接跳過了中間那句runSelectLoop(),徑直跳轉到caller.run()一步了。

固然,父進程Zygote在fork動做後,會退出startSystemServer()函數,並走到runSelectLoop(),從而進入一種循環監聽狀態,每當Activity Manager Service向它發出「啓動新應用進程」的命令時,它又會fork一個子進程,並在子進程裏拋出一個異常,這樣子進程仍是會跳轉到catch一句。

咱們能夠把上面的示意圖再豐富一下:

 還有一點須要說明一下,fork出的SystemServer進程在跳轉到catch語句後,會執行SystemServer類的main()函數,而其餘狀況下,fork出的應用進程在跳轉的catch語句後,則會執行ActivityThread類的main()函數。這個ActivityThread對於應用程序而言很是重要,但由於和本篇主題關係不大,咱們就不在這裏展開講了。

3.2.4.3 SystemServer的main()函數

前文咱們已經看到了,startSystemServer()建立的新進程在執行完applicationInit()以後,會拋出一個異常,並由新fork出的SystemServer子進程的catch語句接住,繼而執行SystemServer類的main()函數。

那麼SystemServer的main()函數又在作什麼事情呢?其調用關係圖以下:

在Android4.4版本中,ServerThread已經再也不繼承於Thread了,它如今只是個輔助類,其命名還殘留有舊代碼的味道。在之前的Android版本中,ServerThread的確繼承於Thread,並且在線程的run()成員函數裏,作着相似addService、systemReady的工做。

由於本文主要是闡述zygote進程的,因此咱們就不在這裏繼續細說system server進程啦,有興趣的同窗能夠繼續研究。咱們仍是回過頭繼續說zygote裏的動做吧。

3.2.5監聽zygote socket


3.2.5.1runSelectLoop()

ZygoteInit的main()函數在調用完startSystemServer()以後,會進一步走到runSelectLoop()。runSelectInit()的代碼以下:
【frameworks/base/core/java/com/android/internal/os/ZygoteInit.java】

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;

        if (loopCount <= 0) {
            gc();
            loopCount = GC_LOOP_COUNT;
        } else {
            loopCount--;
        }

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

在一個while循環中,不斷調用selectReadable()。該函數是個native函數,對應C++層的com_android_internal_os_ZygoteInit_selectReadable()。
【frameworks/base/core/jni/com_android_internal_os_ZygoteInit.cpp】

static jint com_android_internal_os_ZygoteInit_selectReadable (JNIEnv *env, jobject clazz, 
                                                                         jobjectArray fds)
{
    . . . . . .
    int err;
    do {
        err = select (nfds, &fdset, NULL, NULL, NULL);
    } while (err < 0 && errno == EINTR);
    . . . . . .
    for (jsize i = 0; i < length; i++) {
        jobject fdObj = env->GetObjectArrayElement(fds, i);
        . . . . . .
        int fd = jniGetFDFromFileDescriptor(env, fdObj);
        . . . . . .
        if (FD_ISSET(fd, &fdset)) {
            return (jint)i;
        }
    }
    return -1;
}

能夠看到,主要就是調用select()而已。在Linux的socket編程中,select()負責監視若干文件描述符的變化狀況,咱們常見的變化狀況有:讀、寫、異常等等。在zygote中,
err = select (nfds, &fdset, NULL, NULL, NULL);一句的最後三個參數都爲NULL,表示該select()操做只打算監視文件描述符的「讀變化」,並且若是沒有可讀的文件,select()就維持阻塞狀態。

在被監視的文件描述符數組(fds)中,第一個文件描述符對應着「zygote接收其餘進程鏈接申請的那個socket(及sServerSocket)」,一旦它發生了變化,咱們就嘗試創建一個ZygoteConnection。

// (index == 0)的狀況
            ZygoteConnection newPeer = acceptCommandPeer();
            peers.add(newPeer);
            fds.add(newPeer.getFileDesciptor());

看到了嗎,新建立的ZygoteConnection會被再次寫入文件描述符數組(fds)。

若是select動做發現文件描述符數組(fds)的其餘文件描述符有東西可讀了,說明有其餘進程經過某個已創建好的ZygoteConnection發來了命令,此時咱們須要調用runOnce()。

// (index > 0)的狀況
            boolean done;
            done = peers.get(index).runOnce();
            if (done) {
                peers.remove(index);
                fds.remove(index);
            }

創建ZygoteConnection的acceptCommandPeer()的代碼以下:

private static ZygoteConnection acceptCommandPeer() {
    try {
        return new ZygoteConnection(sServerSocket.accept());
    } catch (IOException ex) {
        throw new RuntimeException(
                "IOException during accept()", ex);
    }
}
3.2.5.1.1ZygoteConnection的runOnce()

ZygoteConnection的runOnce()代碼截選以下:

boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {

    String args[];
    Arguments parsedArgs = null;
    FileDescriptor[] descriptors;

    . . . . . .
        args = readArgumentList();
        descriptors = mSocket.getAncillaryFileDescriptors();
    . . . . . . 
    int pid = -1;
    FileDescriptor childPipeFd = null;
    FileDescriptor serverPipeFd = null;

    try {
        parsedArgs = new Arguments(args);
        . . . . . .
        pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, 
                parsedArgs.seInfo, parsedArgs.niceName);
    } 
    . . . . . .
        if (pid == 0) {
            // in child
            IoUtils.closeQuietly(serverPipeFd);
            serverPipeFd = null;
            handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
            return true;
        } else {
            // in parent...pid of < 0 means failure
            IoUtils.closeQuietly(childPipeFd);
            childPipeFd = null;
            return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
        }
    . . . . . .
}


3.2.5.1.2readArgumentList()

runOnce()中從socket中讀取參數數據的動做是由readArgumentList()完成的,該函數的代碼以下:

private String[] readArgumentList()
        throws IOException 
{
    int argc;
    . . . . . .
        String s = mSocketReader.readLine();
    . . . . . .
        argc = Integer.parseInt(s);
    . . . . . .
    String[] result = new String[argc];
    for (int i = 0; i < argc; i++) {
        result[i] = mSocketReader.readLine();
        if (result[i] == null) {
            // We got an unexpected EOF.
            throw new IOException("truncated request");
        }
    }
    return result;
}

但是是誰在向這個socket寫入參數的呢?固然是AMS啦。

咱們知道,當AMS須要啓動一個新進程時,會調用相似下面的句子:

Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, null);

包括ActivityThread類名等重要信息的參數,最終就會經過socket傳遞給zygote。

3.2.5.1.3handleChildProc()

runOnce()在讀完參數以後,會進一步調用到handleChildProc()。正如前文所說,該函數會間接拋出特殊的MethodAndArgsCaller異常,只不過此時拋出的異常攜帶的類名爲ActivityThread。

private void handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors, 
                             FileDescriptor pipeFd, PrintStream newStderr)
                             throws ZygoteInit.MethodAndArgsCaller 
{
    closeSocket();
    ZygoteInit.closeServerSocket();
    . . . . . .
    if (parsedArgs.niceName != null) {
        Process.setArgV0(parsedArgs.niceName);
    }

    if (parsedArgs.runtimeInit) {
        if (parsedArgs.invokeWith != null) {
            WrapperInit.execApplication(parsedArgs.invokeWith,
                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
                    pipeFd, parsedArgs.remainingArgs);
        } else {
            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
                    parsedArgs.remainingArgs);
        }
    } else {
        String className;
        . . . . . .
            className = parsedArgs.remainingArgs[0];
        . . . . . .
        String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1];
        System.arraycopy(parsedArgs.remainingArgs, 1,
                mainArgs, 0, mainArgs.length);

        if (parsedArgs.invokeWith != null) {
            WrapperInit.execStandalone(parsedArgs.invokeWith,
                    parsedArgs.classpath, className, mainArgs);
        } else {
            ClassLoader cloader;
            if (parsedArgs.classpath != null) {
                cloader = new PathClassLoader(parsedArgs.classpath,
                        ClassLoader.getSystemClassLoader());
            } else {
                cloader = ClassLoader.getSystemClassLoader();
            }

            try {
                ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
            } catch (RuntimeException ex) {
                logAndPrintError(newStderr, "Error starting.", ex);
            }
        }
    }
}


4小結

至此,zygote進程就闡述完畢了。做爲一個最原始的「受精卵」,它必須在合適的時機進行必要的細胞分裂。分裂動做也沒什麼大的花樣,不過就是fork()新進程而已。若是fork()出的新進程是system server,那麼其最終執行的就是SystemServer類的main()函數,而若是fork()出的新進程是普通的用戶進程的話,那麼其最終執行的就是ActivityThread類的main()函數。有關ActivityThread的細節,咱們有時間再深刻探討,這裏就不細說了。

本篇文章和個人上一篇文章《Android4.4的init進程》能夠算是姊妹篇啦。讀完這兩篇文章,我相信你們對Android的啓動流程能有一些大面上的認識了。

相關文章
相關標籤/搜索