Android系統啟動流程

Android系統啟動流程

一,序言

Android是谷歌開發的一款基于Linux的開源操作系統,下圖所示為 Android 平臺的主要組件

android-stack.png

Linux 內核

Android 平臺的基礎是 Linux 內核。例如,Android Runtime (ART) 依靠 Linux 內核來執行底層功能,例如線程和低層內存管理。

使用 Linux 內核可讓 Android 利用主要安全功能,并且允許設備制造商為著名的內核開發硬件驅動程序。

硬件抽象層 (HAL)

硬件抽象層 (HAL) 提供標準界面,向更高級別的 Java API 框架顯示設備硬件功能。HAL 包含多個

庫模塊,其中每個模塊都為特定類型的硬件組件實現一個界面,例如相機或藍牙模塊。當框架 API

要求訪問設備硬件時,Android 系統將為該硬件組件加載庫模塊。

Android Runtime

對于運行 Android 5.0(API 級別 21)或更高版本的設備,每個應用都在其自己的進程中運行,并

且有其自己的 Android Runtime (ART) 實例。ART 編寫為通過執行 DEX 文件在低內存設備上運行

多個虛擬機,DEX 文件是一種專為 Android 設計的字節碼格式,經過優化,使用的內存很少。編

譯工具鏈(例如 Jack)將 Java 源代碼編譯為 DEX 字節碼,使其可在 Android 平臺上運行。

ART 的部分主要功能包括:

  • 預先 (AOT) 和即時 (JIT) 編譯
  • 優化的垃圾回收 (GC)
  • 在 Android 9(API 級別 28)及更高版本的系統中,支持將應用軟件包中的 Dalvik Executable 格 式 (DEX) 文件轉換為更緊湊的機器代碼。
  • 更好的調試支持,包括專用采樣分析器、詳細的診斷異常和崩潰報告,并且能夠設置觀察點以監控特定字段。在 Android 版本 5.0(API 級別 21)之前,Dalvik 是 Android Runtime。如果您的應用在 ART 上運行效果很好,那么它應該也可在 Dalvik 上運行,但反過來不一定。

Android 還包含一套核心運行時庫,可提供 Java API 框架所使用的 Java 編程語言中的大部分功能,包括一些 Java 8 語言功能。

原生 C/C++ 庫

許多核心 Android 系統組件和服務(例如 ART 和 HAL)構建自原生代碼,需要以 C 和 C++ 編寫的原生庫。Android 平臺提供 Java 框架 API 以向應用顯示其中部分原生庫的功能。例如,您可以通過 Android 框架的 Java OpenGL API 訪問 OpenGL ES,以支持在應用中繪制和操作 2D 和 3D 圖形。如果開發的是需要 C 或 C++ 代碼的應用,可以使用 Android NDK 直接從原生代碼訪問某些原生平臺庫。

Java API 框架

您可通過以 Java 語言編寫的 API 使用 Android OS 的整個功能集。這些 API 形成創建 Android 應

用所需的構建塊,它們可簡化核心模塊化系統組件和服務的重復使用,包括以下組件和服務:

  • 豐富、可擴展的視圖系統,可用以構建應用的 UI,包括列表、網格、文本框、按鈕甚至可嵌入的網絡瀏覽器
  • 資源管理器,用于訪問非代碼資源,例如本地化的字符串、圖形和布局文件
  • 通知管理器,可讓所有應用在狀態欄中顯示自定義提醒
  • Activity 管理器,用于管理應用的生命周期,提供常見的導航返回棧
  • 內容提供程序,可讓應用訪問其他應用(例如“聯系人”應用)中的數據或者共享其自己的數據。開發者可以完全訪問 Android 系統應用使用的框架 API。

系統應用

  • Android 隨附一套用于電子郵件、短信、日歷、互聯網瀏覽和聯系人等的核心應用。平臺隨附的應用與用戶可以選擇安裝的應用一樣,沒有特殊狀態。因此第三方應用可成為用戶的默認網絡瀏覽器、短信 Messenger 甚至默認鍵盤(有一些例外,例如系統的“設置”應用)。

  • 系統應用可用作用戶的應用,以及提供開發者可從其自己的應用訪問的主要功能。例如,如果您的應用要發短信,您無需自己構建該功能,可以改為調用已安裝的短信應用向您指定的接收者發送消息。

