Android系統(tǒng)的啟動(dòng)流程

Android系統(tǒng)的啟動(dòng),主要是指Android手機(jī)關(guān)機(jī)后,長(zhǎng)按電源鍵后,Android手機(jī)開(kāi)機(jī)的過(guò)程。從系統(tǒng)角度看,Android的啟動(dòng)程序可分為:

1、bootloader引導(dǎo)

2、裝載與啟動(dòng)Linux內(nèi)核

3、啟動(dòng)Android系統(tǒng)

·??3.1、啟動(dòng)Init進(jìn)程

·?3.1、啟動(dòng)Zygote

·?3.1、啟動(dòng)SystemService

·?3.1、啟動(dòng)Launcher

一、Bootloader啟動(dòng)

開(kāi)機(jī),開(kāi)機(jī)就是給系統(tǒng)開(kāi)始供電,此時(shí)硬件電路會(huì)產(chǎn)生一個(gè)確定的復(fù)位時(shí)序,保證CPU是最后一個(gè)被復(fù)位的器件,為什么CPU要最后被復(fù)位呢?因?yàn)椋绻鸆PU第一個(gè)被復(fù)位,則當(dāng)CPU復(fù)位后開(kāi)始運(yùn)行時(shí),其他硬件內(nèi)部的寄存器狀態(tài)可能還沒(méi)有準(zhǔn)備好,比如磁盤或者內(nèi)存,那么久可能出現(xiàn)外圍硬件初始化錯(cuò)誤。當(dāng)正確完成復(fù)位后,CPU開(kāi)始執(zhí)行第一條指令,該指令所在的內(nèi)存地址是固定的,這由CPU的制造者指定。不同的CPU可能會(huì)從不同的地址獲取指令,但這個(gè)地址必須是固定的,這個(gè)固定地址所保存的程序往往被稱為"引導(dǎo)程序(BootLoader)",因?yàn)槠渥饔檬茄b載真正的用戶程序。而U-boot的啟動(dòng)過(guò)程大致上可以分為兩個(gè)階段:

·?第一階段:匯編代碼U-boot的第一條指令從cpu/armXXX/start.S文件開(kāi)始

·?第二階段:C代碼從文件/lib_arm/board.c的start_armboot()函數(shù)開(kāi)始。

二、Linux系統(tǒng)啟動(dòng)

關(guān)于Linux系統(tǒng)啟動(dòng)主要分為三個(gè)階段,第一個(gè)階段是自解壓過(guò)程,第二個(gè)是設(shè)置ARM處理器的工作模式、設(shè)置一級(jí)頁(yè)表等,第三個(gè)階段主要是C代碼,包括Android的初始化的全部工作。

(一) 自解壓過(guò)程

(二) Linux初始化

Linux初始化又分為三個(gè)階段

第一階段

本階段就是上面說(shuō)的到的內(nèi)核解壓縮完成后的階段。

第二階段

從start_kernel函數(shù)開(kāi)始

Linux內(nèi)核啟動(dòng)的第一個(gè)階段是從start_kernel函數(shù)開(kāi)始的。start_kernel是所有Linux平臺(tái)進(jìn)入系統(tǒng)內(nèi)核初始化后的入口函數(shù),它主要完成剩余與硬件平臺(tái)的相關(guān)初始化工作,在進(jìn)行一些系列的與內(nèi)核相關(guān)的初始后,調(diào)用第一個(gè)用戶進(jìn)程——init進(jìn)程并等待用戶進(jìn)程的執(zhí)行,這樣整個(gè)Linux內(nèi)核便啟動(dòng)完畢。該函數(shù)位于init/main.c文件中。

第三階段

根文件系統(tǒng)至少包括以下目錄:

·?/etc/:存儲(chǔ)重要的配置文件

·?/bin/:存儲(chǔ)常用且開(kāi)機(jī)時(shí)必須用到的執(zhí)行文件。

·?/sbin/:存儲(chǔ)著開(kāi)機(jī)過(guò)程中所需的系統(tǒng)執(zhí)行文件。

·?/lib/:存儲(chǔ)/bin/及/sbin/的執(zhí)行文件所需要的鏈接庫(kù),以及Linux的內(nèi)核模塊

·?/dev/:存儲(chǔ)設(shè)備文件

上面五大目錄必須存儲(chǔ)在文件系統(tǒng)上,缺一不可。


