Android開機啓動過程分析

首先android是基於Linux的內核,只有先加載了kernel才能啓動安卓,對於Linux來說android只是其上的一個應用程序。Android的啓動大致可以形象的劃分爲三個過程:

Init->init.rc->zygote從事嵌入式開發的人都知道,Linux加載完內核驅動後會掛載‘/’根文件系統,掛載完成後會執行‘/init’二進制程序,這也是內核啓動後執行的第一個用戶程序,android裏面也是這樣。這個程序的main函數位於android/system/core/init/init.c中,作爲一個操作系統,初始化一般要完成以下幾個工作:

1.創建需要的目錄,掛載文件系統,輸入和讀取文件硬盤數據。

2.裝載和設定全局的環境變量,爲程序的運行搭建好必要的環境。

3.對於android來說,還需要運行Java虛擬機,這是安卓特有的跨平臺特性。

4.加載和運行Framework框架,加載窗口桌面程序,也就是系統的GUI,最後將控制權交給用戶後算啓動完成。

根文件“/init”程序分析:

源碼可以看出,init首先會創建一些必需的目錄,如‘/dev’、‘/proc’、‘/sys’等。然後將設備mount到該目錄下,mount完成後纔可以創建設備節點。例如:

<span style="white-space:pre">	</span>mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
<span style="white-space:pre">	</span>mount("proc", "/proc", "proc", 0, NULL);
<span style="white-space:pre">	</span>mount("sysfs", "/sys", "sysfs", 0, NULL);

創建串口輸出節點,串口重定向輸出:

open_devnull_stdio();

klog_init函數中創建了"/dev/__kmsg__"節點,之後很快unlink掉了,該函數複製了一份串口輸出日誌,日誌包括從kernel啓動到android初始化之間的打印信息,通過dmesg命令就可以打印出來。

 

其中還有如下代碼:

property_init();該函數主要是加載一些默認的property屬性,這些屬性存在於build.propdefault.prop文件中。

get_hardware_name(hardware, &revision);該函數用於從"/proc/cpuinfo"節點中獲取cpu的硬件和版本信息,將這些信息寫入prop屬性變量中。

process_kernel_cmdline();從節點“/proc/cmdline”中獲取bootargs的環境變量,並設置到全局變量中以便後續使用。

 

在程序中系統會讀取和解析init.rc文件,這個文件中更像是一些命令集合,但是程序並沒有立即執行這些命令。而是先解析出來,按照一定規則進行整理放入一個鏈表中,init.rc中的命令並不是像shell一樣是一個直接的執行命令,而是有一個內部的映射,這些映射存在於init/keywords.h文件中,例如:

KEYWORD(mkdir,       COMMAND, 1, do_mkdir)

KEYWORD(mount,       COMMAND, 3, do_mount)

KEYWORD(rm,          COMMAND, 1, do_rm);

KEYWORD(rmdir,       COMMAND, 1, do_rmdir);

mkdirrc中的命令,但do_mkdir纔是真正的命令執行實體。

init的後面有一個for循環,命令的執行是在其中完成的:
    for(;;) {

        execute_one_command();

        restart_processes();

        。。。。。。。。。。。。。。。

}其中execute_one_command會將命令提取出來,一條接一條的進行執行。命令中如果註冊了一個service,那麼它如果執行失敗了,也還可以進行重新啓動執行。

Service往往是單獨的進程進行執行的,這些程序執行成功與否是系統關心的問題,系統需要監管這些進程,所以註冊了一個信號處理函數handle_signal。如果子進程出現錯誤比如內存溢出等,這時會觸發一個信號量,父進程在接收到該信號量後會到handle_signal中進行處理,父進程的有效管理,使得這些進程避免變成“野進程”或者“殭屍進程”。信號處理函數wait_for_one_process中,如果等待的進程程序出錯或者超時會有“waitpid returned pid %d, status = %08x\”的打印,平時如果某個service出錯反覆執行時就會有該打印。直到所有的service成功啓動後,init纔會正常退出for循環。

