首先android是基于Linux的內核,只有先加載了kernel才能啟動安卓,對Linux來講android只是其上的1個利用程序。Android的啟動大致可以形象的劃分為3個進程:
Init->init.rc->zygote。從事嵌入式開發的人都知道,Linux加載完內核驅動后會掛載‘/’根文件系統,掛載完成后會履行‘/init’2進制程序,這也是內核啟動后履行的第1個用戶程序,android里面也是這樣。這個程序的main函數位于android/system/core/init/init.c中,作為1個操作系統,初始化1般要完成以下幾個工作:
1.創建需要的目錄,掛載文件系統,輸入和讀取文件硬盤數據。
2.裝載和設定全局的環境變量,為程序的運行搭建好必要的環境。
3.對android來講,還需要運行Java虛擬機,這是安卓獨有的跨平臺特性。
4.加載和運行Framework框架,加載窗口桌面程序,也就是系統的GUI,最后將控制權交給用戶后算啟動完成。
源碼可以看出,init首先會創建1些必須的目錄,如‘/dev’、‘/proc’、‘/sys’等。然后將裝備mount到該目錄下,mount完成后才可以創建裝備節點。例如:
創建串口輸出節點,串口重定向輸出:
open_devnull_stdio();
在klog_init函數中創建了"/dev/__kmsg__"節點,以后很快unlink掉了,該函數復制了1份串口輸出日志,日志包括從kernel啟動到android初始化之間的打印信息,通過dmesg命令就能夠打印出來。
其中還有以下代碼:
property_init();該函數主要是加載1些默許的property屬性,這些屬性存在于build.prop,default.prop文件中。
get_hardware_name(hardware, &revision);該函數用于從"/proc/cpuinfo"節點中獲得cpu的硬件和版本信息,將這些信息寫入prop屬性變量中。
process_kernel_cmdline();從節點“/proc/cmdline”中獲得bootargs的環境變量,并設置到全局變量中以便后續使用。
在程序中系統會讀取和解析init.rc文件,這個文件中更像是1些命令集合,但是程序并沒有立即履行這些命令。而是先解析出來,依照1定規則進行整理放入1個鏈表中,init.rc中的命令其實不是像shell1樣是1個直接的履行命令,而是有1個內部的映照,這些映照存在于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);
mkdir是rc中的命令,但do_mkdir才是真實的命令履行實體。
在init的后面有1個for循環,命令的履行是在其中完成的:
for(;;) {
execute_one_command();
restart_processes();
。。。。。。。。。。。。。。。
}其中execute_one_command會將命令提取出來,1條接1條的進行履行。命令中如果注冊了1個service,那末它如果履行失敗了,也還可以進行重新啟動履行。
Service常常是單獨的進程進行履行的,這些程序履行成功與否是系統關心的問題,系統需要監管這些進程,所以注冊了1個信號處理函數handle_signal。如果子進程出現毛病比如內存溢出等,這時候會觸發1個信號量,父進程在接收到該信號量后會到handle_signal中進行處理,父進程的有效管理,使得這些進程避免變成“野進程”或“僵尸進程”。信號處理函數wait_for_one_process中,如果等待的進程程序出錯或超時會有“waitpid returned pid %d, status = %08x”的打印,平時如果某個service出錯反復履行時就會有該打印。直到所有的service成功啟動后,init才會正常退出for循環。
分析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中會有1個以on開頭后面跟1個字段的關鍵字,如:
on early-init 這類關鍵字和上面的prop后面隨著1個值的方式不相同。它主要用于表示android的啟動階段,init在加載這些命令時其實不是依照從文件頭到文件尾的方式,而是尋覓這些關鍵字,依照啟動順序順次加載,履行時也依照這個順序履行,例如這些階段還有:
early-init、init、early-fs、early-boot、boot等。
在init.rc中還有1個很關鍵的階段字,代碼以下:
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文件中有1個核心的關鍵字service,使用該關鍵字就是要把其后的命令擴大為1個服務,這個關鍵字的規則也是最復雜的,例以下面這條命令:
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服務就需要重啟;固然這些都是1些異常條件,也就是說如果media或netd產生異常重啟時,zgote就需要重啟。
如果sevices中有oneshot關鍵字,則表示該service只履行1次,例如:
service bootanim /system/bin/bootanimation
class main
user root
group graphics
disabled
oneshot
表示開機動畫履行1次,disabled表示如果出錯或超時不會再次履行,更多具體的命令可參照init.rc文件。
從上面的分析可以看出,到目前為止都沒有真正觸及到android的東西,如果沒有zygote那末安卓頂多算1個linux系統。Zygote進程是所有APK利用的父進程,其它進程都是由該進程孵化產生的,所以將其擬化為1個孵化器。通過android系統的虛擬機可以加載Java的開發環境,從功能上來講,Java語言更類似于Shell和Base語言(屬于偽代碼),與硬件打交道的任務交給了虛擬機去完成。虛擬機的任務就是屏蔽平臺的差異屬性,使得同1份Java語言可以同時運行于X86平臺和ARM平臺。Java語言另外的優勢是面向對象,在利用開發方面比C語言更好用,缺點是運行效力比C差。
Zygote的實際履行程序是/system/bin/app_process,app_process的代碼位于 frameworks/base/cmds/app_process/app_main.cpp中。
分析app_main.cpp的代碼,app_process會在AndroidRuntime.startVm函數中創建第1個虛擬機:
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
ALOGE("JNI_CreateJavaVM failed ");
goto bail;
}
同時app_process啟動后會裝載與framework的相干類和resource資源,檢查/system/framework/下的jar包,將其中的jar文件通過dexopt優化變成dex文件,同時安裝到/data/dalvik-cache/目錄中,這樣下1次履行時直接到該目錄下尋覓。最后啟動兩個核心的類ZygoteInit.java和SystemServer.java類,這兩個類都是以兩個單獨的進程啟動的。SystemServer進程是Android系統的神經中樞,安卓利用直接交互的大部份系統服務都是在該進程中運行的,最關鍵的有:WindowManagerServer(Wms)、ActivityManagerSystemService(AmS)、PackageManagerServer(PmS)等,這些服務都是在該進程中以線程的方式啟動的。
PackagemanagerService主要用來管理apk,該服務會解析apk中的組件,例如Activiey、Service等。系統初始化時會遍歷/system/app/和/data/app/目錄,將apk以包名的情勢拷貝到/data/data/<pkgName>目錄下,將apk中的class文件保存到/data/dalvik-cache/目錄下,同時以相應的apk進行命名。服務利用PackageParser類解析apk中的AndroidManifest.xml文件獲得包的1些信息,并將這些信息保存到/data/system/packages.xml和packages.list中,以便系統后續使用。這些信息應當只會讀取1次,下次啟動后會檢查是不是有更新?如果沒有就使用上1次的文件。
第1次開機時該服務還會去創建1些目錄,如/data/目錄下的data、app-asec、app-lib、app-private等。同時解析/system/etc/permissions/platform.xml文件,知道系統都定義了哪些系統權限和該權限對應可履行程序的uid。當用戶安裝利用時會彈出1個利用要求的權限,這些權限都是在這里定義的。從以上的步驟可以看出,第1次開機會做很多工作,這也就是為何android系統第1次啟動都會很慢的緣由。
當所有的線程服務都啟動完成后,其中的ActivityManagerService(AmS)服務會去檢測其它服務是不是完成,這個是通過調用systemReady()函數來完成的,最后會履行以下代碼:
mMainStack.resumeTopActivityLocked(null);
也就是說AmS會履行最頂層的TopActivity,但是第1次開機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);
}
}
也就是去加載HomeActivity,Android不像其它系統1樣,將1個固定的Activity作為主界面程序加載,而是在AmS的starHomeActiviyLocked()中,系統發出1個catagory字段包括CATEGORY_HOME的intent。以下:
intent.setComponent(mTopComponent);
if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME);
}
不管是哪一個利用程序,只要聲明自己為該類型后,那就能夠被認為是Home程序,系統并沒有選取任何1個“Home”程序,而是將這個權利交給了用戶,用戶的選擇決定了系統啟動的Home,這個就是第1個啟動的Acivity(當用戶使用安卓系統啟動后,會有1個選取界面的操作框,通過按鍵用戶可以選取適合的Activity作為主界面)。當AmS要啟動1個activity時,需要根據intent攜帶的activity名稱,調用內部的PackageManager查詢該名稱對應的具體信息,如果存在就啟動activity,否則返回失敗。
當系統的Lancher界面啟動顯現完成后,系統的啟動也就算完成了。
【本代碼基于android4.2源碼進行分析】