一、init進(jìn)程簡(jiǎn)介

通過(guò)上篇文章我們知道,Android設(shè)備啟動(dòng)要經(jīng)過(guò)3個(gè)階段,BootLoader、Linux Kernel和Android系統(tǒng)服務(wù),一般情況下,他們都會(huì)相應(yīng)的啟動(dòng)對(duì)動(dòng)畫對(duì)應(yīng)。前面我們已經(jīng)知道Andorid系統(tǒng)是如何啟動(dòng)的BootLoaderLinux Kernel的。

嚴(yán)格上講,Android系統(tǒng)實(shí)際上是運(yùn)行于Linux內(nèi)核之上的一系列"服務(wù)進(jìn)程",并不算一個(gè)完成意義上的"操作系統(tǒng)";而這一系列進(jìn)程是維持Android設(shè)備正常工作的關(guān)鍵,所以它們肯定有一個(gè)"根進(jìn)程",這個(gè)"根進(jìn)程"衍生出了這一系列進(jìn)程。這個(gè)"根進(jìn)程"就是init進(jìn)程。

init進(jìn)程是Android系統(tǒng)啟動(dòng)的第一個(gè)進(jìn)程。它通過(guò)解析init.rc腳本來(lái)構(gòu)建出系統(tǒng)的初始形態(tài)。其他的"一系列"Android系統(tǒng)進(jìn)程大部分也是通過(guò)"init.rc"來(lái)啟動(dòng)的。因?yàn)橐嫒莶煌拈_(kāi)發(fā)商,所以init.rc腳本的語(yǔ)法很簡(jiǎn)單,并且采用的是純文本編輯的,這樣導(dǎo)致它可讀性就會(huì)很高。

二、Init.cpp

init是Linux系統(tǒng)中用戶空間的第一個(gè)進(jìn)程(pid=1),Linux Kernel啟動(dòng)后,會(huì)調(diào)用/system/core/init/Init.cpp的main()方法

init總結(jié)

這里里面總結(jié)下init里面main方法做的事情如下:

·?first stage 初始化環(huán)境變量和各種文件系統(tǒng)目錄,klog初始化等

·?selinux相關(guān)初始化完成,然后切換second stage 重啟init進(jìn)程

·?屬性服務(wù)初始化,將各種系統(tǒng)屬性默認(rèn)值填充到屬性Map中

·?創(chuàng)建epoll描述符結(jié)合注冊(cè)socket監(jiān)聽(tīng),處理顯示啟動(dòng)進(jìn)程和掛掉的子進(jìn)程重啟

·?解析init.rc。把各種action、service等解析出來(lái)的填充到相應(yīng)鏈表容器管理

·?有序?qū)arly-init、init等各種cmd加入到執(zhí)行隊(duì)列action_queue鏈表中

·?進(jìn)入while()循環(huán)依次取出執(zhí)行隊(duì)列action_queue中的command執(zhí)行,fork包括app_process在內(nèi)的各種進(jìn)程,epoll阻塞監(jiān)聽(tīng)處理來(lái)自掛掉的子進(jìn)程的消息,根據(jù)設(shè)定策略restart子進(jìn)程。

Android系統(tǒng)啟動(dòng)——?zyogte進(jìn)程 (C篇)

我們大家都是知道"一鼎三足"和"三角形的穩(wěn)定性",那么支撐Android系統(tǒng)的三個(gè)"足"是什么?即init進(jìn)程、SystemServer進(jìn)程和Zygote進(jìn)程。

一、為什么要研究?zygote?

Linux的進(jìn)程是通過(guò)系統(tǒng)調(diào)用fork產(chǎn)生的,fork出的子進(jìn)程除了內(nèi)核中的一些核心的數(shù)據(jù)結(jié)構(gòu)和父進(jìn)程不同之外,其余的內(nèi)存映像都是和父進(jìn)程共享的。只有當(dāng)子進(jìn)程需要去改寫這些共享的內(nèi)存時(shí),操作系統(tǒng)才會(huì)為子進(jìn)程分配一個(gè)新的頁(yè)面,并將老的頁(yè)面上的數(shù)據(jù)復(fù)制一份到新的頁(yè)面,這就是所謂的"寫拷貝"。

