arm linux啟動過程分析
在 bootloader將 linux 核心映像拷貝到 ram 以後,可以通過下例**啟動linux 核心:call_linux(0,machine_type, kernel_params_base)。 其中,machine_tpye是 bootloader檢測出來的處理器型別, kernel_params_base 是啟動引數在 ram 的位址。通過這種方式將 linux 啟動需要的引數從 bootloader傳遞到核心。
linux
核心有兩種映像:一種是非壓縮核心,叫
image
,另一種是它的壓縮版本,叫
zimage
。根據核心映像的不同,
linux
核心的啟動在開始階段也有所不同。zimage 是 image經過壓縮形成的,所以它的大小比 image 小。但為了能使用 zimage,必須在它的開頭加上解壓縮的**,將 zimage 解壓縮之後才能執行,因此它的執行速度比 image 要慢。但考慮到嵌入式系統的儲存空容量一般比較小,採用 zimage 可以占用較少的儲存空間,因此犧牲一點效能上的代價也是值得的。所以一般的嵌入式系統均採用壓縮核心的方式。
對於arm
系列處理器來說,
zimage
的入口程式即為
arch/arm/boot/compressed/head.s
。它依次完成以下工作:開啟
mmu
和cache
,呼叫decompress_kernel()
解壓核心,最後通過呼叫
call_kernel()
進入非壓縮核心
image
的啟動。
下面將具體分析在此之後 linux 核心的啟動過程。
3.1 linux核心入口
linux 非壓縮核心的入口位於檔案/arch/arm/kernel/head-armv.s中的 stext 段。該段的基位址就是壓縮核心解壓後的跳轉位址。如果系統中載入的核心是非壓縮的 image,那麼bootloader將核心從 flash中拷貝到 ram 後將直接跳到該位址處,從而啟動 linux 核心。不同體系結構的 linux 系統的入口檔案是不同的,而且因為該檔案與具體體系結構有關,所以一般均用組合語言編寫。對基於 arm 處理的 linux 系統來說,該檔案就是head-armv.s。該程式通過查詢處理器核心型別和處理器型別呼叫相應的初始化函式,再建立頁表,最後跳轉到
start_kernel()
函式開始核心的初始化工作。
檢測處理器核心型別是在彙編子函式__lookup_processor_type中完成的。通過以下**可實現對它的呼叫:bl __lookup_processor_type。__lookup_processor_type呼叫結束返回原程式時,會將返回結果儲存到暫存器中。其中r8 儲存了頁表的標誌位,r9 儲存了處理器的 id 號,r10 儲存了與處理器相關的 struproc_info_list 結構位址。
檢測處理器型別是在彙編子函式__lookup_architecture_type 中完成的。與__lookup_processor_type類似,它通過**:「bl __lookup_processor_type」來實現對它的呼叫。該函式返回時,會將返回結構儲存在 r5、r6 和 r7 三個暫存器中。其中 r5 儲存了 ram 的起始基位址,r6 儲存了 i/o基位址,r7 儲存了 i/o的頁表偏移位址。當檢測處理器核心和處理器型別結束後,將呼叫
__create_page_tables
子函式來建立頁表,它所要做的工作就是將
ram
基位址開始的
4m空間的物理位址對映到
0xc0000000
開始的虛擬位址處。對筆者的 s3c2410 開發板而言,ram 連線到實體地址0x30000000 處,當呼叫__create_page_tables 結束後 0x30000000 ~ 0x30400000 實體地址將對映到0xc0000000~0xc0400000 虛擬位址處。
當所有的初始化結束之後,使用如下**來跳到
c 程式的入口函式
start_kernel()
處,開始之後的核心初始化工作:
b symbol_name(start_kernel)
3.2 start_kernel函式
start_kernel是所有 linux 平台進入系統核心初始化後的入口函式,它主要完成剩餘的與硬體平台相關的初始化工作,在進行一系列與核心相關的初始化後,呼叫第乙個使用者程序-init 程序並等待使用者程序的執行,這樣整個 linux 核心便啟動完畢。該函式所做的具體工作有:
1)
呼叫setup_arch()
函式進行與體系結構相關的第乙個初始化工作;
對不同的體系結構來說該函式有不同的定義。對於 arm 平台而言,該函式定義在arch/arm/kernel/setup.c。它首先通過檢測出來的處理器型別進行處理器核心的初始化,然後通過bootmem_init()函式根據系統定義的meminfo 結構進行記憶體結構的初始化,最後呼叫paging_init()開啟 mmu,建立核心頁表,對映所有的物理記憶體和 io空間。
2)
建立異常向量表和初始化中斷處理函式;
3) 初始化系統核心程序排程器和時鐘中斷處理機制;
4) 初始化串列埠控制台(
serial-console
);arm-linux 在初始化過程中一般都會初始化乙個串列埠做為核心的控制台,這樣核心在啟動過程中就可以通過串列埠輸出資訊以便開發者或使用者了解系統的啟動程序。
5)
建立和初始化系統
cache
,為各種記憶體呼叫機制提供快取,包括
;動態記憶體分配,虛擬檔案系統(
virtualfile system
)及頁快取。
6) 初始化記憶體管理,檢測記憶體大小及被核心占用的記憶體情況;
7) 初始化系統的程序間通訊機制(
ipc);
當以上所有的初始化工作結束後,start_kernel()函式會呼叫 rest_init()函式來進行最後的初始化,包括建立系統的第乙個程序-init 程序來結束核心的啟動。init程序首先進行一系列的硬體初始化,然後通過命令列傳遞過來的引數掛載根檔案系統。最後 init 程序會執行用 戶傳遞過來的「init=」啟動引數執行使用者指定的命令,或者執行以下幾個程序之一:
execve("/sbin/init",argv_init,envp_init);
execve("/etc/init",argv_init,envp_init);
execve("/bin/init",argv_init,envp_init);
execve("/bin/sh",argv_init,envp_init)。
當所有的初始化工作結束後,cpu_idle()函式會被呼叫來使系統處於閒置(idle)狀態並等待使用者程式的執行。至此,整個 linux 核心啟動完畢。
4. 結論
linux 核心是乙個非常龐大的工程,經過十多年的發展,它已從從最初的幾百 kb 大小發展到現在的幾百兆。清晰的了解它執行的每乙個過程是件非常困難的事。但是在嵌入式開發過程中,我們並不需要十分清楚 linux 的內部工作機制,只要適當修改 linux 核心中那些與硬體相關的部分,就可以將 linux 移植到其它目標平台上。通過對 linux 的啟動過程的分 析,我們可以看出哪些是和硬體相關的,哪些是 linux 核心內部已實現的功能,這樣在移植linux 的過程中便有所針對。而 linux核心的分層設計將使 linux 的移植變得更加容易。
ARM Linux啟動過程分析
5 呼叫 linux核心映像 bootloader完成的最後一項工作便是呼叫 linux核心。如果 linux 核心存放在 flash 中,並且可直接在上面執行 這裡的 flash 指 nor flash 那麼可直接跳轉到核心中去執行。但由於在 flash 中執行 會有種種限制,而且速度也遠不及 r...
詳解 ARM Linux啟動過程分析 2
origin 呼叫 linux核心映像 bootloader完成的最後一項工作便是呼叫 linux核心。如果 linux 核心存放在 flash 中,並且可直接在上面執行 這裡的 flash 指 nor flash 那麼可直接跳轉到核心中去執行。但由於在 flash 中執行 會有種種限制,而且速度也...
Linux啟動過程分析
boot loader 當cpu一上電,此時cpu必須從nor flash或者nand flash中取指令 直接從 nand flash取指令的ap為支援nand 啟動 nand boot 其實原理就是ap 內部的rom裡有一小段 包括nand flash驅動,它啟動從nand flash中讀取一段...