TShopping

 找回密碼
 註冊
搜索
查看: 485|回復: 0

[教學] Android內核解讀-Android系統的開機啟動過程

[複製鏈接]
發表於 2014-4-23 20:14:02 | 顯示全部樓層 |閱讀模式
 
Push to Facebook Push to Plurk Push to Twitter 
前言

當長按手機的power鍵,Android手機就會開機,那麼Android系統的開機啟動過程到底是怎麼樣的呢,本文將要介紹這一過程。簡單來說,Android系統的開機啟動過程大致是這樣的:首先linux系統會啟動一個叫做zygote(可以稱為受精卵、母體)的linux程序,這個程序實際上就是android系統的內核,zygote啟動的時候會建立socket服務端並加載大量的類和資源。接著zygote會孵化第一個dalvik進程SystemServer,在SystemServer中會創建一個socket客戶端,後續AMS(ActivityManagerService)會通過此客戶端和zygote通信,zygote再根據請求孵化出新的dalvik進程即啟動一個新的apk同時把新進程的socket連接關閉。SystemServer初始化完畢後會啟動一個位於棧頂的activity,由於系統剛開機,所以task棧頂沒有activity,於是接著它會發送一個隱式的intent(category:CATEGORY_HOME),也就是launcher了,即Android系統的桌面程序,launcher啟動以後,我們就可以通過桌面啟動各種應用了,可以發現,launcher可以有多個,第三方應用只要加入launcher所需要的intent-filter即可。下面一一分析各個流程。(註:本文分析基於Android4.3源碼)

zygote的啟動過程

zygote是一個linux程序,其對應的可執行文件位於/system/bin/app_process,它在/init.rc中定義,如下

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
     class main
     socket zygote stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
     onrestart restart media
     onrestart restart netd


可以發現,zygote創建了一個流式套接字(即採用TCP協議),並監聽660端口,並且當zygote重啟的時候需要對喚醒電源並重啟Media、netd服務。下面看zygote的源碼,其路徑為frameworks\base\cmds\app_process\app_main.cpp中:


[cpp]