通常,子進(jìn)程被fork出來(lái)后,會(huì)繼續(xù)執(zhí)行系統(tǒng)調(diào)用exec,exec將用一個(gè)新的可執(zhí)行文件的內(nèi)容替換當(dāng)前進(jìn)程的代碼段、數(shù)據(jù)段、堆和棧段。Fork加exec 是Linux啟動(dòng)應(yīng)用的標(biāo)準(zhǔn)做法,init進(jìn)程也是這樣來(lái)啟動(dòng)的各種服務(wù)的。

· Zygote創(chuàng)建應(yīng)用程序時(shí)卻只使用了fork,沒(méi)有調(diào)用exec。Android應(yīng)用中執(zhí)行的是Java代碼,Java代碼的不同才造成了應(yīng)用的區(qū)別,而對(duì)于運(yùn)行Java的環(huán)境,要求卻是一樣的。

· Zygote初始化時(shí)會(huì)創(chuàng)建創(chuàng)建虛擬機(jī),同時(shí)把需要的系統(tǒng)類庫(kù)和資源文件加載到內(nèi)存里面。Zygote fork出子進(jìn)程后,這個(gè)子進(jìn)程也繼承了能正常工作的虛擬機(jī)和各類系統(tǒng)資源,接下來(lái)子進(jìn)程只需要裝載APK文件的字節(jié)碼文件就可以運(yùn)行了。這樣應(yīng)用程序的啟動(dòng)時(shí)間就會(huì)大大縮短。


二、Zygote進(jìn)程(C層)的啟動(dòng)

Zygote進(jìn)程在init進(jìn)程中以service的方式啟動(dòng)的。從Android 5.0開(kāi)始,Zygote還是有變動(dòng)的,之前是直接放入init.rc中的代碼塊中,現(xiàn)在是放到了單獨(dú)的文件中,通過(guò)init.rc中通過(guò)"import"的方式引入文件。如下:代碼在init.rc11行

import?/init.${ro.zygote}.rc

屬性rozyoget 可能取值有可能為如下:

·?zygote32

·?zygote32_64

·?zygote64

·?zygote64_32

(三)、zygote的啟動(dòng)流程

那我們就整理一下zygote的啟動(dòng)流程,大致如下:


AppRuntime繼承自AndroidRuntime類,并且重載了onVmCreated 、onStarted、onZygoteInit和onExit函數(shù)。我們發(fā)現(xiàn)并沒(méi)有重載start函數(shù),而在app_main.cpp的main()函數(shù)的最后runtime.start函數(shù),所以具體執(zhí)行在AndroidRuntime類的start函數(shù)。那我們就來(lái)研究下AndroidRuntime類

三、關(guān)于虛擬機(jī)簡(jiǎn)介

Android系統(tǒng)在4.4版本的時(shí)候,發(fā)布了一個(gè)ART運(yùn)行時(shí),來(lái)替換之前的一直使用的Dalvik虛擬機(jī),接來(lái)解決之前Dalvik虛擬機(jī)的性能問(wèn)題,關(guān)于ART的實(shí)現(xiàn)原理,我后續(xù)會(huì)單獨(dú)講解。這里先略過(guò)。這里先簡(jiǎn)單的講解下虛擬機(jī)。Dalvik虛擬機(jī)實(shí)則是也算是一個(gè)Java虛擬機(jī),只不過(guò)它的執(zhí)行的不是class文件,而是dex文件。所以,ART運(yùn)行時(shí)的設(shè)計(jì)的最perfect的方案肯定也是類似于一個(gè)Dalvik虛擬機(jī)的形式。其實(shí)無(wú)論是ART虛擬機(jī)還是Dalvik虛擬機(jī)在接口上與Java虛擬機(jī)基本上是一致的(但是其內(nèi)部的機(jī)制是不一樣的)。這樣才能無(wú)縫隙的銜接。那我們來(lái)簡(jiǎn)單的看下這三個(gè)虛擬機(jī)(Java虛擬機(jī)、Dalvik虛擬機(jī)、ART運(yùn)行時(shí)) 如下圖:

1、相同點(diǎn)

通過(guò)上圖我們知道,Dalvik虛擬機(jī)和ART運(yùn)行時(shí)都實(shí)現(xiàn)了3個(gè)抽象Java虛擬機(jī)的接口,即:

· 1、JNI_GetDefaultJavaVMInitArgs:獲取虛擬機(jī)的默認(rèn)是初始化參數(shù)

