歡迎光臨
每天分享高質量文章

Linux 的啟動流程

本篇的重點是講解設備和驅動的啟動流程,設備和驅動的流程是整個內核啟動的核心,也是工作中最常面對的問題。出於知識點的系統性考慮,在進入主題之前我們先看下整個 Linux 在 ARM 中的啟動流程如何。

Uboot 的啟動流程

ARM Linux 的啟動流程大致為:Uboot → Kernel → Root filesystem。Uboot 在上電的時候就拿到 CPU 的控制權,實現了硬體的初始化。具體是怎麼實現的呢?一起來看一下,CPU 的內部集成了小容量的 Sram,而 PC 指標一上電就指向 Sram 的起始地址 0x00000000,所以一上電 Uboot 代碼就得到了運行。

Uboot 拿到 CPU 使用權就開始做初始化工作,比如關閉看門狗、設置 CPU 運行樣式、設置堆棧、初始化記憶體、網卡、nand flash 等,最後把 Linux 內核加載到記憶體中。

  • 初始化 RAM

因為內核要在 RAM 中運行,所以在呼叫內核之前必須初始化和設置 RAM,為呼叫內核做好準備。

  • 初始化串口

內核在啟動過程中可以將信息通過串口輸出,這樣就可以清楚的知道內核啟動信息。雖然串口不是 Uboot 必須要完成的工作,但是通過串口可以方便除錯 Uboot 和內核的各種信息。

  • 檢測處理器型別

Uboot 在呼叫內核前需要檢測系統的處理器型別,並將其儲存在某個變數中提供給內核,內核在啟動過程中會根據該處理器的型別呼叫相應的初始化程式。

  • 設置內核啟動引數

內核在啟動過程中會根據該啟動引數進行相應的初始化工作。

  • 呼叫內核鏡像

值得註意的是儲存 Uboot 的儲存器不同,Uboot 的執行過程也並不相同,一般來講 Flash 分為 nor Flash 和 nand Flash 兩種:nor Flash 支持芯片內執行(XIP,eXecute In Place),這樣代碼可以在 Flash 上直接執行而不必複製到 RAM 中去執行。

但是 nand Flash 並不支持 XIP,所以要想執行 nand Flash 上的代碼,必須先將其複製到 RAM 中去,然後跳到 RAM 中去執行。如果內核存放在 nor Flash 中,那麼可直接跳轉到內核中去執行。但通常由於在 nor Flash 中執行代碼會有種種限制,而且速度也遠不及 RAM 快,所以一般的嵌入式系統都是將內核覆制到 RAM 中,然後跳轉到 RAM 中去執行。不論哪種情況,在跳到內核執行之前 CPU 的暫存器必須滿足以下條件:r0 = 0,r1 = 處理器型別,r2 = 標記串列在 RAM 中的地址。

Linux 內核的啟動流程(設備和驅動的加載)

關於 Uboot 的啟動本課程不做詳細介紹,因為本課程的主要內容是內核。在講述內核啟動之前讓我們先瞭解下內核的組成結構:

其中,

(1)vmlinusx 是 ELF 格式的 Object 檔案,這種檔案只是各個原始碼經過連接以後得到的檔案,並不能在 ARM 平臺上運行。

(2)經過 objcopy 這個工具轉換以後,得到了二進制格式檔案 Image,Image 檔案相比於 vmlinusx 檔案,除了格式不同以外,還被去除了許多註釋和除錯的信息。

(3)Image 檔案經過壓縮以後得到了 piggy.gz,這個檔案僅僅是 Image 的壓縮版,並無其他不同。

(4)接著編譯生成另外幾個模塊檔案 misc.o、big_endian.o、head.o、head-xscale.o,這幾個檔案組成一個叫 Bootstrap Loader 的組件,又叫引導程式,編譯生成 piggy.o 檔案。

(5)最後 piggy.o 檔案和 Bootstrap Loader 組成一個 Bootable Kernel Image 檔案(可啟動檔案)。

經過上面的分析不難知道 piggy.o 就是內核鏡像,而剩下的幾個檔案就組成了引導程式。知道了內核的組成結構,Uboot 就是按照內核的組成結構一層一層剝開然後引導內核的:

可以說 start_kernel() 之前的所有工作都是為了將環境準備好,滿足 start_kernel() 的要求,然後由 start_kernel() 開始進行內核的加載:

關於 start_kernl() 函式的內容太多,可以通過紅色回呼函式看出,start_kernel() 函式基本是在回呼很多對應的註冊函式。為了本系列課程的結構性這裡就不展開所有知識點講解,本篇內容接著前一篇設備樹的內容重點講解下設備和驅動的匹配過程。

還記得上一篇講到的設備樹三大作用嗎?

  • 平臺標識;
  • 運行時配置;
  • 設備信息集合。

接下來我們就看看內核在啟動的時候是如何尋找設備,驅動又如何和設備系結的。

首先在平臺目錄下可以看到有很多平臺描述的檔案,如圖:

有那麼多的平臺,我們到底要執行哪個平臺是首先要考慮的事情。這也是設備三大功能的第一個功能——平臺標識。

  • 設備樹里有對設備根節點的 Compatible 描述,平臺檔案里有對 __initconst 的描述,如果兩個欄位一致則找到了對應的板級檔案,這樣就通過設備樹把要用的設備平臺與其他平臺區分開來了,如圖:

找到平臺後就可以根據回呼函式的指標呼叫該平臺的註冊函式。這裡以飛思卡爾 imx.6dl 平臺為例,回呼的時候會呼叫 imx6q_init_machine() 函式,如下:

這裡補充一個知識點,細心的讀者也許發現了在 Compatible 欄位里用逗號分隔了兩個字串。板級匹配的時候用的是哪個字串,另外一個字串又是做什麼用?首先後面的欄位 “fsl,imx6dl” 是抽象共用平臺描述符,前面的欄位 “fsl,imx6dl-sabresd” 是通用平臺下的具體平臺描述符,可以理解為母板和子板的區別。在具體的子板檔案中我們可以通過前面的欄位進行設備信息的獲取,如圖:

  • 接著是運行時配置,讓內核在啟動的時候根據引數設置進行不同的處理。有經驗的讀者清楚在 Uboot 里也有對 Bootargs 的配置,這裡為什麼多此一舉呢,是為了在 Uboot 中更靈活的對內核啟動進行配置。

  • 最後的作用就是設備信息集合,這是設備和驅動匹配的核心,也是工作中面對最多的情況。出於這一作用的內容是工作中經常遇到的重點也是難點,我們專門用一篇內容來詳細講解各級設備是如何展開的,並且手把手教你如何定製一套自己的開發板全新案例。

赞(0)

分享創造快樂