圖解 Android 系列(一)揭祕 Android 系統啟動過程

jeanboy發表於2019-03-27

當我們按下手機開機按鍵後,手機就會啟動了。然後會看到 Logo,開機動畫,最後會進入到手機桌面(Launcher),手機也就啟動完成了。

我一直搞不明白,這個過程到底做了什麼?為什麼按一個按鍵,手機就啟動了呢?

隨著對 Android 的瞭解越來越多,直到閱讀了原始碼,才逐漸解答了我的疑惑。如果你也有相同疑惑,請繼續往下看,我將從原始碼的角度分析下 Android 系統啟動的整個流程。

計算機是如何啟動的?

智慧手機相當於是精簡版的計算機,我們先看看計算機是如何啟動的?

首先熟悉一些概念,我們知道計算機的硬體包括:CPU、記憶體、硬碟、顯示卡、顯示器、鍵盤、滑鼠等其他輸入輸出裝置。所有的軟體(比如:作業系統)都是存放在硬碟上的,程式執行時需要將程式從硬碟上讀取到記憶體中,然後載入到 CPU 中來執行的。

當我們按下開機鍵時,此時記憶體中什麼都沒有,因此需要藉助某種方式,將作業系統載入到記憶體中,而完成這項任務的就是 BIOS。

BIOS

BIOS: Basic Input/Output System(基本輸入輸出系統),在 IBM PC 相容系統上,是一種業界標準的韌體介面(來自維基百科)。

BIOS 一般是主機板晶片上的一個程式,計算機通電後,第一件事就是讀取它。BIOS 程式啟動後,首先會檢查計算機硬體能否滿足執行的基本條件,這個過程叫做「硬體自檢」(Power-On Self-Test),縮寫為 POST。

自檢過程中如果硬體出現問題,主機板會發出不同含義的蜂鳴,啟動將中止。 如果沒有問題,螢幕就會顯示出 CPU,記憶體,硬碟等資訊。也就是我們按下開機按鍵後經常看到的,螢幕上快速滾動各種提示。

硬體自檢圖

硬體自檢完成後,BIOS 會把控制權轉交給下一階段的啟動程式。

這時 BIOS 需要知道,下一階段的啟動程式到底存放在哪一個裝置當中。也就是說 BIOS 需要有一個外部儲存裝置的排序,排在前面的裝置就是優先轉交控制權的裝置。 這種排序叫做啟動排序,也就是我們平時進入 BIOS 介面(比如:按 F9/F10 等等,這裡裝過系統的小夥伴應該比較熟悉)時能看到的 Boot Sequence。

啟動順序圖

如果我們沒有進行特殊操作的話,那麼 BIOS 就會按照這個啟動順序將控制權交給下一個儲存裝置。 我們在使用 U 盤或者光碟之類的安裝系統時就是在這裡將啟動順序改變了,將本來要移交給硬碟的控制權交給了 U 盤或者光碟。

主開機記錄

BIOS 按照啟動順序,把控制權轉交給排在第一位的儲存裝置。第一儲存裝置被啟用後,計算機讀取該裝置的第一個扇區,也就是讀取最前面的 512 個位元組。

這最前面的 512 個位元組,就叫做主開機記錄(Master boot record,縮寫為 MBR)。

主開機記錄 MBR 是位於磁碟最前邊的一段引導程式碼。它負責磁碟作業系統對磁碟進行讀寫時分割槽合法性的判別、分割槽引導資訊的定位,它由磁碟作業系統在對硬碟進行初始化時產生的。

硬碟的主開機記錄 MBR 是不屬於任何一個作業系統的,它先於所有的作業系統而被調入記憶體,併發揮作用,然後才將控制權交給主分割槽內的作業系統,並用主分割槽資訊表來管理硬碟。

MBR 只有512個位元組,放不了太多東西。 它的主要作用是,告訴計算機到硬碟的哪一個位置去找作業系統。

主開機記錄由三個部分組成:

  • 第 1 - 446 位元組:呼叫作業系統的機器碼。
  • 第 447 - 510 位元組:分割槽表(Partition table)。
  • 第 511 - 512 位元組:主開機記錄簽名(0x55 和 0xAA)。

