源碼分析-Zygote進(jìn)程啟動到Launcher

上兩篇聊了Launcher之后的流程褪那,現(xiàn)在咱們再看一下之前的流程 --- 也就是Zygote進(jìn)程啟動之后:


無標(biāo)題.png

Zygote, 意為“受精卵”,安卓進(jìn)程孵化器
上圖可以看出隔嫡,由linux kernel 的 init (一生萬物)進(jìn)程可以啟動Zygote進(jìn)程,Zygote又啟動dalvik书在,初始化java環(huán)境灰伟,app需要的資源,系統(tǒng)服務(wù)... fork(孵化)桌面Launcher以及各個app進(jìn)程

在系統(tǒng)啟動腳本system/core/rootdir/init.rc文件中儒旬,我們可以看到啟動Zygote進(jìn)程的腳本命令:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server  
    socket zygote stream 666   # zygote需要一個套接字
    onrestart write /sys/android_power/request_state wake  # zygote重啟的話栏账,需要執(zhí)行這個操作
    onrestart write /sys/power/state on  
    onrestart restart media  
    onrestart restart netd  

app_process對應(yīng)的源碼在frameworks/base/cmds/app_process目錄下,其入口函數(shù)main在文件app_main.cpp中:

/*
* 啟動zygote的方式為/system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
* 所以 argc == 5
*      argv里頭存的就是這5個參數(shù)argv[0]=="/system/bin/app_process" ,argv[1] == "-Xzygote"....
*/
int main(int argc, char* const argv[])
{
    ......
    // These are global variables in ProcessState.cpp
    mArgC = argc;
    mArgV = argv;

    mArgLen = 0;
    for (int i=0; i<argc; i++) {
        mArgLen += strlen(argv[i]) + 1;
    }
    mArgLen--;
    // 以上代碼主要是將參數(shù)相關(guān)信息保存到全局變量中

    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); // 這個函數(shù)會返回1栈源,表示只處理了-Xzytote這一個參數(shù)挡爵,所謂的處理實(shí)際上就是將這個參數(shù)添加到了runtime對象的mOptions 變量中。

    // Parse runtime arguments.  Stop at first unrecognized option.
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    const char* parentDir = NULL;
    const char* niceName = NULL;
    const char* className = NULL;
    // 進(jìn)入循環(huán)之前 i == 1, argc == 4  argv 指向“-Xzygote” 
    while (i < argc) {
        const char* arg = argv[i++];
        if (!parentDir) {
            parentDir = arg;  // parentDir被賦值為"/system/bin"
        } else if (strcmp(arg, "--zygote") == 0) {  
            zygote = true;
            niceName = "zygote"; // 進(jìn)程名
        } 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) {  // 設(shè)置進(jìn)程名
        setArgv0(argv0, niceName);
        set_process_name(niceName);
    }

    runtime.mParentDir = parentDir;

    if (zygote) { // 走這個分支
        runtime.start("com.android.internal.os.ZygoteInit",
                startSystemServer ? "start-system-server" : "");
    } else if (className) {
        // Remainder of args get passed to startup class main()
        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;
    }
}

main函數(shù)主要就是創(chuàng)建了runtime實(shí)例甚垦,并且解析參數(shù)茶鹃,然后調(diào)用runtime的start函數(shù),接著我們分析AppRuntime的start函數(shù):

/*
 * Start the Android runtime.  This involves starting the virtual machine
 * and calling the "static void main(String[] args)" method in the class
 * named by "className".
 *
 * Passes the main function two arguments, the class name and the specified
 * options string.
 */
 // 首先我們明確下傳進(jìn)來的參數(shù)  className == "com.android.internal.os.ZygoteInit"  options == "start-system-server"
void AndroidRuntime::start(const char* className, const char* options)
{
    ALOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
            className != NULL ? className : "(unknown)");

    /*
     * 'startSystemServer == true' means runtime is obsolete and not run from
     * init.rc anymore, so we print out the boot start event here.
     */
    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)));
    }

    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);  //配置ANDROID_ROOT環(huán)境變量
    }

    //const char* kernelHack = getenv("LD_ASSUME_KERNEL");
    //ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
    //1:調(diào)用startVm函數(shù)創(chuàng)建虛擬機(jī)艰亮;
    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env) != 0) { // 創(chuàng)建虛擬機(jī)
        return;
    }
    onVmCreated(env);
    //2:調(diào)用startReg函數(shù)注冊Android Natvie函數(shù)闭翩;
    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    /*
     * We want to call main() with a String array with arguments in it.
     * At present we have two arguments, the class name and an option string.
     * Create an array to hold them.
     */
    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);

    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */
    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 {
            //3:讓虛擬機(jī)去執(zhí)行com.android.internal.os.ZygoteInit的main函數(shù)。
            /* 調(diào)用com.android.internal.os.ZygoteInit的main函數(shù)垃杖,strArray是參數(shù)男杈,數(shù)組里面有兩個元素,
            className == "com.android.internal.os.ZygoteInit"  options == "start-system-server" */
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

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

    ALOGD("Shutting down VM\n");
    if (mJavaVM->DetachCurrentThread() != JNI_OK)
        ALOGW("Warning: unable to detach main thread\n");
    if (mJavaVM->DestroyJavaVM() != 0)
        ALOGW("Warning: VM did not shut down cleanly\n");
}

