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系統的入口檔案是不同的,而且因為該檔案與具體體系結構有關,所以一般均用組合語言編寫[3]。對基於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的移植變得更加容易。
嵌入式Linux啟動過程 2
3 linux核心的啟動過程 在 bootloader將 linux 核心映像拷貝到 ram 以後,可以通過下例 啟動 linux 核心 call linux 0,machine type,kernel params base 其中,machine tpye 是 bootloader檢測出來的處理器...
嵌入式Linux啟動過程 1
1 引言 linux最初是由瑞典赫爾辛基大學的學生linus torvalds在1991年開發出來的,之後在gnu的支援下,linux獲得了巨大的發展。雖然linux在桌面pc機上的普及程度遠不及微軟的windows作業系統,但它的發展速度之快 使用者數量的日益增多,也是微軟所不能輕視的。而近些年來...
嵌入式Linux系統啟動過程
乙個嵌入式 linux 系統從軟體角度看可以分為四個部分 引導引導程式 bootloader linux 核心,檔案系統,應用程式。當系統首次引導時,或系統被重置時,處理器會執行乙個位於flash rom中的已知位置處的 bootloader就是這第一段 它主要用來初始化處理器及外設,然後呼叫 li...