· 2、JNI_CreateJavaVM:在進(jìn)程中創(chuàng)建虛擬機(jī)實(shí)例

· 3、JNI_GetCreatedJavaVMs:獲取進(jìn)程中創(chuàng)建虛擬機(jī)實(shí)例

2、不同點(diǎn)

以上描述的是Dalvik虛擬機(jī)與ART運(yùn)行時(shí)的共同之處,當(dāng)然它們之間還有不同點(diǎn),最大的不同點(diǎn)在于,Dalvik虛擬機(jī)執(zhí)行的是dex字節(jié)碼,ART虛擬機(jī)執(zhí)行的是本地機(jī)器嗎。這意味著Dalvik虛擬機(jī)包含一個(gè)解釋器,用來(lái)執(zhí)行dex字節(jié)碼,當(dāng)前從Android2.2開(kāi)始,也包含了一個(gè)JIT(Just-In-Time),用來(lái)在運(yùn)行時(shí)動(dòng)態(tài)地將執(zhí)行頻率很高的dex字節(jié)碼翻譯成本地機(jī)器碼,然后再執(zhí)行。通過(guò)JIT,就可以有效地提高Dalvik虛擬機(jī)的執(zhí)行效率。但是,dex字節(jié)翻譯成本地機(jī)器碼是發(fā)生在應(yīng)用程序運(yùn)行過(guò)程中的,并且應(yīng)用程序每一次重新運(yùn)行的時(shí)候,都要做重做這個(gè)翻譯的工作。所以即使用了JIT,Dalvik虛擬機(jī)總體的性能還是不能與直接執(zhí)行本地機(jī)器碼的ART運(yùn)行時(shí)相比。

3、ART原理簡(jiǎn)介

那我們來(lái)看下,Android運(yùn)行時(shí)從Dalvik虛擬機(jī)替換成ART運(yùn)行時(shí),并不要求開(kāi)發(fā)者重新將自己的應(yīng)用直接編譯成目標(biāo)的機(jī)器碼。也就是說(shuō),其實(shí)開(kāi)發(fā)者開(kāi)發(fā)出來(lái)的應(yīng)用程序經(jīng)過(guò)編譯和打包之后,仍然是一個(gè)包含dex字節(jié)碼的APK文件。既然應(yīng)用程序包含的仍然是dex字節(jié)碼,而ART運(yùn)行時(shí)需要的是本地機(jī)器碼,這必然要有一個(gè)翻譯的過(guò)程。這個(gè)翻譯的過(guò)程。這個(gè)翻譯的過(guò)程當(dāng)然不能發(fā)生在應(yīng)用程序運(yùn)行的時(shí)候,否則的話就和Dalvik虛擬機(jī)JIT一樣,并沒(méi)有解決性能的問(wèn)題。在計(jì)算機(jī)的世界里,與JIT相對(duì)的是AOT,即Ahead-Of-Time的簡(jiǎn)稱,它發(fā)生在程序運(yùn)行時(shí)之前。我們用靜態(tài)語(yǔ)言(比如C/C++) 來(lái)開(kāi)發(fā)的應(yīng)用程序的時(shí)候,編譯器直接就把他們翻譯成目標(biāo)機(jī)器碼。這種靜態(tài)語(yǔ)言的編譯方式也是AOT的一種。ART運(yùn)行時(shí)并不要求開(kāi)發(fā)者將自己的應(yīng)用直接編譯成目標(biāo)機(jī)器碼。這樣,將應(yīng)用的dex字節(jié)碼翻譯成本地機(jī)器碼的最恰當(dāng)?shù)腁OT時(shí)機(jī)就是發(fā)生在應(yīng)用安裝的時(shí)候。在沒(méi)有ART虛擬機(jī)之前,應(yīng)用的安裝過(guò)程,其實(shí)也會(huì)執(zhí)行一次"翻譯"的過(guò)程。只不過(guò)這個(gè)"翻譯"過(guò)程是將dex字節(jié)碼進(jìn)行優(yōu)化,這也就是由dex文件生成odex文件。這個(gè)過(guò)程在安裝服務(wù)PackageManagerService請(qǐng)求守護(hù)進(jìn)程installd來(lái)執(zhí)行的。從這個(gè)角度來(lái)看,在應(yīng)用安裝的過(guò)程中將dex字節(jié)碼翻譯成本地機(jī)器碼對(duì)原來(lái)的應(yīng)用安裝流程基本上就不會(huì)產(chǎn)生什么影響。