start函數(shù)主要做了以下幾件事情:
1:調(diào)用startVm函數(shù)創(chuàng)建虛擬機(jī);
2:調(diào)用startReg函數(shù)注冊Android Natvie函數(shù)调俘;
3:讓虛擬機(jī)去執(zhí)行com.android.internal.os.ZygoteInit的main函數(shù)伶棒。

public static void main(String argv[]) {
        try {
            // Start profiling the zygote initialization.
            SamplingProfilerIntegration.start();
            // 1、創(chuàng)建一個套接字彩库,用于監(jiān)聽ams發(fā)過來的fork請求
            registerZygoteSocket(); 
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                SystemClock.uptimeMillis());
            preload(); 
            // 2肤无、加載classes 和resources, 后面會詳細(xì)分析
            EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                SystemClock.uptimeMillis());

            // Finish profiling the zygote initialization.
            SamplingProfilerIntegration.writeZygoteSnapshot();

            // Do an initial gc to clean up after startup
            gc();

            // 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")) {
                //3、 創(chuàng)建system server進(jìn)程骇钦,ams wms pms等常見service都在該進(jìn)程里面
                startSystemServer(); 
            } else if (!argv[1].equals("")) {
                throw new RuntimeException(argv[0] + USAGE_STRING);
            }

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

            if (ZYGOTE_FORK_MODE) {
                runForkMode();
            } else {
                // 4宛渐、進(jìn)入循環(huán)監(jiān)聽模式,監(jiān)聽外來請求
                runSelectLoopMode(); 
            }

            closeServerSocket();
        } catch (MethodAndArgsCaller caller) {
            caller.run();
        } catch (RuntimeException ex) {
            Log.e(TAG, "Zygote died with exception", ex);
            closeServerSocket();
            throw ex;
        }
    }

com.android.internal.os.ZygoteInit的main函數(shù)主要做了四件事情:

1:調(diào)用registerZygoteSocket()創(chuàng)建一個套接字眯搭,用于監(jiān)聽ams發(fā)過來的fork請求窥翩,如下:
private static void registerZygoteSocket(String socketName) {
    if (sServerSocket == null) {
        int fileDesc;
        //此處的socket name,就是zygote
        final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
        try {
            //記得么鳞仙?在init.zygote.rc被加載時寇蚊,就會創(chuàng)建一個名為zygote的socket
            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();
            //獲取zygote socket的文件描述符
            fd.setInt$(fileDesc);
            //將zygote socket包裝成一個server socket
            sServerSocket = new LocalServerSocket(fd);
        } catch (IOException ex) {
            throw new RuntimeException("Error binding to local socket '" + fileDesc + "'", ex);
        }
    }
}

2:調(diào)用preload()預(yù)加載classes 和resources,如下棍好;

static void preload() {
    Log.d(TAG, "begin preload");
    //讀取文件framework/base/preloaded-classes仗岸,然后通過反射加載對應(yīng)的類
    //需要加載數(shù)千個類允耿,啟動慢的原因之一
    preloadClasses();
    //負(fù)載加載一些常用的系統(tǒng)資源
    preloadResources();
    //圖形相關(guān)的
    preloadOpenGL();
    //一些必要庫
    preloadSharedLibraries();
    //好像是語言相關(guān)的字符信息
    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");
}

3:調(diào)用startSystemServer()創(chuàng)建system server進(jìn)程,ams wms pms等常見service都在該進(jìn)程里面扒怖,如下较锡;

private static boolean startSystemServer(String abiList, String socketName) {
    //準(zhǔn)備capabilities參數(shù)
    ........
    String args[] = {
        "--setuid=1000",
        "--setgid=1000",
        "--setgroups=.........",
        "--capabilities=" + capabilities + "," + capabilities,
        "--nice-name=system_server",
        "--runtime-args",
        "com.android.server.SystemServer",
    };
    ZygoteConnection.Arguments parsedArgs = null;

    int pid;

    try {
        //將上面準(zhǔn)備的參數(shù),按照ZygoteConnection的風(fēng)格進(jìn)行封裝
        parsedArgs = new ZygoteConnection.Arguments(args);
        ...........

        //通過fork"分裂"出system server
        /* Request to fork the system server process */
        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) {
        ............
        //pid = 0, 在進(jìn)程system server中
        //system server進(jìn)程處理自己的工作
        handleSystemServerProcess(parsedArgs);
    }

    return true;
}

