Linux核心啟動過程和Bootloader(總述)

來源:互聯網
上載者:User

 

1.Linux核心啟動過程概述
   一個嵌入式 Linux 系統從軟體角度看可以分為四個部分:引導載入程式(Bootloader),Linux 核心,檔案系統,應用程式。其中 Bootloader是系統啟動或複位以後執行的第一段代碼,它主要用來初始化處理器及外設,然後調用 Linux 核心。Linux 核心在完成系統的初始化之後需要掛載某個檔案系統做為根檔案系統(Root Filesystem)。根檔案系統是 Linux 系統的核心組成部分,它可以做為Linux 系統中檔案和資料的儲存地區,通常它還包括系統設定檔和運行應用軟體所需要的庫。應用程式可以說是嵌入式系統的“靈魂”,它所實現的功能通常就是設計該嵌入式系統所要達到的目標。如果沒有應用程式的支援,任何硬體上設計精良的嵌入式系統都沒有實用意義。
 

2. Bootloader啟動過程

   Bootloader在運行過程中雖然具有初始化系統和執行使用者輸入的命令等作用,但它最根本的功能就是為了啟動 Linux 核心。

2.1 Bootloader的概念和作用

  Bootloader是嵌入式系統的引導載入程式,它是系統上電後啟動並執行第一段程式,其作用類似於 PC 機上的 BIOS。在完成對系統的初始化任務之後,它會將非易失性儲存空間(通常是Flash或DOC等)中的Linux 核心拷貝到 RAM 中去,然後跳轉到核心的第一條指令處繼續執行,從而啟動 Linux 核心。由此可見,Bootloader 和 Linux 核心有著密不可分的聯絡,要想清楚的瞭解 Linux核心的啟動過程,我們必須先得認識 Bootloader的執行過程,這樣才能對嵌入式系統的整個啟動過程有清晰的掌握

2.2 Bootloader的執行過程

   不同的處理器上電或複位後執行的第一條指令地址並不相同,對於 ARM 處理器來說,該地址為 0x00000000。對於一般的嵌入式系統,通常把 Flash 等非易失性儲存空間映射到這個地址處,而 Bootloader就位於該儲存空間的最前端,所以系統上電或複位後執行的第一段程式便是Bootloader。而因為儲存 Bootloader的儲存空間不同,Bootloader的執行過程也並不相同,下面將具體分

  嵌入式系統中廣泛採用的非易失性儲存空間通常是 Flash,而 Flash 又分為 Nor Flash 和Nand Flash 兩種。 它們之間的不同在於:Nor Flash 支援晶片內執行(XIP, eXecute In Place),這樣代碼可以在Flash上直接執行而不必拷貝到RAM中去執行。而Nand Flash並不支援XIP,所以要想執行 Nand Flash 上的代碼,必須先將其拷貝到 RAM中去,然後跳到 RAM 中去執行

2.3 Bootloader的功能

  (1)初始化 RAM

     因為 Linux 核心一般都會在 RAM 中運行,所以在調用 Linux 核心之前 bootloader 必須設定和初始化 RAM,為調用 Linux核心做好準備。初始化 RAM 的任務包括設定CPU 的控制寄存器參數,以便能正常使用 RAM 以及檢測RAM 大小等

  (2)初始化串口

    串口在 Linux 的啟動過程中有著非常重要的作用,它是 Linux核心和使用者互動的方式之一。Linux 在啟動過程中可以將資訊通過串口輸出,這樣便可清楚的瞭解 Linux 的啟動過程。雖然它並不是 Bootloader 必須要完成的工作,但是通過串口輸出資訊是調試Bootloader 和Linux 核心的強有力的工具,所以一般的 Bootloader 都會在執行過程中初始化一個串口做為調試連接埠

  (3)檢測處理器類型

    Bootloader在調用 Linux核心前必須檢測系統的處理器類型,並將其儲存到某個常量中提供給 Linux 核心。Linux 核心在啟動過程中會根據該處理器類型調用相應的初始化程式

  (4)設定 Linux啟動參數

    Bootloader在執行過程中必須設定和初始化 Linux 的核心啟動參數。目前傳遞啟動參數主要採用兩種方式:即通過 struct param_struct 和struct tag(標記列表,tagged list)兩種結構傳遞。struct param_struct 是一種比較老的參數傳遞方式,在 2.4 版本以前的核心中使用較多。從 2.4 版本以後 Linux 核心基本上採用標記列表的方式。但為了保持和以前版本的相容性,它仍支援
struct param_struct 參數傳遞方式,只不過在核心啟動過程中它將被轉換成標記列表方式。標記列表方式是種比較新的參數傳遞方式,它必須以 ATAG_CORE 開始,並以ATAG_NONE 結尾。中間可以根據需要加入其他列表。Linux核心在啟動過程中會根據該啟動參數進行相應的初始化工作

   (5)調用 Linux核心映像

    Bootloader完成的最後一項工作便是調用 Linux核心。如果 Linux 核心存放在 Flash 中,並且可直接在上面運行(這裡的 Flash 指 Nor Flash),那麼可直接跳轉到核心中去執行。但由於在 Flash 中執行代碼會有種種限制,而且速度也遠不及 RAM 快,所以一般的嵌入式系統都是將 Linux核心拷貝到 RAM 中,然後跳轉到 RAM 中去執行

   不論哪種情況,在跳到 Linux 核心執行之前 CPU的寄存器必須滿足以下條件:r0=0,r1=處理器類型,r2=標記列表在 RAM中的地址

3.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 儲存了處理器的 識別碼,r10 儲存了與處理器相關的 stru proc_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 核心便啟動完畢。該函數所做的具體工作有:調用 setup_arch()函數進行與體繫結構相關的第一個初始化工作;對不同的體繫結構來說該函數有不同的定義。對於 ARM 平台而言,該函數定義在arch/arm/kernel/Setup.c。它首先通過檢測出來的處理器類型進行處理器核心的初始化,然後通過
bootmem_init()函數根據系統定義的 meminfo 結構進行記憶體結構的初始化,最後調用paging_init()開啟 MMU,建立核心頁表,映射所有的實體記憶體和 IO空間。建立異常向量表和初始化中斷處理函數;初始化系統核心進程調度器和時鐘中斷處理機制;初始化串口控制台(serial-console);ARM-Linux 在初始化過程中一般都會初始化一個串口做為核心的控制台,這樣核心在啟動過程中就可以通過串口輸出資訊以便開發人員或使用者瞭解系統的啟動進程。建立和初始化系統 cache,為各種記憶體調用機制提供緩衝,包括;動態記憶體分配,虛擬檔案系統(VirtualFile
System)及頁緩衝。初始化記憶體管理,檢測記憶體大小及被核心佔用的記憶體情況;初始化系統的處理序間通訊機制(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 核心啟動完畢。

 

本篇文章來源於 Linux公社網站(www.linuxidc.com)  原文連結:http://www.linuxidc.com/Linux/2011-09/42229.htm

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.