Init.rc初始化文件分析:

分析init.rc文件,通過該文件可以知道系統都做了哪些事情,比如其中的幾個關鍵命令:

on property:ro.debuggable=1

start console

這種語句表示系統會讀取prop屬性(全局註冊表)中的值,如果property:ro.debuggable=1則執行條件以下的語句start console,所以打開android的打印串口是在這地方做的。

sysclktz 0    

設置時區。

loglevel 3

設置log等級。

on property:ro.kernel.qemu=1

    start adbd

根據build.prop中的該屬性設置決定是否啓動遠程調試adbd

service bootanim /system/bin/bootanimation

啓動開機動畫。

如果留意,Init.rc中會有一個以on開頭後面跟一個字段的關鍵字,如:

on early-init  這種關鍵字和上面的prop後面跟着一個值的方式不相同。它主要用於表示android的啓動階段,init在加載這些命令時並不是按照從文件頭到文件尾的方式,而是尋找這些關鍵字,按照啓動順序依次加載,執行時也按照這個順序執行,例如這些階段還有:

early-initinitearly-fsearly-bootboot等。

init.rc中還有一個很關鍵的階段字,代碼如下:
on emmc-fs

mount ext4 ext4@system /system ro

 mount ext4 ext4@userdata /data nosuid nodev

mount ext4 ext4@cache /cache nosuid nodev

這幾條命令的意思是將@system分區,以ext4的文件系統格式掛載到根目錄/system下,ro表示只讀的意思。emmc-fs階段要開始於其它的階段,因爲只有將系統的核心目錄掛載了,才能進行後續操作。Init.rc除了支持emmc,還支持nand,決定用哪一個是系統自動識別完成的,代碼如下:  

       if ( check_flash_type() == NAND_TYPE) {

            action_for_each_trigger("fs", action_add_queue_tail);

        }

        else if ( check_flash_type() == EMMC_TYPE) {

            action_for_each_trigger("emmc-fs", action_add_queue_tail);

        }

通過檢查bootargs中的關鍵字“hinand”或者“mmcblk”知道flash的類型。

rc文件中有一個核心的關鍵字service,使用該關鍵字就是要把其後的命令擴展爲一個服務,這個關鍵字的規則也是最複雜的,例如下面這條命令:

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

這條命令使用service指令告訴系統將zygote加入到系統服務中,service的語法爲:

service service_name 可執行程序路徑 可執行程序的入口參數

/system/bin/app_process爲實際可執行的程序,後面‘-’爲執行的參數。socket用於服務所使用到的socket,後面參數依次爲名稱、類型、端口、地址。onrestart命令指定該服務重啓的條件,即當滿足這些條件後,zygote服務就需要重啓;當然這些都是一些異常條件,也就是說如果media或者netd發生異常重啓時,zgote就需要重啓。

如果sevices中有oneshot關鍵字,則表示該service只執行一次,例如:

service bootanim /system/bin/bootanimation

    class main

    user root

    group graphics

    disabled

    oneshot

表示開機動畫執行一次,disabled表示如果出錯或超時不會再次執行,更多具體的命令可參照init.rc文件。

Zygote卵孵化器分析:

從上面的分析可以看出,到目前爲止都沒有真正涉及到android的東西,如果沒有zygote那麼安卓頂多算一個linux系統。Zygote進程是所有APK應用的父進程,其它進程都是由該進程孵化產生的,所以將其擬化爲一個孵化器。通過android系統的虛擬機可以加載Java的開發環境,從功能上來說,Java語言更類似於ShellBase語言(屬於僞代碼),與硬件打交道的任務交給了虛擬機去完成。虛擬機的任務就是屏蔽平臺的差異屬性,使得同一份Java語言可以同時運行於X86平臺和ARM平臺。Java語言另外的優勢是面向對象,在應用開發方面比C語言更好用,缺點是運行效率比C差。

