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

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

對于純Android應(yīng)用層開發(fā)來講汁尺,了解一些Android的啟動流程的知識并不會直接提高自己的代碼質(zhì)量屋讶。但是作為整個Android系統(tǒng)的開端悴能,這部分的流程時刻影響著應(yīng)用層的方方面面窃躲。這些知識也是作為Android開發(fā)進階必須要了解的一部分嫂伞。

過去你可能會有疑問方椎,比如為什么所有應(yīng)用的父進程號都是同一個聂抢?ActivityManagerService是怎么來的?Android上的Java虛擬機什么時候被加載的辩尊?

本文來分析總結(jié)一下Android 系統(tǒng)在啟動的Init涛浙、Zygote、SystemServer摄欲,來幫助大家了解一下轿亮。

一:Init

當(dāng)用戶按下開機鍵的時候,引導(dǎo)芯片加載BootLoader到內(nèi)存中胸墙,開始拉起Linux OS我注,一旦Linux內(nèi)核啟動完畢后,它就會在系統(tǒng)文件中尋找 init.rc 文件迟隅,并啟動init 進程但骨。

什么是 init.rc 文件励七?

init.rc文件是一個非常重要的配置文件,它由AIL語言(Android Init Language)編寫奔缠。

(init.rc的一個小細(xì)節(jié):Android8.0后對init.rc文件進行了拆分掠抬,比如64位的Zygote的啟動腳本在init.zygote64.rc文件中)

在啟動的init進程中,會進入system/core/init/init.cpp文件的main方法中校哎,關(guān)鍵代碼如下:

int main(int argc,char ** argv){
    
    ...
    if(is_first_stage){
        //創(chuàng)建和掛在啟動所需要的文件目錄
        mount("tmpfs","/dev","tmpfs",MS_NOSUID,"mode=0755");
        mkdir("/dev/pts",0755);
        //創(chuàng)建和掛在很多...
        ...
    }
    
    ...
    //對屬性服務(wù)進行初始化
    property_init();
    
    ...
    //用于設(shè)置子進程信號處理函數(shù)(如Zygote)两波,如果子進程異常退出,init進程會調(diào)用該函數(shù)中設(shè)定的信號處理函數(shù)來處理
    signal_handler_init();
    
    ...

    //啟動屬性服務(wù)
    start_property_service();
    
    ...
    
    //解析init.rc配置文件
    parser.ParseConfig("/init.rc");
    
}

上述代碼中,有一個概念是 屬性服務(wù),經(jīng)常玩Windows 的同學(xué)應(yīng)該知道WIndows注冊表,注冊表采用鍵值對的形式來記錄用戶轻绞、軟件的一些使用信息,這樣即使系統(tǒng)或者軟件重啟也能夠根據(jù)之前注冊表中的記錄來進行相應(yīng)的初始化劣坊。init進程啟動的屬性服務(wù),就是用來儲存這些屬性的屈留。

屬性服務(wù)的查詢和修改都是通過Socket進行的局冰,系統(tǒng)文件內(nèi)定義對多為8個用戶提供服務(wù)。并且屬性服務(wù)中的系統(tǒng)屬性分為兩種:普通屬性和控制屬性绕沈∪裣耄控制屬性用來執(zhí)行一些命令帮寻,比如開機的動畫就使用了這種屬性乍狐。

在處理了繁多的任務(wù)后,init進程會進行最關(guān)鍵的一部操作: 啟動Zygote

代碼在 frameworks/base/cmds/app_process/app_main.cpp中:

int main(int argc ,char* const argv[]){
    ...
    if(zygote){
        //啟動Zygote進程
        runtime.start("com.android.internal.os.ZygoteInit",args,zygote);
    }
}

總結(jié)一下固逗,init進程啟動后做了哪幾件事:

(1)創(chuàng)建和掛載啟動所需要的文件和目錄

(2)初始化和啟動屬性服務(wù)浅蚪。

(3)解析init.rc配置文件,并且啟動了Zygote進程

二:Zygote

(1)Zygote概述:

在Android系統(tǒng)中烫罩,DVM(Dalvik虛擬機)和ART惜傲、應(yīng)用程序進程以及運行系統(tǒng)關(guān)鍵服務(wù)的SystemServer進程都是由Zygote進程創(chuàng)建的,我們也將它稱為孵化器(本來字面意思就是受精卵...)贝攒。它通過fork復(fù)制進程的形勢來創(chuàng)建應(yīng)用進程和SystemServer進程盗誊,由于Zygote進程在啟動時會創(chuàng)建DVM或者ART,因此通過fork而創(chuàng)建的應(yīng)用程序進程和SystemServer進程可以在內(nèi)部獲取一個DVM或者ART的實例副本隘弊。

init.rc文件引入Zygote的啟動腳本哈踱,這些腳本都是由AIL編寫的。

由于Android 5.0后,Android開始支持64位程序梨熙,Zygote也就有了32位與64位之分开镣。一共有四種Zygote啟動腳本:

init.zygote32.rc

init.zygote32_64.rc

init.zygote64.rc

init.zygote64_32.rc

(2)Zygote啟動流程

Zygote啟動時主要調(diào)用app_main.cpp的main()中的AppRuntime的start方法來啟動Zygote進程。我們先展示app_main.cpp中的main函數(shù)咽扇。

frameworks/base/cmds/app_process/app_main.cpp:

int main(int argc,char* const argv[]){
    ...
    
    while( i < argc ){
        const char* arg=argv[i++];
        
        if(strcmp(arg,"--zygote")==0){
            //如果當(dāng)前進程在Zygote中邪财,則設(shè)置zygote=true
            zygote=true;
            niceName=ZYGOTE_NICE_NAME;
        }else if(strcmp(arg,"--start-system-server")==0){
            //如果當(dāng)前進程在SystemServer中陕壹,將startSystemServer=true
            startSystemServer=true;
        }
        ...
    }
    ...
    //承接上面Init進程中的代碼
    if(zygote){
        //啟動Zygote進程
        runtime.start("com.android.internal.os.ZygoteInit",args,zygote);
    }
}

由于Zygote進程都是通過fock自身來創(chuàng)建子進程,這樣Zygote進程和他的子進程(比如SystemServer)都可以進入app_main.cpp的main函數(shù)中树埠,因此需要區(qū)分一下當(dāng)前進程運行在哪個進程里糠馆。

接下來我們進入 runtime.start中看看:

frameworks/base/core/jni/AndroidRuntime.cpp:

void AndroidRuntime::start(const char* className,const Vector<String8>& options,bool zygote){
    ...
    
    //startVm函數(shù)用于啟動Java虛擬機
    if(startVm(&mJavaVM,&env,zygote)!=0){
        return;
    }
    onVmCreated(env);
    //為Java虛擬機注冊JNI方法
    if(startReg(env)<0){
        return;
    }
    ...
    //從app_main的main方法中得知,className是com.android.internal.os.ZygoteInit
    classNameStr=env->NewStringUTF(className);
    
    ...
    
    //找到ZygoteInit類
    jclass startClass=env->FindClass(slashClassName);
    
    ...
    //找到ZygoteInit的main方法
    jmethodID startMeth=env->GetStaticMethodID(startClass,"main","([Ljava/lang/String;)V");
    
    ...
    //通過JNI調(diào)用ZygoteInit的main方法
    env->CallStaticVoidMethod(startClass,startMeth,strArray);
}

以上代碼就厲害了怎憋,它從Init進程中的AndroidRuntime的main函數(shù)榨惠,啟動了Java虛擬機,并且通過JNI啟動了Zygote盛霎,一波操作之后赠橙,Zygote順利從Native層進入了Java層。

隨后我們進入ZygoteInit中愤炸,看看它的main方法做了什么期揪。

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java:

public static void main(String argv[]){
    ...
    //創(chuàng)建一個Server端的Socket,socketName值為:zygote
    zygoteServer.registerServerSocket(socketName);
    
    if(!enableLazyPreload){
        ...
        //預(yù)加載類和資源
        preload(bootTimingsTraceLog);
    }else{
        ...
    }
    
    if(startSystemServer){
        //啟動SystemServer進程
        startSystemServer(abiList,socketName,zygoteSerer);
    }
    //等待AMS的請求
    zygoteServer.runSelectLoop(abiList);
    zygoteServer.closeServerSocket();
}

大致代碼就此結(jié)束规个。

總結(jié)一下ZygoteInit的main方法都做了哪些事情:

**1.創(chuàng)建了一個Server端的Socket(名為zygote凤薛,AndroidQ之后改為在ZygoteServer的構(gòu)造中創(chuàng)建該Socket,以及USAPSocket)

ZygoteServer(boolean isPrimaryZygote) {
mUsapPoolEventFD = Zygote.getUsapPoolEventFD();

    if (isPrimaryZygote) {
        mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
        mUsapPoolSocket =
                Zygote.createManagedSocketFromInitSocket(
                        Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);
    } else {
        mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME);
        mUsapPoolSocket =
                Zygote.createManagedSocketFromInitSocket(
                        Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);
    }

    mUsapPoolSupported = true;
    fetchUsapPoolPolicyProps();
}

2.預(yù)加載類和資源

3.啟動了SystemServer進程

4.等待AMS請求創(chuàng)建新的應(yīng)用程序進程**


最后再總結(jié)一下Zygote進程啟動公做了幾件事:

1.創(chuàng)建AndroidRuntime并調(diào)用其start方法诞仓,啟動Zygote進程缤苫。

2.創(chuàng)建Java虛擬機并為Java虛擬機注冊JNI方法。

3.通過JNI調(diào)用ZygoteInit的main函數(shù)進入Zygote的java框架層墅拭。

4.通過registerZygoteSocket方法創(chuàng)建服務(wù)端Socket活玲,并通過runSelectLoop方法等待AMS的請求來創(chuàng)建新的應(yīng)用程序進程。

5.啟動SystemServer谍婉。

三:SystemServer

SystemServer進程主要用于創(chuàng)建系統(tǒng)服務(wù)舒憾,我們熟知的AMS、WMS穗熬、PMS都是由它來創(chuàng)建的镀迂。

在前面講到過,Zygote進程啟動了SystemServer進程唤蔗,我們看一下啟動部分的代碼探遵。
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java:

private static boolean startSystemServer(String abiList,String socketName){
    ...
    //判斷進程是否是SystemServer
    if(pid == 0){
        ...
        //關(guān)閉Zygote的Socket
        zygoteServer.closeServerSocket();
        //啟動SystemServer進程
        handleSystemServerProcess(parsedArgs);
    }
}

由于SystemServer是Zygote進程fork出來的,所以該進程也擁有一個ZygoteServer所開啟等待AMS連接的Socket實例副本妓柜。在這里并不需要這個Socket箱季,所以關(guān)閉。

接下來看看handleSystemServerProcess()方法领虹。

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java:

private static void handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs){
    ...
    ClassLoader cl=null;
    if(systemServerClasspath!=null){
        //在這里創(chuàng)建了PathClassLoader
        cl = createPathClassLoader(systemServerClasspath,parsedArgs.targetSdkVersion);
        Thread.currentThread().setContextClassLoader(cl);
    }
    //zygoteInit方法
    ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion,parsedArgs.remainingArgs,cl);
}

就是這里规哪,創(chuàng)建了大名鼎鼎的 PathClassLoader

接下來看看zygoteInit方法塌衰。

frameworks/base/core/java/com/android/internal/os/ZygoteInit.java:

public static final void zygoteInit(int targetSdkVersion,String[] argv,ClassLoader classLoader){
    ...
    //就在該方法中诉稍,啟動了Binder線程池
    ZygoteInit.nativeZygoteInit();
    //進入SystemServer的main方法
    RuntimeInit.applicationInit(targetSdkVersion,argv,classLoader);
}

nativeZygoteInit方法一看名稱蝠嘉,就知道是在Native層的代碼。用來啟動Binder線程池杯巨,這樣SystemServer進程就可以使用Binder與其他進程進行通信了蚤告。

再講一下RuntimeInit的applicationInit方法,該方法用于啟動SystemServer(以及后來我們的應(yīng)用程序進程的啟動)服爷。

frameworks/base/core/java/com/android/internal/os/RuntimeInit.java:

protected static void applicationINit(int targetSdkVersion,String[] argv,ClassLoader classLoader){
    ...
    invokeStaticMain(args.startClass,args.startArgs,classLoader);
}

...

private static void invokeStaticMain(String className,String[] argv,ClassLoader classLoader){
    Class<?> cl;
    ...
    
    //className是com.android.server.SystemServer
    cl=Class.forName(className,true,classLoader);
    
    ...
    //找到SystemServer的main方法
    m=cl.getMethod("main",new Class[]{String[].class});
    
    ...
    //拋出異常杜恰,這里拋出異常中調(diào)用了SystemServer的main方法
    throw new Zygote.MethodAndArgsCaller(m,argv);
    
}