二,Android系統啟動流程

參考http://gityuan.com/2016/02/01/android-booting/

android-boot.jpeg
Android framework.png
Android系統啟動.png

1. 啟動電源以及系統啟動

當電源按下,引導芯片代碼開始從預定義的地方(固化在ROM)開始執行。加載引導程序到RAM,然后執行

2. 引導程序

引導程序是在Android操作系統開始運行前的一個小程序。引導程序是運行的第一個程序,因此它是針對特定的主板與芯片的。設備制造商要么使用很受歡迎的引導程序比如redboot、uboot、qi

bootloader或者開發自己的引導程序,它不是Android操作系統的一部分。引導程序是OEM廠商或者運營商加鎖和限制的地方。

引導程序分兩個階段執行。

  1. 第一個階段,檢測外部的RAM以及加載對第二階段有用的程序;

  2. 第二階段,引導程序設置網絡、內存等等。這些對于運行內核是必要的,為了達到特殊的目標,引導程序可以根據配置參數或者輸入數據設置內核。

Android引導程序可以在\bootable\bootloader\legacy\usbloader找到。傳統的加載器包含兩個文件,需要在這里說明:

  1. init.s初始化堆棧,清零BBS段,調用main.c的_main()函數;
  2. main.c初始化硬件(鬧鐘、主板、鍵盤、控制臺),創建linux標簽

3. 內核

Android內核與桌面linux內核啟動的方式差不多。內核啟動時,設置緩存、被保護存儲器、計劃列表,加載驅動。當內核完成系統設置,它首先在系統文件中尋找”init”文件,然后啟動root進程或者系統的第一個進程

4. init進程

init進程是Linux系統中用戶空間的第一個進程,進程號固定為1。Kernel啟動后,在用戶空間啟動init進程,并調用init中的main()方法執行init進程的職責。

5. 啟動Lancher App

三,源碼分析

init進程分析

參考http://gityuan.com/2016/02/05/android-init/

其中init進程是Android系統中及其重要的第一個進程,接下來我們來看下init進程注意做了些什么

  1. 創建和掛載啟動所需要的文件目錄
  2. 初始化和啟動屬性服務
  3. 解析init.rc配置文件并啟動Zygote進程
android-booting.jpeg

zygote進程

參考http://gityuan.com/2016/02/13/android-zygote/

Zygote中文翻譯為“受精卵”,正如其名,它主要用于孵化子進程。在Android系統中有以下兩種程序:java應用程序,主要基于ART虛擬機,所有的應用程序apk都屬于這類native程序,也就是利用C或C++語言開發的程序,如bootanimation。所有的Java應用程序進程及系統服務SystemServer進程都由Zygote進程通過Linux的fork()函數孵化出來的,這也就是為什么把它稱為Zygote的原因,因為他就像一個受精卵,孵化出無數子進程,而native程序則由Init程序創建啟動。Zygote進程最初的名字不是“zygote”而是“app_process”,這個名字是在Android.mk文件中定義的Zgyote是Android中的第一個art虛擬機,他通過socket的方式與其他進程進行通信。這里的“其他進程”其實主要是系統進程——SystemServer

Zygote是一個C/S模型,Zygote進程作為服務端,它主要負責創建Java虛擬機,加載系統資源,啟動SystemServer進程,以及在后續運行過程中啟動普通的應用程序,其他進程作為客戶端向它發出“孵化”請求,而Zygote接收到這個請求后就“孵化”出一個新的進程。比如,當點擊Launcher里的應用程序圖標去啟動一個新的應用程序進程時,這個請求會到達框架層的核心服務ActivityManagerService中,當AMS收到這個請求后,它通過調用Process類發出一個“孵化”子進程的Socket請求,而Zygote監聽到這個請求后就立刻fork一個新的進程出來

Zygote啟動過程的調用流程圖:

zygote_start.jpeg
  1. 解析init.zygote.rc中的參數,創建AppRuntime并調用AppRuntime.start()方法;
  2. 調用AndroidRuntime的startVM()方法創建虛擬機,再調用startReg()注冊JNI函數;
  3. 通過JNI方式調用ZygoteInit.main(),第一次進入Java世界;
  4. registerZygoteSocket()建立socket通道,zygote作為通信的服務端,用于響應客戶端請求;
  5. preload()預加載通用類、drawable和color資源、openGL以及共享庫以及WebView,用于提高app啟動效率;
  6. zygote完畢大部分工作,接下來再通過startSystemServer(),fork得力幫手system_server進程,也是上層framework的運行載體。
  7. zygote功成身退,調用runSelectLoop(),隨時待命,當接收到請求創建新進程請求時立即喚醒并執行相應工作。

System Server啟動流程

System Server 是Zygote fork 的第一個Java 進程, 這個進程非常重要,因為他們有很多的系統線程,提供所有核心的系統服務看到大名鼎鼎的WindowManager, ActivityManager了嗎?對了,它們都是運行在system_server的進程里。還有很多“Binder-x”的線程,它們是各個Service為了響應應用程序遠程調用請求而創建的。除此之外,還有很多內部的線程,比如 ”UI thread”, “InputReader”, “InputDispatch” 等等,我,現在我們只關心System Server是如何創建起來的。

SystemServer的main() 函數。

public static void main(String[] args) { 
  new SystemServer().run();
} 

記下來分成4部分詳細分析SystemServer run方法的初始化流程:

初始化必要的SystemServer環境參數,比如系統時間、默認時區、語言、load一些Library等等,

初始化Looper,我們在主線程中使用到的looper就是在SystemServer中進行初始化的

初始化Context,只有初始化一個Context才能進行啟動Service等操作,這里看一下源碼:

private void createSystemContext() {
  ActivityThread activityThread = ActivityThread.systemMain(); 
  mSystemContext = activityThread.getSystemContext();
  mSystemContext.setTheme(DEFAULT_SYSTEM_THEME); 
  final Context systemUiContext = activityThread.getSystemUiContext();
  systemUiContext.setTheme(DEFAULT_SYSTEM_THEME);
}

看到沒有ActivityThread就是這個時候生成的

繼續看ActivityThread中如何生成Context:

public ContextImpl getSystemContext() {
  synchronized (this) { 
    if (mSystemContext == null) { 
      mSystemContext = ContextImpl.createSystemContext(this); 
    }return mSystemContext; 
  } 
}

ContextImpl是Context類的具體實現,里面封裝完成了生成幾種常用的createContext的方法:

static ContextImpl createSystemContext(ActivityThread mainThread) {
  LoadedApk packageInfo = new LoadedApk(mainThread); 
  //省略代碼 
  return context; 
}

static ContextImpl createSystemUiContext(ContextImpl systemContext) { 
  final LoadedApk packageInfo = systemContext.mPackageInfo; 
  //省略代碼
  return context; 
}

static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
  if (packageInfo == null) throw new IllegalArgumentException("packageInfo"); //省略代碼 
  return context; 
}

static ContextImpl createActivityContext(ActivityThread mainThread, LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId, Configuration overrideConfiguration) { 
  //省略代碼 
  return context; 
}

初始化SystemServiceManager,用來管理啟動service,SystemServiceManager中封裝了啟動Service的startService方法啟動系統必要的Service,啟動service的流程又分成三步走:

// Start services. 
try {
  traceBeginAndSlog("StartServices"); 
  startBootstrapServices(); 
  startCoreServices(); 
  startOtherServices(); 
  SystemServerInitThreadPool.shutdown(); 
} catch (Throwable ex) { 
  // 
} finally { 
  traceEnd();
}

啟動BootstrapServices,就是系統必須需要的服務,這些服務直接耦合性很高,所以干脆就放在一個方法里面一起啟動,比如PowerManagerService、RecoverySystemService、DisplayManagerService、ActivityManagerService等等啟動以基本的核心Service,很簡單,只有三個BatteryService、UsageStatsService、WebViewUpdateService啟動其它需要用到的Service,比如NetworkScoreService、AlarmManagerService

Zygote會默默的在后臺觀看像Sytem Server,一旦發現System Server 掛掉了,將其回收,然后將自己殺掉,重新開始新的一生。代碼在dalvik/vm/native/dalvik_system_zygote.cpp 中