Zygote的實際執行程序是/system/bin/app_processapp_process的代碼位於 frameworks/base/cmds/app_process/app_main.cpp中。

分析app_main.cpp的代碼,app_process會在AndroidRuntime.startVm函數中創建第一個虛擬機:

if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {

        ALOGE("JNI_CreateJavaVM failed\n");

        goto bail;

    }

同時app_process啓動後會裝載與framework的相關類以及resource資源,檢查/system/framework/下的jar包,將其中的jar文件通過dexopt優化變爲dex文件,同時安裝到/data/dalvik-cache/目錄中,這樣下一次執行時直接到該目錄下尋找。最後啓動兩個核心的類ZygoteInit.javaSystemServer.java類,這兩個類都是以兩個單獨的進程啓動的。SystemServer進程是Android系統的神經中樞,安卓應用直接交互的大部分系統服務都是在該進程中運行的,最關鍵的有:WindowManagerServer(Wms)ActivityManagerSystemServiceAmS)、PackageManagerServer(PmS)等,這些服務都是在該進程中以線程的方式啓動的。

PackagemanagerService主要用來管理apk,該服務會解析apk中的組件,例如ActivieyService等。系統初始化時會遍歷/system/app//data/app/目錄,將apk以包名的形式拷貝到/data/data/<pkgName>目錄下,將apk中的class文件保存到/data/dalvik-cache/目錄下,同時以相應的apk進行命名。服務利用PackageParser類解析apk中的AndroidManifest.xml文件獲取包的一些信息,並將這些信息保存到/data/system/packages.xmlpackages.list中,以便系統後續使用。這些信息應該只會讀取一次,下次啓動後會檢查是否有更新?如果沒有就使用上一次的文件。

第一次開機時該服務還會去創建一些目錄,如/data/目錄下的dataapp-asecapp-libapp-private等。同時解析/system/etc/permissions/platform.xml文件,知道系統都定義了哪些系統權限以及該權限對應可執行程序的uid。當用戶安裝應用時會彈出一個應用要求的權限,這些權限都是在這裏定義的。從以上的步驟可以看出,第一次開機會做很多工作,這也就是爲什麼android系統第一次啓動都會很慢的原因。

啓動第一個Activity

當所有的線程服務都啓動完成後,其中的ActivityManagerService(AmS)服務會去檢測其它服務是否完成,這個是通過調用systemReady()函數來完成的,最後會執行如下代碼:

mMainStack.resumeTopActivityLocked(null);

也就是說AmS會執行最頂層的TopActivity,但是第一次開機TopActivity是沒有的,這時候就會有:

if (next == null) {

            // There are no more activities!  Let's just start up the

            // Launcher...

            if (mMainStack) {

                ActivityOptions.abort(options);

                return mService.startHomeActivityLocked(mCurrentUser);

            }

        }

也就是去加載HomeActivityAndroid不像其它系統一樣,將一個固定的Activity作爲主界面程序加載,而是在AmSstarHomeActiviyLocked()中,系統發出一個catagory字段包含CATEGORY_HOMEintent。如下:

intent.setComponent(mTopComponent);

        if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {

            intent.addCategory(Intent.CATEGORY_HOME);

        }

無論是哪個應用程序,只要聲明自己爲該類型後,那就可以被認爲是Home程序,系統並沒有選取任何一個“Home”程序,而是將這個權利交給了用戶,用戶的選擇決定了系統啓動的Home,這個就是第一個啓動的Acivity(當用戶使用安卓系統啓動後,會有一個選取界面的操作框,通過按鍵用戶可以選取合適的Activity作爲主界面)。當AmS要啓動一個activity時,需要根據intent攜帶的activity名稱,調用內部的PackageManager查詢該名稱對應的具體信息,如果存在就啓動activity,否則返回失敗。

當系統的Lancher界面啓動呈現完成後,系統的啓動也就算完成了。

【本代碼基於android4.2源碼進行分析】

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