1.int main(int argc, char* const argv[])  
2.{  
3.#ifdef __arm__  
4.    /*
5.     * b/7188322 - Temporarily revert to the compat memory layout
6.     * to avoid breaking third party apps.
7.     *
8.     * THIS WILL GO AWAY IN A FUTURE ANDROID RELEASE.
9.     *
11.     * changes the kernel mapping from bottom up to top-down.
12.     * This breaks some programs which improperly embed
13.     * an out of date copy of Android's linker.
14.     */  
15.    char value[PROPERTY_VALUE_MAX];  
16.    property_get("ro.kernel.qemu", value, "");  
17.    bool is_qemu = (strcmp(value, "1") == 0);  
18.    if ((getenv("NO_ADDR_COMPAT_LAYOUT_FIXUP") == NULL) && !is_qemu) {  
19.        int current = personality(0xFFFFFFFF);  
20.        if ((current & ADDR_COMPAT_LAYOUT) == 0) {  
21.            personality(current | ADDR_COMPAT_LAYOUT);  
22.            setenv("NO_ADDR_COMPAT_LAYOUT_FIXUP", "1", 1);  
23.            execv("/system/bin/app_process", argv);  
24.            return -1;  
25.        }  
26.    }  
27.    unsetenv("NO_ADDR_COMPAT_LAYOUT_FIXUP");  
28.#endif  
29.  
30.    // These are global variables in ProcessState.cpp  
31.    mArgC = argc;  
32.    mArgV = argv;  
33.  
34.    mArgLen = 0;  
35.    for (int i=0; i<argc; i++) {  
36.        mArgLen += strlen(argv) + 1;  
37.    }  
38.    mArgLen--;  
39.    //注意,這裡持有了一個AppRuntime對象,其繼承自AndroidRuntime  
40.    AppRuntime runtime;  
41.    const char* argv0 = argv[0];  
42.  
43.    // Process command line arguments  
44.    // ignore argv[0]  
45.    argc--;  
46.    argv++;  
47.  
48.    // Everything up to '--' or first non '-' arg goes to the vm  
49.  
50.    int i = runtime.addVmArguments(argc, argv);  
51.  
52.    // Parse runtime arguments.  Stop at first unrecognized option.  
53.    bool zygote = false;  
54.    bool startSystemServer = false;  
55.    bool application = false;  
56.    const char* parentDir = NULL;  
57.    const char* niceName = NULL;  
58.    const char* className = NULL;  
59.    //這裡是解析init.rc中定義的zygote的啟動參數  
60.    while (i < argc) {  
61.        const char* arg = argv[i++];  
62.        if (!parentDir) {  
63.            parentDir = arg;  
64.        } else if (strcmp(arg, "--zygote") == 0) {  
65.            zygote = true;  
66.            niceName = "zygote";  
67.        } else if (strcmp(arg, "--start-system-server") == 0) {  
68.            startSystemServer = true;  
69.        } else if (strcmp(arg, "--application") == 0) {  
70.            application = true;  
71.        } else if (strncmp(arg, "--nice-name=", 12) == 0) {  
72.            niceName = arg + 12;  
73.        } else {  
74.            className = arg;  
75.            break;  
76.        }  
77.    }  
78.  
79.    if (niceName && *niceName) {  
80.        setArgv0(argv0, niceName);  
81.        set_process_name(niceName);  
82.    }  
83.  
84.    runtime.mParentDir = parentDir;  
85.  
86.    if (zygote) {  
87.        //從init.rc中的定義可以看出,zygote為true,startSystemServer也為true  
88.        //最終這裡會調用ZygoteInit的main方法  
89.        runtime.start("com.android.internal.os.ZygoteInit",  
90.                startSystemServer ? "start-system-server" : "");  
91.    } else if (className) {  
92.        // Remainder of args get passed to startup class main()  
93.        runtime.mClassName = className;  
94.        runtime.mArgC = argc - i;  
95.        runtime.mArgV = argv + i;  
96.        runtime.start("com.android.internal.os.RuntimeInit",  
97.                application ? "application" : "tool");  
98.    } else {  
99.        fprintf(stderr, "Error: no class name or --zygote supplied.\n");  
100.        app_usage();  
101.        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");  
102.        return 10;  
103.    }  
104.}  
說明:這句代碼runtime.start("com.android.internal.os.ZygoteInit", startSystemServer ? "start-system-server" : "")在AndroidRuntime中實現,其最終會調用ZygoteInit的main方法,請看env->CallStaticVoidMethod(startClass, startMeth, strArray);這裡的startClass就是com.android.internal.os.ZygoteInit,而startMeth就是main,所以,我們直接看ZygoteInit的main方法,代碼路徑為:frameworks\base\core\java\com\android\internal\os\ZygoteInit.java:
[java]


1.public static void main(String argv[]) {  
2.    try {  
3.        // Start profiling the zygote initialization.  
4.        SamplingProfilerIntegration.start();  
5.        //這裡註冊流式socket,以便於fork新的dalvik進程  
6.        registerZygoteSocket();  
7.        EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,  
8.            SystemClock.uptimeMillis());  
9.        //這裡預先加載一些類和資源  
10.        preload();  
11.        EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,  
12.            SystemClock.uptimeMillis());  
13.  
14.        // Finish profiling the zygote initialization.  
15.        SamplingProfilerIntegration.writeZygoteSnapshot();  
16.  
17.        // Do an initial gc to clean up after startup  
18.        gc();  
19.  
20.        // Disable tracing so that forked processes do not inherit stale tracing tags from  
21.        // Zygote.  
22.        Trace.setTracingEnabled(false);  
23.  
24.        // If requested, start system server directly from Zygote  
25.        if (argv.length != 2) {  
26.            throw new RuntimeException(argv[0] + USAGE_STRING);  
27.        }  
28.  
29.        if (argv[1].equals("start-system-server")) {  
30.            //啟動SystemServer,zygote通過SystemServer和上層服務進行交互  
31.            startSystemServer();  
32.        } else if (!argv[1].equals("")) {  
33.            throw new RuntimeException(argv[0] + USAGE_STRING);  
34.        }  
35.  
36.        Log.i(TAG, "Accepting command socket connections");  
37.        //通過Select方式監聽端口,即異步讀取消息,死循環,沒有消息則一直阻塞在那裡  
38.        runSelectLoop();  
39.  
40.        closeServerSocket();  
41.    } catch (MethodAndArgsCaller caller) {  
42.        caller.run();  
43.    } catch (RuntimeException ex) {  
44.        Log.e(TAG, "Zygote died with exception", ex);  
45.        closeServerSocket();  
46.        throw ex;  
47.    }  
48.}  



