零基礎詳解:什么是Bootloader?U-Boot啟動流程全解析!
- 一、什么是Bootloader?
- 📌 舉個例子:
- 二、U-Boot 是什么?
- 三、U-Boot啟動過程:分為兩個階段
- 🔹 第一階段(匯編階段)
- 🔹 第二階段(C語言階段)
- 四、U-Boot是怎么啟動Linux內核的?
- 五、U-Boot與Linux之間的參數傳遞機制
- 💡 什么是 tag(啟動參數)?
- 六、為什么U-Boot要關閉 Cache 和 MMU?
- 七.為什么要給內核傳遞參數?
- 為什么要傳遞這些參數?
- 八、系統啟動完整流程回顧
- 九、總結一句話
- 📌 小貼士:常見面試題匯總
一、什么是Bootloader?
Bootloader(引導加載程序),是一段特殊的“小程序”,它運行在 系統剛上電時,主要目的是:
- 初始化硬件環境(如:內存、時鐘、中斷、串口等)
- 然后將 Linux內核從 flash(NAND, NOR FLASH,SD,MMC等)拷貝到SDRAM中,最后啟動Linux內核
通俗一點講,Bootloader 就像電腦中的 BIOS —— 當你按下電源開關,它最先啟動,完成最基礎的硬件檢測后,才將控制權交給操作系統(比如 Windows 或 Linux)。
📌 舉個例子:
項目 | PC系統 | 嵌入式Linux系統 |
---|---|---|
初始程序 | BIOS | Bootloader |
操作系統 | Windows | Linux |
存儲介質 | 硬盤 | Flash / SD卡等 |
二、U-Boot 是什么?
U-Boot(全稱為 Universal Bootloader)是一種 開源、通用的Bootloader,支持多種架構(ARM、MIPS、x86等),被廣泛應用于嵌入式Linux開發中。
它的主要任務:
- 初始化硬件資源(如內存、串口、存儲器等)
- 加載Linux內核到內存中
- 傳遞啟動參數給內核
- 啟動Linux內核
三、U-Boot啟動過程:分為兩個階段
🔹 第一階段(匯編階段)
此階段主要用 匯編代碼編寫,功能包括:
- 初始化時鐘系統
- 關閉看門狗(防止系統誤復位)
- 關閉中斷
- 啟動指令Cache(ICache)
- 關閉數據Cache與TLB
- 關閉MMU
- 初始化SDRAM(用于加載后續程序)
- 初始化NAND Flash
- 代碼重定位(將U-Boot代碼搬運至SDRAM中運行)
💡 注意:這部分代碼位置受限(通常在片內RAM或FLASH運行),所以通常要寫得很精煉。
🔹 第二階段(C語言階段)
這部分用 C語言編寫,主要功能是:
- 初始化串口(便于調試)
- 打印啟動信息
- 檢測內存映射情況
- 從存儲介質讀取Linux內核鏡像和根文件系統(initrd)將內核映象和根文件系統映象從 Flash上讀到SDRAM空間中
- 設置內核啟動參數
- 跳轉到Linux內核執行入口
四、U-Boot是怎么啟動Linux內核的?
當U-Boot完成自身工作后,就會把控制權交給Linux內核。那么,怎么跳轉呢?
👉 本質就是:修改PC寄存器的值為內核所在地址
這樣CPU下一條執行指令就來自Linux內核,開始正式啟動!
五、U-Boot與Linux之間的參數傳遞機制
這部分是面試常考,請務必掌握!
在跳轉到內核之前,U-Boot 會設置好三個寄存器:
寄存器 | 含義 | 示例說明 |
---|---|---|
R0 | 保持為0 | 固定寫0即可 |
R1 | 機器ID(開發板型號) | 如友善之臂開發板是199,見mach-types.h |
R2 | 參數的地址(tag結構體) | tag鏈表,里面保存了內存大小、命令行、根文件系統等 |
📌 這些參數是內核“啟動時所需的環境信息”,如果不給內核傳遞參數,它無法知道設備的具體配置!
- R0寄存器:設置為0,通常用于標識內核啟動時的參數。
- R1寄存器:傳遞給內核一個“機器ID”。這是一個唯一標識當前硬件平臺的ID,每個開發板或CPU都會有一個唯一的ID。內核會根據該ID來判斷是否支持當前硬件平臺。
- R2寄存器:存儲一個指向參數列表的指針,該列表包含了內核啟動時需要的所有環境信息(例如內存大小、內存地址、文件系統等)。
💡 什么是 tag(啟動參數)?
tag 是一組結構體,存放了啟動參數,形式如下:
struct tag {struct tag_header {uint32_t size; // tag大小(單位:4字節)uint32_t tag; // tag類型(如ATAG_MEM、ATAG_CMDLINE等)} hdr;union {struct tag_mem32 mem;struct tag_cmdline cmdline;// 其他類型參數...} u;
};
📌 常見的tag有:
- ATAG_CORE:表示起始
- ATAG_MEM:內存起始地址與大小
- ATAG_CMDLINE:命令行參數(如根文件系統路徑)
- ATAG_NONE:表示結束
六、為什么U-Boot要關閉 Cache 和 MMU?
- 上電后RAM未初始化前,Cache 不能正常工作。
- 如果數據 Cache 開啟了,會導致從未初始化的Cache中讀取錯誤數據,程序可能崩潰!
- 所以:必須關閉MMU和Data Cache,指令Cache可以開也可以不開。
七.為什么要給內核傳遞參數?
在啟動Linux內核之前,U-Boot完成了對硬件的基本初始化。此時,U-Boot已經“適應”了當前的開發板,但Linux內核并不一定能直接支持所有開發板。因為每個開發板的硬件配置都可能有所不同,例如不同的CPU型號、內存配置、外設接口等。而內核本身并不是為每個具體的開發板都做了硬件適配的,因此,內核在啟動時需要了解當前開發板的硬件環境。為此,U-Boot需要將這些硬件信息傳遞給內核,以幫助內核做相應的配置和適配。
為什么要傳遞這些參數?
- 硬件適配:Linux內核需要了解當前硬件平臺的配置(例如內存大小、CPU架構、設備樹等)才能正確配置和初始化硬件資源。如果沒有這些信息,內核就無法正確識別或初始化硬件設備。
- 啟動過程的靈活性:開發板通常會有不同的硬件平臺,U-Boot的作用就是提供一個通用的引導機制,而通過參數傳遞,U-Boot使得內核在加載后可以根據傳遞的參數調整自身的行為和配置。
- 內核啟動時的配置:內核需要根據不同的硬件環境來調整內存、CPU等硬件的使用模式。例如,在某些開發板上可能需要配置特定的硬件接口,或者根據內存的大小調整內核的行為。通過參數傳遞,內核能夠靈活地進行這些調整。
八、系統啟動完整流程回顧
下面是系統上電到進入Linux系統的完整流程圖:
[ 上電/復位 ]↓
[ ROM代碼執行 ]↓
[ 初始化外設 & SDRAM ]↓
[ 加載并執行U-Boot ]↓
[ U-Boot初始化硬件 ]↓
[ 加載Linux內核 + initrd ]↓
[ 設置啟動參數(R0, R1, R2)]↓
[ 跳轉到內核入口 ]↓
[ Linux內核初始化 ]↓
[ 掛載根文件系統 ]↓
[ 啟動用戶空間進程 ]
九、總結一句話
Bootloader 就是操作系統的“搬運工 + 啟動器”,而 U-Boot 就是最流行的 Bootloader,它讓 Linux 成為可能!
📌 小貼士:常見面試題匯總
面試問題 | 回答要點 |
---|---|
什么是Bootloader? | 啟動時執行的第一段程序,初始化硬件,加載并啟動內核 |
U-Boot做了哪些事? | 兩階段:匯編(初始化)、C語言(加載內核+參數設置+跳轉) |
U-Boot如何跳轉到Linux內核? | 設置PC寄存器為內核地址 |
參數是如何傳遞給內核的? | 用R0=0, R1=機器ID, R2=tag結構體地址傳遞 |
為什么要關閉Data Cache? | 避免RAM未初始化時Cache取錯誤數據導致程序異常 |
什么是tag結構?如何組織? | struct tag,含header+聯合體,ATAG_CORE起始、ATAG_NONE結束 |