static void Dalvik_dalvik_system_Zygote_forkSystemServer( const u4* args, JValue* pResult){ 
  ... 
    pid_t pid; pid = forkAndSpecializeCommon(args, true);
  ... 
    if (pid > 0) { 
      int status; 
      gDvm.systemServerPid = pid;
      /* WNOHANG 會讓waitpid 立即返回,這里只是為了預防上面的賦值語句沒有完成之 前SystemServer就crash 了*/ 
      if (waitpid(pid, &status, WNOHANG) == pid) {
        ALOGE("System server process %d has died. Restarting Zygote!", pid); 
        kill(getpid(), SIGKILL); 
      }
    }
  RETURN_INT(pid); 
}
/* 真正的處理在這里 */ 
static void sigchldHandler(int s){ 
  ... 
    pid_t pid;
  int status;
  ... 
    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
      ...
        if (pid == gDvm.systemServerPid) { 
          ... 
            kill(getpid(), SIGKILL); 
        } 
    }
  ... 
}

static void Dalvik_dalvik_system_Zygote_fork(const u4* args, JValue* pResult){
  pid_t pid; 
  ... 
    setSignalHandler(); //signalHandler 在這里注冊 
  ... 
    pid = fork(); 
  ... 
    RETURN_INT(pid);
}

在Unix-like系統,父進程必須用 waitpid 等待子進程的退出,否則子進程將變成”Zombie” (僵尸)進程,不僅系統資源泄漏,而且系統將崩潰(沒有system server,所有Android應用程序都無法運行)。但是waitpid() 是一個阻塞函數(WNOHANG參數除外),所以通常做法是在signal 處理數里進行無阻塞的處理,因為每個子進程退出的時候,系統會發出 SIGCHID 信號。Zygote會把自己殺掉, 那父親死了,所有的應用程序不就成為孤兒了? 不會,因為父進程被殺掉后系統會自動給所有的子進程發生SIGHUP信號,該信號的默認處理就是將殺掉自己退出當前進程。但是一些后臺進程(Daemon)可以通過設置SIG_IGN參數來忽略這個信號,從而得以在后臺繼續運行。

總結

  1. init 根據init.rc 運行 app_process, 并攜帶‘–zygote’ 和 ’–startSystemServer’ 參數。
  2. AndroidRuntime.cpp::start() 里將啟動JavaVM,并且注冊所有framework相關的系統JNI接口。
  3. 第一次進入Java世界,運行ZygoteInit.java::main() 函數初始化Zygote. Zygote 并創建Socket的server 端
  4. 然后fork一個新的進程并在新進程里初始化SystemServer. Fork之前,Zygote是preload常用的Java類庫,以及系統的resources,同時GC()清理內存空間,為子進程省去重復的工作。
  5. SystemServer 里將所有的系統Service初始化,包括ActivityManager 和 WindowManager, 他們是應用程序運行起來的前提。
  6. 依次同時,Zygote監聽服務端Socket,等待新的應用啟動請求。
  7. ActivityManager ready 之后尋找系統的“Startup” Application, 將請求發給Zygote。
  8. Zygote收到請求后,fork出一個新的進程。
  9. Zygote監聽并處理SystemServer 的 SIGCHID 信號,一旦System Server崩潰,立即將自己殺死。init會重啟Zygote.

四,總結

1. 手機開機andorid系統啟動的流程

當按電源鍵觸發開機,首先會從 ROM 中預定義的地方加載引導程序 BootLoader 到 RAM 中,并執行 BootLoader 程序啟動 Linux Kernel, 然后啟動用戶級別的第一個進程: init 進程。init 進程會解析init.rc 腳本做一些初始化工作,包括掛載文件系統、創建工作目錄以及啟動系統服務進程等,其中系統服務進程包括 Zygote、service manager、media 等。在 Zygote 中會進一步去啟動 system_server 進程,然后在 system_server 進程中會啟動 AMS、WMS、PMS 等服務,等這些服務啟動之后,AMS 中就會打開 Launcher 應用的 home Activity,最終就看到了手機的 "桌面"。

  1. 啟動電源以及系統啟動

加載引導程序Bootloader到RAM,然后執行。

  1. 引導程序BootLoader啟動