下面看一下runSelectLoop方法,看看它是如何fork產生一個新的進程的:
[java]
1./**
2. * Runs the zygote process's select loop. Accepts new connections as
3. * they happen, and reads commands from connections one spawn-request's
4. * worth at a time.
5. *
6. * @throws MethodAndArgsCaller in a child process when a main() should
7. * be executed.
8. */  
9.private static void runSelectLoop() throws MethodAndArgsCaller {  
10.    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();  
11.    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();  
12.    FileDescriptor[] fdArray = new FileDescriptor[4];  
13.  
14.    fds.add(sServerSocket.getFileDescriptor());  
15.    peers.add(null);  
16.  
17.    int loopCount = GC_LOOP_COUNT;  
18.    //死循環,沒有消息則一直阻塞在這裡  
19.    while (true) {  
20.        int index;  
21.  
22.        /*
23.         * Call gc() before we block in select().
24.         * It's work that has to be done anyway, and it's better
25.         * to avoid making every child do it.  It will also
26.         * madvise() any free memory as a side-effect.
27.         *
28.         * Don't call it every time, because walking the entire
29.         * heap is a lot of overhead to free a few hundred bytes.
30.         */  
31.        if (loopCount <= 0) {  
32.            gc();  
33.            loopCount = GC_LOOP_COUNT;  
34.        } else {  
35.            loopCount--;  
36.        }  
37.  
38.  
39.        try {  
40.            fdArray = fds.toArray(fdArray);  
41.            //通過select()函數來讀取新的socket消息,其返回值有<0、0、>0三種  
42.            //分別代表:發生異常、繼續讀取新消息、首先處理當前消息  
43.            index = selectReadable(fdArray);  
44.        } catch (IOException ex) {  
45.            throw new RuntimeException("Error in select()", ex);  
46.        }  
47.  
48.        if (index < 0) {  
49.            throw new RuntimeException("Error in select()");  
50.        } else if (index == 0) {  
51.            //構造一個ZygoteConnection對象,並將其加入到peers列表中  
52.            ZygoteConnection newPeer = acceptCommandPeer();  
53.            peers.add(newPeer);  
54.            fds.add(newPeer.getFileDesciptor());  
55.        } else {  
56.            boolean done;  
57.            //這裡處理當前socket消息,ZygoteConnection的runOnce會被調用,一個新的dalvik進程會被創建  
58.            done = peers.get(index).runOnce();  
59.  
60.            if (done) {  
61.                //處理完了以後刪除此socket消息  
62.                peers.remove(index);  
63.                fds.remove(index);  
64.            }  
65.        }  
66.    }  
67.}  

接著,我們還需要看下ZygoteConnection的runOnce方法,看看一個dalvik進程到底是如何產生的,我們知道每個apk都運行在一個獨立的dalvik進程中,所以當啟動一個apk的時候,zygote會孵化出一個新的進程,在這個進程中運行此apk。  在ZygoteConnection中,新進程是通過Zygote的靜態方法forkAndSpecialize來產生的:

pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
     parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo, parsedArgs.niceName);