其中,第二部分「分割槽表」的作用,就是將硬碟分成若干個區。硬碟分割槽有很多好處,考慮到每個區可以安裝不同的作業系統,「主開機記錄」因此必須知道將控制權轉交給哪個區。

分割槽表的長度只有 64 個位元組,裡面又分成四項,每項 16 個位元組。所以,一個硬碟最多隻能分四個一級分割槽,又叫做「主分割槽」。

四個主分割槽裡面,只有一個是啟用的。計算機會讀取啟用分割槽的第一個扇區,叫做卷引導記錄(Volume boot record,縮寫為 VBR )。卷引導記錄的主要作用是,告訴計算機,作業系統在這個分割槽裡的位置。

如果這 512 個位元組的最後兩個位元組是 0x55 和 0xAA ,表明這個裝置可以用於啟動;如果不是,表明裝置不能用於啟動,控制權於是被轉交給啟動順序中的下一個裝置。

當計算機載入 MBR 後,計算機會從 MBR 中知道當前硬碟的檔案格式、硬碟分割槽情況、系統盤存放位置等資訊,然後控制權將被移交給了系統盤所在的分割槽。

如果硬碟上裝有多個系統的話,在找到可用的 MBR 後,計算機從 MBR 中讀取前面 446 位元組的機器碼之後,不再把控制權轉交給某一個分割槽,而是執行事先安裝的啟動管理器(boot loader),由使用者選擇啟動哪一個作業系統。

Windows 中是 Boot Manager。

Windows Boot Manager

Linux 環境中,目前最流行的啟動管理器是 Grub。

Linux Grub

載入核心階段

選擇完作業系統後,控制權轉交給作業系統,作業系統的核心首先被載入記憶體。

以 Linux 系統為例,先載入 /boot 目錄下面的 kernel。 核心載入成功後,第一個執行的程式是 /sbin/init。 它根據配置檔案(Debian 系統是 /etc/initab )產生 init 程式。 這是 Linux 啟動後的第一個使用者程式,pid 程式編號為 1,其他程式都是它的後代。

然後,init 程式載入系統的各個模組,比如:視窗程式和網路程式,直至執行 /bin/login 程式,跳出登入介面,等待使用者輸入使用者名稱和密碼。

至此,計算機全部啟動過程完成。

Android 系統啟動過程

瞭解了計算機的啟動流程,我們再來看一下 Android 系統的啟動過程。Android 系統是基於 Linux 核心的,所以啟動過程與 Linux 系統有很多相似的地方。

由於 Android 屬於嵌入式裝置,並沒有像計算機上那樣的 BIOS 程式, 取而代之的是 Bootloader —— 系統啟動載入器。 它類似於 BIOS,在系統載入前,用以初始化硬體裝置,建立記憶體空間的映像圖,為最終呼叫系統核心準備好環境。

Android Bootloader

在 Android 裡沒有硬碟,而是 ROM,它類似於硬碟存放作業系統,使用者程式等。 ROM 跟硬碟一樣也會劃分為不同的區域,用於放置不同的程式,在 Android 中主要劃分為一下幾個分割槽:

  • /boot:存放載入程式,包括核心和記憶體操作程式。
  • /system:相當於電腦 C 盤,存放 Android 系統及系統應用。
  • /recovery:恢復分割槽,可以進入該分割槽進行系統恢復。
  • /data:使用者資料區,包含了使用者的資料:聯絡人、簡訊、設定、使用者安裝的程式。
  • /cache:安卓系統快取區,儲存系統最常訪問的資料和應用程式。
  • /misc:包含一些雜項內容,如系統設定和系統功能啟用禁用設定。
  • /sdcard:使用者自己的儲存區,可以存放照片,音樂,視訊等檔案。

Bootloader

那麼 Bootloader 是如何被載入的呢?

與計算機啟動過程類似,當按下電源按鍵後,引導晶片程式碼開始從預定義的地方(固化在 ROM 中的預設程式碼)開始執行,晶片上的 ROM 會尋找 Bootloader 程式碼,並載入到記憶體(RAM)中。