在invokeStaticMain方法最后,以拋出異常的方式調(diào)用了SystemServer的main方法(之后在啟動其他應(yīng)用進程的時候仍源,也是這樣調(diào)用ActivityThread的main方法的)心褐。這種處理會清除所有設(shè)置過程需要的堆棧幀。

接下來我們解析一下SystemServer進程笼踩。

frameworks/base/services/java/com/android/server/SystemServer.java:

public static void main(String[] args){
    //就一行代碼
    new SystemServer().run();
}

private void run(){
    ...
    //創(chuàng)建消息Looper
    Looper.prepareMainLooper();
    //加載動態(tài)庫
    System.loadLibrary("android_servers");
    //創(chuàng)建SystemServiceManager
    mSystemServiceManager=new SystemServiceManager(mSystemContext);
    
    ...
    //啟動引導(dǎo)服務(wù)
    startBootstrapServices();
    //啟動核心服務(wù)
    startCoreServices();
    //啟動其他服務(wù)
    startOtherServices();
    
    ...
}

我們可以看到SystemServer在啟動后逗爹,陸續(xù)啟動了各項服務(wù),包括ActivityManagerService嚎于,PowerManagerService掘而,PackageManagerService等等,而這些服務(wù)的父類都是SystemService于购。

最后總結(jié)一下SystemServer進程:

1.啟動Binder線程池

2.創(chuàng)建了SystemServiceManager(用于對系統(tǒng)服務(wù)進行創(chuàng)建袍睡、啟動和生命周期管理)

3.啟動了各種服務(wù)

結(jié)尾

到此為止,本文基本結(jié)束了肋僧。Android系統(tǒng)啟動代碼繁多斑胜,我們只截取了部分關(guān)鍵代碼作為展示。最后總結(jié)一下色瘩,Init進程伪窖、Zygote、SystemServer的關(guān)系居兆,如果還是有不懂的童鞋可以記住:

盤古(Linux 內(nèi)核)開天辟地后竹伸,世上誕生了女媧(Init進程)泥栖,女媧過于孤獨決定要個孩子,就用泥巴捏了個亞當(dāng)(Zygote)勋篓,后來亞當(dāng)也覺得整天就跟他媽在一起也太煩了吧享,決定造個女孩,于是用自己的肋骨(fork)創(chuàng)建了一個夏娃(SystemServer)譬嚣,于是萬物開始...

本文大量參考《Android 進階解密》一書钢颂,同時建議對Android底層有興趣的童鞋搞一本看看,里面講解的必然比本文精辟不少拜银。

本文純手打殊鞭,歡迎各位點亮愛心遭垛,給個小小的贊以資鼓勵,謝謝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末操灿,一起剝皮案震驚了整個濱河市锯仪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌趾盐,老刑警劉巖庶喜,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異救鲤,居然都是意外死亡久窟,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門本缠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瘸羡,“玉大人,你說我怎么就攤上這事搓茬∮汤担” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵卷仑,是天一觀的道長峻村。 經(jīng)常有香客問我,道長锡凝,這世上最難降的妖魔是什么粘昨? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮窜锯,結(jié)果婚禮上张肾,老公的妹妹穿的比我還像新娘。我一直安慰自己锚扎,他們只是感情好吞瞪,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著驾孔,像睡著了一般芍秆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上翠勉,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天妖啥,我揣著相機與錄音,去河邊找鬼对碌。 笑死荆虱,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播怀读,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼诉位,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了愿吹?” 一聲冷哼從身側(cè)響起不从,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎犁跪,沒想到半個月后椿息,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡坷衍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年寝优,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枫耳。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡乏矾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出迁杨,到底是詐尸還是另有隱情钻心,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布铅协,位于F島的核電站捷沸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏狐史。R本人自食惡果不足惜痒给,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望骏全。 院中可真熱鬧苍柏,春花似錦、人聲如沸姜贡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鲁豪。三九已至潘悼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間爬橡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工棒动, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留糙申,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓船惨,卻偏偏與公主長得像柜裸,于是被迫代替她去往敵國和親缕陕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345

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