在嵌入式系統開發中,Bootloader 和 Linux 核心扮演著至關重要的角色。Bootloader 負責初始化硬體並引導核心啟動,而核心則管理系統資源並提供各種服務。理解它們的運作機制對於構建穩定的嵌入式系統至關重要。本文將會深入探討 Bootloader(特別是 U-Boot)和 Linux 核心,從基礎概念到實際操作,涵蓋跨編譯、環境設定、命令使用、目錄結構解析等關鍵環節。同時,我們將以 Raspberry Pi 和 QEMU 為例,示範如何在模擬環境中進行系統啟動和測試,並簡要介紹 BusyBox 在嵌入式系統中的應用。
跨編譯
跨編譯則是指程式碼在一臺機器(宿主機)上編譯,但目標平臺是另一臺機器。這通常發生在目標平臺資源有限,無法在其上直接編譯程式碼的情況下。跨編譯允許開發者在更強大的宿主機上編譯程式,然後將編譯好的程式佈署到資源有限的目標平臺上。
設定工具鏈
為了實作跨編譯,需要設定適合的工具鏈。工具鏈是一套能夠支援跨編譯的工具集合,包括編譯器、連結器等。設定工具鏈的步驟通常包括下載和安裝工具鏈軟體,然後組態環境以使用這些工具。
工具鏈的特點
一個典型的工具鏈包括以下幾個部分:
- 編譯器(Compiler):負責將高階語言程式碼轉換成機器碼。
- 連結器(Linker):負責將多個物件檔案連結成可執行檔案。
- 函式庫檔案(Library):提供常用的函式和變數,以方便程式設計師使用。
- 標頭檔案(Header File):包含函式宣告、巨集定義等,供程式設計師使用。
例項:設定 Gene 工具鏈
假設我們已經下載並解壓縮了 Gene 工具鏈,現在需要探索它的特點並使用它進行跨編譯。首先,找到工具鏈的目錄,通常會有一個特定的版本號,如 2708
,這代表著工具鏈的版本。
步驟 1:組態環境
進入工具鏈目錄,組態環境以使用這些工具。這可能涉及設定路徑變數,使系統能夠找到工具鏈中的工具。
步驟 2:瞭解工具鏈結構
探索工具鏈目錄,瞭解它包含哪些工具和函式庫檔案。通常,會有多個目錄分別存放不同架構的工具和函式庫。
步驟 3:跨編譯範例
選擇一個範例程式,使用工具鏈進行跨編譯。這涉及使用特定的編譯器和連結器選項,以確保程式碼能夠在目標平臺上正確執行。
Bootloader 概念介紹
在嵌入式系統中,Bootloader 是一段非常重要的程式,負責初始化硬體並將作業系統載入記憶體中。它是系統啟動過程中的第一個程式,負責初始化硬體環境,然後將控制權交給作業系統。
Bootloader 的必要性
Bootloader 是系統啟動過程中的第一個程式,它負責初始化硬體,組態記憶體,然後將控制權交給作業系統。沒有 Bootloader,系統就無法啟動。
Bootloader 的功能
Bootloader 的主要功能包括:
- 初始化硬體環境
- 載入作業系統
- 組態記憶體
- 提供作業系統啟動所需的資源
Bootloader 的工作流程
Bootloader 的工作流程如下:
- 初始化硬體環境
- 載入作業系統
- 組態記憶體
- 將控制權交給作業系統
Bootloader 的型別
Bootloader 有多種型別,包括:
- Primary Bootloader:負責初始化硬體環境
- Secondary Bootloader:負責載入作業系統
Bootloader 的實作
Bootloader 的實作可以使用多種語言,包括 C、C++、組合語言等。Bootloader 的開發需要對硬體有深入的瞭解,同時也需要對作業系統有深入的瞭解。
Bootloader 的優點
Bootloader 的優點包括:
- 提供了一個安全的啟動過程
- 可以初始化硬體環境
- 可以組態記憶體
- 可以提供作業系統啟動所需的資源
Bootloader 的挑戰
Bootloader 的挑戰包括:
- 需要對硬體有深入的瞭解
- 需要對作業系統有深入的瞭解
- 需要處理多種不同的硬體平臺
Bootloader 的未來發展
Bootloader 的未來發展包括:
- 支援多種不同的硬體平臺
- 提供更安全的啟動過程
- 支援更多的作業系統
Bootloader 的實踐
Bootloader 的實踐包括:
- 使用 C 或 C++ 開發 Bootloader
- 使用組合語言開發 Bootloader
- 使用開源的 Bootloader 程式碼
Bootloader 的應用
Bootloader 的應用包括:
- 嵌入式系統
- 手機系統
- 電腦系統
- 其他需要啟動過程的系統
Bootloader 的設計
Bootloader 的設計需要考慮多種因素,包括:
- 硬體環境
- 作業系統
- 記憶體組態
- 安全性
Bootloader 的實作
Bootloader 的實作需要對硬體有深入的瞭解,同時也需要對作業系統有深入的瞭解。Bootloader 的實作需要使用多種語言,包括 C、C++、組合語言等。
graph LR A[Bootloader] --> B[初始化硬體環境] B --> C[載入作業系統] C --> D[組態記憶體] D --> E[將控制權交給作業系統]
內容解密:
上述程式碼展示了 Bootloader 的基本流程,包括初始化硬體環境、載入作業系統、組態記憶體、將控制權交給作業系統等步驟。
圖表翻譯:
此圖表示 Bootloader 的工作流程,從初始化硬體環境開始,然後載入作業系統,組態記憶體,最後將控制權交給作業系統。這個流程是 Bootloader 的核心部分,負責初始化硬體環境,然後將控制權交給作業系統。
Bootloader 基礎概念
Bootloader 是一種程式,負責初始化硬體、載入作業系統核心和根檔案系統。它是開機過程中的第一步,負責設定 SDRAM 控制器、序列埠除錯和開機媒體等。
Bootloader 型別
Bootloader 可分為兩類:PC 用的 Bootloader 和嵌入式裝置用的 Bootloader。PC 用的 Bootloader 包括 GRUB 和 LILO,而嵌入式裝置用的 Bootloader 包括 U-Boot 和 RedBoot。
U-Boot 概念
U-Boot 是一種開源的 Bootloader,廣泛用於嵌入式平臺。它的全名是 Universal Bootloader,簡稱 U-Boot。U-Boot 可以載入作業系統核心和根檔案系統,支援多種電腦架構,包括 ARM、ARM 64 位、x86、MIPS 和 microblaze 等。
U-Boot 架構
U-Boot 可以分為兩個階段:第一階段和第二階段。第一階段負責設定記憶體控制器和 SDRAM,第二階段負責載入作業系統核心和根檔案系統。U-Boot 也可以分為兩個部分:SPL 和 U-Boot。SPL 是一個小型的 Bootloader,負責基本的硬體設定,然後將控制權交給 U-Boot。
U-Boot 環境
U-Boot 環境是 U-Boot 中的一個重要部分,負責儲存 U-Boot 和板級相關的設定和資訊。U-Boot 環境支援多種命令,包括 printenv、setenv 和 saveenv 等。
U-Boot 編譯
要編譯 U-Boot,需要選擇正確的組態檔案。組態檔案中包含了硬體設定和其他相關資訊。要跨編譯 U-Boot 程式碼,需要在組態檔名稱後面新增 “_defconfig”。
# 跨編譯 U-Boot 程式碼
make rpi_3_32b_defconfig
make
U-Boot 命令
U-Boot 支援多種命令,包括:
- printenv:列出所有環境變數
- setenv:設定環境變數
- saveenv:儲存環境變數
# 列出所有環境變數
printenv
# 設定環境變數
setenv bootcmd "bootm 0x80000000"
# 儲存環境變數
saveenv
圖表翻譯:
graph LR A[Bootloader] --> B[設定記憶體控制器] B --> C[載入作業系統核心] C --> D[載入根檔案系統] D --> E[啟動作業系統]
這個圖表顯示了 Bootloader 的啟動過程,包括設定記憶體控制器、載入作業系統核心和根檔案系統等步驟。
U-Boot 環境與命令
U-Boot 是一種開源的 Bootloader,廣泛用於嵌入式系統。瞭解 U-Boot 的環境和命令是使用它的基礎。首先,printenv
命令用於顯示當前的 U-Boot 環境變數。setenv
命令則用於設定環境變數,但這些設定只會儲存在 RAM 中,重啟後會失效。若要儲存設定,需要使用 saveenv
命令,將設定從 RAM 載入到非易失性記憶體。
Boot 命令
boot
命令會執行預設的組態檔案並啟動板卡。除了 boot
之外,還有 boots
和 bootm
等命令。bootm
用於從記憶體啟動應用程式映像。
Go 命令
go
命令非常重要,因為它允許在不啟動核心的情況下執行應用程式。這是 Bootloader 的一項強大功能,讓開發者可以在不需要完整作業系統的情況下測試和執行程式。
記憶體相關命令
md
命令用於顯示記憶體或 DDR(RAM)內容。mm
命令則用於修改記憶體內容,適用於外部記憶體或 DDR 記憶體的修改。mmc
命令用於分析 MMC 記憶體。nfs
用於網路啟動。ping
命令用於檢查網路連線。
重置和環境變數命令
reset
命令會重置 U-Boot 並重新啟動系統。run
命令用於執行環境變數。
TFTP 和 USB 啟動
tftpboot
命令用於從 TFTP 伺服器啟動。usb
命令與 USB 啟動有關。
建立 U-Boot 環境
要為真正的 Raspberry Pi 建立 U-Boot 環境,首先需要下載 U-Boot 並設定環境。接下來,需要組態和建構 U-Boot。這個過程涉及到多個步驟,包括下載 U-Boot 程式碼、設定環境、組態和建構。
下載和組態 U-Boot
首先,需要下載 U-Boot 的原始碼。由於最新版本的 U-Boot 可能不支援目前 Ubuntu 中的 GCC,所以可能需要簽出到較舊的分支。然後,需要清除現有的組態並簽出到目標分支。
安裝 Device Tree 編譯器
Device Tree 是一個描述硬體裝置資訊的檔案,最新的 U-Boot 需要 Device Tree 編譯器來建構。安裝 Device Tree 編譯器是一個必要的步驟。
組態 Cross Compiler
組態 Cross Compiler 是為了能夠跨平臺編譯 U-Boot。這需要安裝適當的工具鏈並設定環境變數。
組態硬體
組態硬體涉及到設定 U-Boot 對特定硬體平臺的支援,例如 Raspberry Pi。在 configs
目錄中可以找到與不同硬體平臺相關的組態檔案。需要根據所使用的硬體選擇合適的組態檔案。
編譯 U-Boot
在組態完成後,就可以開始編譯 U-Boot。編譯過程會產生 U-Boot 的可執行檔,這個檔案可以燒錄到板卡中,以實作 Bootloader 的功能。
U-Boot 的組態檔案
U-Boot 的組態檔案包含了許多設定選項,包括硬體支援、記憶體組態等。這些設定對於 U-Boot 的正常運作至關重要。組態檔案中的一些重要設定包括 ARM 架構、BCM283X 架構(適用於 Raspberry Pi)、MMC、USB、GPIO 等。
組態檔案結構
組態檔案的結構清晰,有不同的部分對應不同的設定。例如,ARM 架構的設定、特定硬體平臺的設定等。瞭解這些設定可以幫助使用者自定義 U-Boot 的行為。
u-boot 的基本組態和構建
u-boot 是一個開源的引導載入程式,廣泛用於嵌入式系統。要開始使用 u-boot,我們需要進行一些基本的組態和構建。在這個過程中,我們將瞭解 u-boot 的一些基本特性和可用的功能。
首先,我們需要組態 u-boot。組態過程包括選擇目標板和設定各種引數。目標板是指 u-boot 將要執行的硬體平臺。在我們的例子中,我們將使用 Raspberry Pi 作為目標板。組態完成後,我們可以開始構建 u-boot。
構建 u-boot 的過程涉及編譯和連結。我們可以使用 -j4
引數來加速構建過程,這將利用四個 CPU 核心來平行編譯。構建完成後,我們將得到 u-boot 的二進位制檔案,包括 u-boot
和 u-boot.bin
。其中,u-boot.bin
是最終的可執行檔案,我們需要將其放入 SD 卡中,以便 Raspberry Pi 可以從中啟動。
為 QEMU 構建 u-boot
除了為 Raspberry Pi 構建 u-boot 外,我們還可以為 QEMU 構建 u-boot。QEMU 是一個模擬器,可以模擬各種硬體平臺。為 QEMU 構建 u-boot 的過程與為 Raspberry Pi 構建類似,但有一些不同之處。
首先,我們需要克隆 u-boot 程式碼函式庫,然後 checkout 到一個較舊的版本,因為最新版本可能不支援 QEMU 的 versatilepb 目標。之後,我們需要設定交叉編譯器,以便為 ARM 平臺編譯 u-boot。
組態和構建 u-boot 的過程與之前類似。我們需要組態 u-boot,為 versatilepb 目標設定引數,然後開始構建。構建完成後,我們將得到 u-boot 的二進位制檔案,包括 u-boot
和 u-boot.bin
。
在 QEMU 上執行 u-boot
要在 QEMU 上執行 u-boot,我們需要使用 QEMU 命令列工具,並提供必要的引數,包括 u-boot 二進位制檔案的路徑和其他組態引數。執行 QEMU 後,我們將看到 u-boot 的命令列介面,在這裡我們可以執行各種命令,包括列印環境變數 (printenv
) 等。
# 執行 QEMU 並載入 u-boot
qemu-system-arm -M versatilepb -m 128M -kernel u-boot.bin -serial stdio
在 u-boot 命令列介面中,我們可以使用 printenv
命令來檢視當前的環境變數設定,這些變數定義了板級的各種引數,如 baud 率、乙太網設定等。
# 在 u-boot 命令列中列印環境變數
printenv
總結
本文介紹了 u-boot 的基本組態、構建和執行過程,包括為 Raspberry Pi 和 QEMU 構建 u-boot,並在 QEMU 上執行 u-boot。透過這些步驟,我們可以更深入地瞭解 u-boot 的工作原理和其在嵌入式系統中的應用。下一步,我們將探索如何使用 TFTP 在 u-boot 中傳輸檔案,這是網路通訊中的一個重要方面。
Linux 核心概述
Linux 核心是作業系統的核心,負責管理系統的資源和提供基本的服務。沒有核心,作業系統就無法正常運作。核心的主要功能包括程式管理、記憶體管理、裝置控制、網路管理等。使用者若要存取任何資源,必須先向核心請求許可。
核心架構
作業系統可以分為三個層級:硬體層、核心層和使用者層。硬體層負責管理硬體裝置,核心層負責控制系統的資源,使用者層則是應用程式和工具的所在地。當使用者想要存取硬碟時,需要向核心請求許可,核心層會檢查是否有存取許可權,如果有,則允許使用者存取硬碟。
核心的必要性
即使沒有核心,也可以從 u-boot 執行應用程式,但是這樣做缺乏多個應用程式例項的優點,也沒有記憶體保護機制。這意味著無法執行多執行緒應用程式,且記憶體管理會出現問題。核心提供了記憶體管理、程式管理和裝置控制等功能,沒有這些功能,作業系統就無法順暢運作。
目錄結構
Linux 核心的目錄結構如下:
graph TD A[根目錄] --> B[include] A --> C[arch] A --> D[drivers] A --> E[fs] A --> F[init] A --> G[ipc] A --> H[kernel] A --> I[lib] A --> J[mm] A --> K[net] A --> L[sound] A --> M[usr]
目錄解密:
- 根目錄:Linux 核心的根目錄,包含所有核心檔案和目錄。
- include:包含核心的頭檔案。
- arch:包含與架構相關的程式碼。
- drivers:包含硬體裝置驅動程式。
- fs:包含檔案系統相關程式碼。
- init:包含初始化程式碼。
- ipc:包含程式間通訊相關程式碼。
- kernel:包含核心的主要程式碼。
- lib:包含核心的函式函式庫。
- mm:包含記憶體管理相關程式碼。
- net:包含網路管理相關程式碼。
- sound:包含音訊管理相關程式碼。
- usr:包含使用者層的程式碼。
圖表翻譯:
此圖表展示了 Linux 核心的目錄結構,根目錄包含所有核心檔案和目錄。每個目錄都有其特定的功能,例如 include 目錄包含核心的頭檔案,arch 目錄包含與架構相關的程式碼等。這個圖表幫助我們瞭解 Linux 核心的組織結構和各個部分的功能。
掌握 Linux 核心目錄結構的重要性
要深入理解 Linux 核心的運作機制,掌握其目錄結構是非常重要的。透過瞭解核心的組織方式,您可以輕鬆找到需要修改的程式碼,以適應您自定義的裝置或板卡。例如,如果您想要修改實際硬體裝置,如 Raspberry Pi 或其他 Linux 板卡,您需要知道哪部分程式碼需要修改。假設您已經修改了 RAM 的大小,或者改變了從 MMC 到 NAND 或 NOR 的啟動選項,甚至增加了 NAND 或 NOR 的大小,您就需要知道驅動程式所在的位置,以便您可以瀏覽到該目錄,瞭解程式碼並進行修改。
但是,在進行這些修改之前,瞭解基本驅動程式的概念和位置是至關重要的。為了理解核心原始碼,您不需要下載它,可以直接存取核心原始碼倉函式庫。以 Linux 4.9.142 核心原始碼為例,您可以看到其中包含了各種驅動程式和不同的資料夾。
核心目錄結構概覽
- Documentation: 最重要的資料夾之一,包含了有助於理解程式碼工作流程和其他內容的檔案。
- arch: 與架構相關的資料夾,包含了各種架構(如 arm、arm64、MIPS、microblaze 等)的程式碼。
- block: 塊裝置驅動程式的資料夾。
- drivers: 核心中最重要的部分,包含了大多數驅動程式的程式碼,佔據了核心程式碼的大部分大小。其中包括字元驅動程式、時鐘驅動程式、GPIO 驅動程式、I2C 驅動程式、MMC 驅動程式等。
- fs: 檔案系統的資料夾,包含了 ext3、ext4、NFS 等檔案系統的驅動程式。
- include: 包含了 Linux 核心的標頭檔案。
- init: 初始化程式碼,當核心啟動時執行的程式碼。
- kernel: 架構依賴的核心程式碼。
- lib: 用於構建 Linux 核心的函式庫。
- mm: 記憶體管理的資料夾。
- net: 網路相關的實用程式,如 IPv4、IPv6 等。
- scripts: 用於核心初始組態的指令碼資料夾。
瞭解這些資料夾的功能和位置,可以幫助您更好地掌握 Linux 核心的結構和工作原理,並在需要時進行有效的修改和定製。
混合語言開發環境下的板級支援套件(BSP)設計
在進行板級支援套件(BSP)的設計時,我們需要考慮到多種因素,包括硬體架構、晶片級別支援以及板級別支援。首先,我們需要了解主線核心對於開發板的支援情況,例如 Raspberry Pi 和 BeagleBone Black 等。然而,在大多數情況下,我們需要從板級或晶片供應商獲得 BSP。這些 BSP 可能會落後於主線核心,但不同供應商的支援程度也各不相同。因此,選擇合適的供應商以獲得適當的支援至關重要。
板級支援套件的分類
板級支援套件可以分為三個類別:架構級別、晶片級別和板級別。架構級別支援包括 ARM、PowerPC 等,而晶片級別支援則是針對特定的晶片,如 Raspberry Pi 或 TI 板相關支援。板級別支援則需要了解評估板支援和 COTS 板支援。評估板支援包括 TI EVM 和 Raspberry Pi 板,而 COTS 板則是由玄貓提供的。
COTS 板的特點
COTS 板,全稱為商用現貨(Commercial Off-The-Shelf),是指可以直接使用的板卡。這類板卡設計簡單,易於安裝,大部分軟體都可以直接購買使用,包括作業系統、辦公軟體、文書處理和郵件程式等。COTS 板的主要優點在於其批次生產導致的低成本。
晶片級別和板級別支援
晶片級別支援通常由玄貓完成,並且這種支援包含在核心樹中。另一方面,板級別支援也是由玄貓完成,根據 SOC 核心,並需要與板製造商聯絡以瞭解未來的變化或更新。
在 QEMU 上啟動 Raspberry Pi
要在 QEMU 上啟動 Raspberry Pi,首先需要下載必要的前置條件,包括跨編譯的核心程式碼和根檔案系統(rootfs)。然後,可以使用 QEMU 來啟動 Raspberry Pi,並瞭解各個引數的作用,包括核心檔案、initrd、根檔案系統等。
BusyBox 的理解
BusyBox 是嵌入式 Linux 中的一個重要工具,被稱為「瑞士軍刀」。它將許多常見的 Linux 公用程式合併成一個小型可執行檔案。每個公用程式都是 BusyBox 的符號連結,這意味著所有這些命令都是從 BusyBox 衍生而來的。 BusyBox 中的公用程式通常比其完整功能的 GNU 對應物具有更少的選項。
嵌入式系統開發的核心,Bootloader 和 Linux 核心,正朝向更精簡、高效的方向發展。本文深入剖析了從跨編譯工具鏈的設定、Bootloader 的工作原理、U-Boot 的組態與使用,到 Linux 核心架構及目錄結構,涵蓋了嵌入式系統開發的關鍵環節。分析了Bootloader 如何初始化硬體並引導系統,以及 U-Boot 的組態、編譯和常用命令,更進一步探討了在 QEMU 環境下模擬 Raspberry Pi 的流程,以及 BusyBox 在嵌入式系統中的重要作用。技術堆疊的各層級協同運作中體現,掌握這些技術對於構建和定製嵌入式系統至關重要。對於想要深入學習嵌入式系統的開發者,理解並掌握這些基礎概念是不可或缺的。玄貓認為,隨著物聯網和邊緣計算的蓬勃發展,精簡高效的嵌入式系統開發技術將持續成為市場的焦點,值得開發者投入更多精力深入研究。