具體的我們就不用多看了,內部肯定是通過linux系統的fork()函數來產生一個新進程的。當一個新的dalvik進程產生了以後,還需要做一些清場的工作,由於新進程是由zygote程序fork出來的,所以子進程具有zygote的一份拷貝,我們知道,zygote啟動的時候創建了一個socket服務端,這個服務端只能有一個,由zygote孵化的子進程是不應該有的,所以子進程孵化出來以後,還必須關閉拷貝的socket服務端,這些操作在handleChildProc方法中完成:
[java]
1.private void handleChildProc(Arguments parsedArgs,  
2.        FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)  
3.        throws ZygoteInit.MethodAndArgsCaller {  
4.    //關閉本地和服務端(如果有)的socket  
5.    closeSocket();  
6.    ZygoteInit.closeServerSocket();  
7.  
8.    if (descriptors != null) {  
9.        try {  
10.            ZygoteInit.reopenStdio(descriptors[0],  
11.                    descriptors[1], descriptors[2]);  
12.  
13.            for (FileDescriptor fd: descriptors) {  
14.                IoUtils.closeQuietly(fd);  
15.            }  
16.            newStderr = System.err;  
17.        } catch (IOException ex) {  
18.            Log.e(TAG, "Error reopening stdio", ex);  
19.        }  
20.    }  
21.  
22.    if (parsedArgs.niceName != null) {  
23.        Process.setArgV0(parsedArgs.niceName);  
24.    }  
25.  
26.    if (parsedArgs.runtimeInit) {  
27.        if (parsedArgs.invokeWith != null) {  
28.            WrapperInit.execApplication(parsedArgs.invokeWith,  
29.                    parsedArgs.niceName, parsedArgs.targetSdkVersion,  
30.                    pipeFd, parsedArgs.remainingArgs);  
31.        } else {  
32.            RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,  
33.                    parsedArgs.remainingArgs);  
34.        }  
35.    } else {  
36.        String className;  
37.        try {  
38.            className = parsedArgs.remainingArgs[0];  
39.        } catch (ArrayIndexOutOfBoundsException ex) {  
40.            logAndPrintError(newStderr,  
41.                    "Missing required class name argument", null);  
42.            return;  
43.        }  
44.  
45.        String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1];  
46.        System.arraycopy(parsedArgs.remainingArgs, 1,  
47.                mainArgs, 0, mainArgs.length);  
48.  
49.        if (parsedArgs.invokeWith != null) {  
50.            WrapperInit.execStandalone(parsedArgs.invokeWith,  
51.                    parsedArgs.classpath, className, mainArgs);  
52.        } else {  
53.            ClassLoader cloader;  
54.            if (parsedArgs.classpath != null) {  
55.                cloader = new PathClassLoader(parsedArgs.classpath,  
56.                        ClassLoader.getSystemClassLoader());  
57.            } else {  
58.                cloader = ClassLoader.getSystemClassLoader();  
59.            }  
60.  
61.            try {  
62.                //這裡子進程的main方法被調用,此時,子進程完全從zygote(母體)上脫離出來了  
63.                ZygoteInit.invokeStaticMain(cloader, className, mainArgs);  
64.            } catch (RuntimeException ex) {  
65.                logAndPrintError(newStderr, "Error starting.", ex);  
66.            }  
67.        }  
68.    }  
69.}  

同時在ZygoteInit中會預先加載一些類和資源,具體代碼在preload方法中:
    static void preload() {
        preloadClasses();
        preloadResources();
    }SystemServer的創建
SystemServer作為zygote孵化的第一個dalvik進程,其孵化過程在上面已經進行了描述,但是其和普通進程的啟動略有不同,普通進程由Zygote.forkAndSpecialize來啟動,而SystemServer由Zygote.forkSystemServer來啟動,其次是SystemServer內部多創建了一個socket客戶端。關於SystemServer內部的本地socket客戶端,本文前面已經說過,外圍的Service都是通過SystemServer和zygote交互的,比如要啟動一個apk,首先AMS會發起一個新進程的創建請求,在startProcessLocked方法中會調用Process的start方法,其內部會調用startViaZygote方法,而在startViaZygote內部會創建一個本地socket和zygote通信,我們要知道,AMS是在SystemServer進程中創建的,所以說在SystemServer中創建一個本地socket和zygote通信是有道理的。SystemServer的一個很重要的作用是創建各種服務,包括大家常見的WindowManagerService 、AlarmManagerService、ActivityManagerService等,然後上層的各種manager通過binder和service進行交互,關於SystemServer創建各種服務的過程以及和binder的交互,請參考我之前寫的一篇博客的其中一節,這裡就不重複了:點擊打開點擊打開