4:調(diào)用runSelectLoopMode()進(jìn)入循環(huán)監(jiān)聽模式盗痒,監(jiān)聽外來請求蚂蕴,如下。

private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();

    //首先將server socket加入到fds
    fds.add(sServerSocket.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);
            //關(guān)注事件到來
            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;
            }
            //server socket最先加入fds积糯, 因此這里是server socket收到數(shù)據(jù)
            if (i == 0) {
                //收到新的建立通信的請求掂墓,建立通信連接
                ZygoteConnection newPeer = acceptCommandPeer(abiList);
                //加入到peers和fds
                peers.add(newPeer);
                fds.add(newPeer.getFileDesciptor());
             } else {
                //其它通信連接收到數(shù)據(jù),runOnce執(zhí)行對應(yīng)命令
                boolean done = peers.get(i).runOnce();
                if (done) {
                    //對應(yīng)通信連接不再需要執(zhí)行其它命令看成,關(guān)閉并移除
                    peers.remove(i);
                    fds.remove(i);
                }
            }
        }
    }
}

從上面代碼可知君编,初始時,fds中僅有server socket川慌,因此當(dāng)有數(shù)據(jù)到來時吃嘿,將執(zhí)行i ==0 的部分。
此時梦重,顯然是需要創(chuàng)建新的通信連接兑燥,因此acceptCommandPeer將被調(diào)用。

private static ZygoteConnection acceptCommandPeer(String abiList) {
    try {
        return new ZygoteConnection(sServerSocket.accept(), abiList);
    } catch (IOException ex) {
        throw new RuntimeException("IOException during accept()", ex);
    }
}
總結(jié)
1:acceptCommandPeer封裝了socket的accpet函數(shù)琴拧。于是我們知道降瞳,對應(yīng)的新的連接,zygote將會創(chuàng)建出一個新的socket與其通信蚓胸,并將該socket加入到fds中挣饥。因此,一旦通信連接建立后沛膳,fds中將會包含有多個socket扔枫。
2:當(dāng)poll監(jiān)聽到這一組sockets上有數(shù)據(jù)到來時,就會從阻塞中恢復(fù)锹安。于是短荐,我們需要判斷到底是哪個socket收到了數(shù)據(jù)。
3:在runSelectLoop中采用倒序的方式輪詢叹哭,由于server socket第一個被加入到fds忍宋,因此最后輪詢到的socket才需要處理新建連接的操作;其它socket收到數(shù)據(jù)時风罩,僅需要調(diào)用zygoteConnection的runonce函數(shù)執(zhí)行數(shù)據(jù)對應(yīng)的操作讶踪。
4:若一個連接處理完所有對應(yīng)消息后,該連接對應(yīng)的socket和連接等將被移除泊交。

那么從 system server 到 ActivityManager 到 Launcher 又是如何溝通的呢乳讥,也就是第一篇提到的
AMS.startProcessLocked()-->Process.start()-->zygoteSendArgsAndGetResult()-->ZygoteInit.invokeStaticMain(cloader, className, mainArgs)(ActivityThread.main)
system server,具體的過程在介紹system server時再分析

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末廓俭,一起剝皮案震驚了整個濱河市云石,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌研乒,老刑警劉巖汹忠,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異雹熬,居然都是意外死亡宽菜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門竿报,熙熙樓的掌柜王于貴愁眉苦臉地迎上來铅乡,“玉大人,你說我怎么就攤上這事烈菌≌笮遥” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵芽世,是天一觀的道長挚赊。 經(jīng)常有香客問我,道長济瓢,這世上最難降的妖魔是什么荠割? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮旺矾,結(jié)果婚禮上蔑鹦,老公的妹妹穿的比我還像新娘。我一直安慰自己宠漩,他們只是感情好举反,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扒吁,像睡著了一般火鼻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上雕崩,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天魁索,我揣著相機(jī)與錄音,去河邊找鬼盼铁。 笑死粗蔚,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的饶火。 我是一名探鬼主播鹏控,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼致扯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了当辐?” 一聲冷哼從身側(cè)響起抖僵,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缘揪,沒想到半個月后耍群,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡找筝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年蹈垢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袖裕。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡曹抬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出陆赋,到底是詐尸還是另有隱情沐祷,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布攒岛,位于F島的核電站赖临,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏灾锯。R本人自食惡果不足惜兢榨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望顺饮。 院中可真熱鬧吵聪,春花似錦、人聲如沸兼雄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赦肋。三九已至块攒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間佃乘,已是汗流浹背囱井。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留趣避,地道東北人庞呕。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親住练。 傳聞我的和親對象是個殘疾皇子地啰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容