引導程序BootLoader是在Android操作系統開始運行前的一個小程序,它的主要作用是把系統OS拉起來并運行。

  1. Linux內核啟動

內核啟動時,設置緩存、被保護存儲器、計劃列表、加載驅動。當內核完成系統設置,它首先在系統文件中尋找init.rc文件,并啟動init進程。

  1. init進程啟動

init進程是系統空間內的第一個進程,進行初始化和啟動屬性服務,在main方法中進行,包括初始化資源文件和啟動一系列的屬性服務。通過執行init.rc文件的腳本文件來啟動Zygote進程。

  1. Zygote進程啟動

所有的應用程序包括system系統進程 都是zygote進程負責創建,因此zygote進程也被稱為進程孵化器,它創建進程是通過復制自身來創建應用進程,它在啟動過程中會在內部創建一個虛擬機實例,所以通過復制zygote進程而得到的應用進程和系統服務進程都可以快速地在內部的獲得一個虛擬機實例拷貝。

  1. SystemServer進程啟動

啟動Binder線程池和SystemServiceManager,systemServiceManger主要是對系統服務進行創建、啟動和生命周期管理,就會啟動各種系統服務。(android中最核心的服務AMS就是在SystemServer進程中啟動的)

  1. Launcher啟動

Launcher組件是由之前啟動的systemServer所啟動的
這也是andorid系統啟動的最后一步,launcher是andorid系統home程序,主要是用來顯示系統中已安裝的應用程序。 launcher應用程序的啟動會通過請求packageManagerService返回系統中已經安裝的應用信息,并將這些應用信息通過封裝處理成快捷列表顯示在系統屏幕上,這樣咱們就可以單擊啟動它們。
被SystemServer進程啟動的ActivityManagerService會啟動Launcher,Launcher啟動后會將已安裝應用的快捷圖標顯示到界面上。

2. system_server 為什么要在 Zygote 中啟動,而不是由 init 直接啟動呢?

Zygote 作為一個孵化器,可以提前加載一些資源,這樣 fork() 時基于 Copy-On-Write 機制創建的其他進程就能直接使用這些資源,而不用重新加載。比如 system_server 就可以直接使用 Zygote 中的 JNI函數、共享庫、常用的類、以及主題資源。

3. 為什么要專門使用 Zygote 進程去孵化應用進程,而不是讓 system_server 去孵化呢?

首先 system_server 相比 Zygote 多運行了 AMS、WMS 等服務,這些對一個應用程序來說是不需要的。另外進程的 fork() 對多線程不友好,僅會將發起調用的線程拷貝到子進程,這可能會導致死鎖,而system_server 中肯定是有很多線程的。

4. 上面具體是怎么導致死鎖的嗎?

  • 在 POSIX 標準中,fork 的行為是這樣的:復制整個用戶空間的數據(通常使用 copy-on-write 的策略,所以可以實現的速度很快)以及所有系統對象,然后僅復制當前線程到子進程。這里:所有父進程中別的線程,到了子進程中都是突然蒸發掉的

  • 對于鎖來說,從 OS 看,每個鎖有一個所有者,即最后一次 lock 它的線程。假設這么一個環境,在 fork之前,有一個子線程 lock 了某個鎖,獲得了對鎖的所有權。fork 以后,在子進程中,所有的額外線程都人間蒸發了。而鎖卻被正常復制了,在子進程看來,這個鎖沒有主人,所以沒有任何人可以對它解鎖。當子進程想 lock 這個鎖時,不再有任何手段可以解開了。程序發生死鎖

5. Zygote 為什么不采用 Binder 機制進行 IPC 通信?

Binder 機制中存在 Binder 線程池,是多線程的,如果 Zygote 采用 Binder 的話就存在上面說的

fork() 與 多線程的問題了。其實嚴格來說,Binder 機制不一定要多線程,所謂的 Binder 線程只不過是在循環讀取 Binder 驅動的消息而已,只注冊一個 Binder 線程也是可以工作的,比如 service manager就是這樣的。實際上 Zygote 盡管沒有采取 Binder 機制,它也不是單線程的,但它在 fork() 前主動停止了其他線程,fork() 后重新啟動了。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容