接著 Bootloader 開始執行,Bootloader 會讀取 ROM 找到作業系統並將 Linux 核心載入到 RAM 中。

當 Linux 核心啟動後會初始化各種軟硬體環境,載入驅動程式,掛載根檔案系統,Linux 核心載入的最後階段會啟動並執行第一個使用者空間程式 init 程式。

Linux 核心

Android 系統本質上就是一個基於 Linux 核心的作業系統,與 Ubuntu Linux、Fedora Linux 類似,我們要了解 Android 系統,必定先要了解一些 Linux 核心的知識。

Linux 核心的東西特別多,本文也不可能全部講完,本文主要介紹 Android 系統啟動流程,所以這裡主要介紹一些核心啟動相關的知識。

Linux 核心啟動過程主要涉及 3 個特殊的程式,swapper 程式(又稱為 idle 程式,PID = 0), init 程式(PID = 1)和 kthreadd 程式(PID = 2),這三個程式是核心的基礎。

  • idle 程式是 Linux 系統第一個程式,是 init 程式和 kthreadd 程式的父程式。
  • init 程式是 Linux 系統第一個使用者程式,是 Android 系統應用程式的始祖,我們的 app 都是直接或間接以它為父程式。
  • kthreadd 程式是 Linux 系統核心管家,所有的核心執行緒都是直接或間接以它為父程式。

程式關係圖

idle 程式

很多文章講 Android 都從 init 程式講起,它的程式號是 1,既然程式號是 1,那麼有沒有程式號是 0 的程式呢?其實是有的。

這個程式名字叫 init_task,後期會退化為「idle」,它是 Linux 系統的第一個程式(init 程式是第一個使用者程式),也是唯一一個沒有通過 fork 或者 kernel_thread 產生的程式,它在完成初始化操作後,主要負責程式排程、交換。

idle 程式是 Linux 系統的第一個程式,程式號是 0,在完成系統環境初始化工作之後,開啟了兩個重要的程式,init 程式和 kthreadd 程式,執行完建立工作之後,開啟一個無限迴圈,負責程式的排程。

kthreadd 程式

kthreadd 程式由 idle 通過 kernel_thread 建立,並始終執行在核心空間, 負責所有核心執行緒的排程和管理,所有的核心執行緒都是直接或者間接的以 kthreadd為 父程式。

init 程式

init 程式啟動分為前後兩部分,前一部分是在核心啟動的,主要是完成建立和核心初始化工作,內容都是跟 Linux 核心相關的;後一部分是在使用者空間啟動的,主要完成 Android 系統的初始化工作。

Android 系統一般會在根目錄下放一個 init 的可執行檔案,也就是說 Linux 系統的 init 程式在核心初始化完成後,就直接執行 init 這個檔案,這個檔案的原始碼在 /system/core/init/init.cpp

介紹到這裡,我們來看下目前 Android 系統做了哪些工作,如下圖:

init 程式

總結

本文介紹的內容相對比較簡單,主要是想帶大家瞭解了一下計算機啟動的流程,瞭解一下 Android 系統從按下電源鍵到 init 程式開始啟動的過程,為以後的章節做下準備。

接下來的章節會涉及到 Android 系統原始碼,建議大家準備一下系統原始碼,對著原始碼學習會更加印象深刻,事半功倍。

原始碼基於:Android Oreo(8.0)

關於原始碼下載

可以參考下面方式下載 Android 原始碼:

也可以線上檢視原始碼:

原始碼檢視工具

原始碼檢視工具推薦使用 Visual Studio Code 我用的就是這個,也可以使用 Source Insight 不過是要付費的,並且只有 Windows 平臺,大家根據個人喜好自行選擇就好。

參考資料

我的 GitHub

github.com/jeanboydev

我的公眾號

歡迎關注我的公眾號,分享各種技術乾貨,各種學習資料,職業發展和行業動態。

Android 波斯灣

技術交流群

歡迎加入技術交流群,來一起交流學習。

QQ 技術交流群

QQ 技術交流群

相關文章