四、啟動(dòng)虛擬機(jī)

啟動(dòng)虛擬機(jī)主要包括兩部分,即

· jni_invocation.Init(NULL):初始化JNI環(huán)境

· AndroidRuntime::startVm(...)函數(shù):?jiǎn)?dòng)

(二)、Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized)函數(shù)解析

·第一步:初始化內(nèi)存映射模塊

·第二步:調(diào)用ParsedOptions的靜態(tài)函數(shù)Create(raw_options, ignore_unrecognized, &runtime_options)來(lái)解析ART運(yùn)行時(shí)的啟動(dòng)選項(xiàng),并且保存變量options指向一個(gè)ParsedOptions對(duì)象的各個(gè)成員變量中

·第三步:原子屬性庫(kù)的初始化

·第四步:Monitor初始化

·第五步:設(shè)置runtime_options的一些默認(rèn)選項(xiàng)

·第六步:創(chuàng)建heap對(duì)象。

·第七步:配置一些選項(xiàng)

·獲取dump_gc_performance_on_shutdown_ 并配置,XX:DumpGCPerformanceOnShutdown用于傳遞給dalvikvm,獲得應(yīng)用的GC性能時(shí)序

·配置JDWP,即Java Debug Wire Protocol 的縮寫,它定義了調(diào)試器(debugger)和被調(diào)試的Java 虛擬機(jī)(target vm)之間的通信協(xié)議。

·配置JIT選項(xiàng),用來(lái)提升代碼執(zhí)行效率。如果是5.0之后的,不需要開(kāi)啟該選項(xiàng),因?yàn)锳RT是需要AOT的,但是也兼容dalvik的dex解釋器,所以JIT也是有的

·創(chuàng)建線性分配器 linear_alloc_

·第八步:獲取ART的架構(gòu)

·第九步:初始化信號(hào)鏈

·第十步:根據(jù)ART的架構(gòu),進(jìn)行對(duì)應(yīng)信號(hào)的處理,所以所有信號(hào)都要交給fault_manager來(lái)處理。

·第十一步:創(chuàng)建JavaVMExt,這個(gè)JavaVMExt實(shí)例最終是要返回給調(diào)用的,使得調(diào)用者可以通過(guò)該JavaVMExt實(shí)例和ART虛擬機(jī)交互

·第十二步:創(chuàng)建一個(gè)線程,并且attach線程(attach的過(guò)程實(shí)際就是創(chuàng)建Thread對(duì)象并初始化Thread對(duì)象的過(guò)程)

·第十三步:attach后通過(guò)調(diào)用EnableObjectValidation函數(shù)來(lái)驗(yàn)證heap

·第十四步:創(chuàng)建ClassLinker實(shí)例,這是一個(gè)非常重要的實(shí)例,類的加載、鏈接和初始化都是在這個(gè)類中完成的。如果有BootImageSpace,則調(diào)用ClassLinker::InitFromBootImage來(lái)完成ClassLinker的初始化,如果沒(méi)有BootImageSpace,則調(diào)用ClassLinker::InitWithoutImage來(lái)完成初始化。前者通過(guò)ImageSpace來(lái)加載系統(tǒng)類;后者是通過(guò)boot_class_path,boot_class_path是系統(tǒng)類DexFile數(shù)組,ImageSpace的優(yōu)點(diǎn)是加載快,通過(guò)mmap加載一個(gè)系統(tǒng)類的鏡像文件。

·第十五步:初始化sentinel_的值

·第十六步:如果有MethodTrace選項(xiàng),則進(jìn)行相應(yīng)的配置

·第十七步:如果有Profiler選項(xiàng),則進(jìn)行相應(yīng)的配置

·第十八步:提前分配一個(gè)OutOfMemoryError和NoClassDefFoundError

·第十九步:配置NativeBridge中間模塊,從Android 5.0,開(kāi)始在其ART的實(shí)現(xiàn)中,引入了一個(gè)叫做NativeBridge的中間模塊,這個(gè)模塊基本上就是為了JNI調(diào)用時(shí)進(jìn)行動(dòng)態(tài)轉(zhuǎn)碼用的,自帶了基本上所有的處理邏輯。

可以關(guān)注我的公眾號(hào):Android架構(gòu)師成長(zhǎng)之路

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容