1.final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {  
2.    // Find the first activity that is not finishing.  
3.    //找到棧頂的activity記錄  
4.    ActivityRecord next = topRunningActivityLocked(null);  
5.  
6.    // Remember how we'll process this pause/resume situation, and ensure  
7.    // that the state is reset however we wind up proceeding.  
8.    final boolean userLeaving = mUserLeaving;  
9.    mUserLeaving = false;  
10.    //由於系統剛啟動,棧頂肯定沒有activity,所以next為null  
11.    if (next == null) {  
12.        // There are no more activities!  Let's just start up the  
13.        // Launcher...  
14.        if (mMainStack) {  
15.            ActivityOptions.abort(options);  
16.            //程序執行到這裡,桌面就會被調起來  
17.            return mService.startHomeActivityLocked(mCurrentUser);  
18.        }  
19.    }  
20.    ...此處省略  
21.}  



最後看看桌面是如何被調起來的:
[java]


1.boolean startHomeActivityLocked(int userId) {  
2.    if (mHeadless) {  
3.        // Added because none of the other calls to ensureBootCompleted seem to fire  
4.        // when running headless.  
5.        ensureBootCompleted();  
6.        return false;  
7.    }  
8.  
9.    if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL  
10.            && mTopAction == null) {  
11.        // We are running in factory test mode, but unable to find  
12.        // the factory test app, so just sit around displaying the  
13.        // error message and don't try to start anything.  
14.        return false;  
15.    }  
16.    Intent intent = new Intent(  
17.        mTopAction,  
18.        mTopData != null ? Uri.parse(mTopData) : null);  
19.    intent.setComponent(mTopComponent);  
20.    if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {  
21.        //其實就是為intent加上CATEGORY_HOME這個Category,接著就發送隱式intent來調起所有滿足條件的桌面  
22.        //這也是第三方桌面存在的價值  
23.        intent.addCategory(Intent.CATEGORY_HOME);  
24.    }  
25.    ActivityInfo aInfo =  
26.        resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);  
27.    if (aInfo != null) {  
28.        intent.setComponent(new ComponentName(  
29.                aInfo.applicationInfo.packageName, aInfo.name));  
30.        // Don't do this if the home app is currently being  
31.        // instrumented.  
32.        aInfo = new ActivityInfo(aInfo);  
33.        aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);  
34.        ProcessRecord app = getProcessRecordLocked(aInfo.processName,  
35.                aInfo.applicationInfo.uid);  
36.        if (app == null || app.instrumentationClass == null) {  
37.            intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);  
38.            //這裡啟動桌面activity,到此為止,桌面被啟動了,我們就可以認為手機開機完成了  
39.            mMainStack.startActivityLocked(null, intent, null, aInfo,  
40.                    null, null, 0, 0, 0, null, 0, null, false, null);  
41.        }  
42.    }  
43.  
44.    return true;  
45.}  

到此為止,桌面已經啟動了,也就意味著手機的開機啟動過程已經完成,後續我們就可以通過桌面來啟動各個應用了,根據本文的介紹,我們已經知道apk啟動時dalvik進程的創建過程,關於單個activity的啟動過程,請參看我之前寫的另一篇文章Android源碼分析-Activity的啟動過程

。到此為止,本文結束了,相信大家對Android系統的開機啟動過程應該有了一個感性的認識了。


 

臉書網友討論
您需要登錄後才可以回帖 登錄 | 註冊 |

本版積分規則



Archiver|手機版|小黑屋|免責聲明|TShopping

GMT+8, 2016-12-9 23:33 , Processed in 0.059870 second(s), 22 queries .

本論壇言論純屬發表者個人意見,與 TShopping綜合論壇 立場無關 如有意見侵犯了您的權益 請寫信聯絡我們。

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回復 返回頂部 返回列表