嵌入式Linux內核啟動主要分為這三個階段

2021-01-08 IT168

  【IT168 資訊】嵌入式linux內核的啟動全過程主要分為三個階段。第一階段為內核自解壓過程,第二階段主要工作是設置ARM處理器工作模式、使能MMU、設置一級頁表等,而第三階段則主要為C代碼,包括內核初始化的全部工作,下面是詳細介紹。

  一、Linux內核自解壓過程

  在linux內核啟動過程中一般能看到圖1內核自解壓界面,這裡重點討論內核的自解壓過程。

  內核壓縮和解壓縮代碼都在目錄kernel/arch/arm/boot/compressed,編譯完成後將產生head.o、misc.o、piggy.gzip.o、vmlinux、decompress.o這幾個文件,head.o是內核的頭部文件,負責初始設置;misc.o將主要負責內核的解壓工作,它在head.o之後;piggy.gzip.o是一個中間文件,其實是一個壓縮的內核(kernel/vmlinux),只不過沒有和初始化文件及解壓文件連結而已;vmlinux是沒有(zImage是壓縮過的內核)壓縮過的內核,就是由piggy.gzip.o、head.o、misc.o組成的,而decompress.o是為支持更多的壓縮格式而新引入的。

  圖1 解壓內核

  在BootLoader完成系統的引導以後並將Linux內核調入內存之後,調用do_bootm_linux(),這個函數將跳轉到kernel的起始位置。如果kernel沒有被壓縮,就可以啟動了。如果kernel被壓縮過,則要進行解壓,在壓縮過的kernel頭部有解壓程序。壓縮過的kernel入口第一個文件源碼位置在arch/arm/boot/compressed/head.S。它將調用函數decompress_kernel(),這個函數在文件arch/arm/boot/compressed/misc.c中,decompress_kernel()又調用proc_decomp_setup(),arch_decomp_setup()進行設置,然後列印出信息「Uncompressing Linux...」後,調用gunzip()將內核放於指定的位置。

  下面簡單介紹一下解壓縮過程,也就是函數decompress_kernel實現的功能:解壓縮代碼位於kernel/lib/inflate.c,inflate.c是從gzip源程序中分離出來的,包含了一些對全局數據的直接引用,在使用時需要直接嵌入到代碼中。gzip壓縮文件時總是在前32K字節的範圍內尋找重複的字符串進行編碼, 在解壓時需要一個至少為32K字節的解壓緩衝區,它定義為window[WSIZE]。inflate.c使用get_byte()讀取輸入文件,它被定義成宏來提高效率。輸入緩衝區指針必須定義為inptr,inflate.c中對之有減量操作。inflate.c調用flush_window()來輸出window緩衝區中的解壓出的字節串,每次輸出長度用outcnt變量表示。在flush_window()中,還必須對輸出字節串計算CRC並且刷新crc變量。在調用gunzip()開始解壓之前,調用makecrc()初始化CRC計算表。最後gunzip()返回0表示解壓成功。我們在內核啟動的開始都會看到這樣的輸出:

  UncompressingLinux...done, booting the kernel.

  這也是由decompress_kernel函數輸出的,執行完解壓過程,再返回到head.S中的583行,啟動內核

  其中r4中已經在head.S的第180行處預置為內核鏡像的地址,如下代碼:

  這樣就進入Linux內核的第一階段,我們也稱之為stage1。

  二、Linux內核啟動第一階段stage1

  承接上文,這裡所以說的第一階段stage1就是內核解壓完成並出現Uncompressing Linux...done,booting the kernel.之後的階段。

  該部分代碼實現在arch/arm/kernel的 head.S中,該文件中的彙編代碼通過查找處理器內核類型和機器碼類型調用相應的初始化函數,再建 立頁表,最後跳轉到start_kernel()函數開始內核的初始化工作。檢測處理器類型是在彙編子函數__lookup_processor_type中完成的,通過以下代碼可實現對它的調用:bl__lookup_processor_type(在文件head-commom.S實現)。__lookup_processor_type調用結束返回原程序時,會將返回結果保存到寄存器中。其中r5寄存器返回一個用來描述處理器的結構體地址,並對r5進行判斷,如果r5的值為0則說明不支持這種處理器,將進入__error_p。r8保存了頁表的標誌位,r9 保存了處理器的ID 號,r10保存了與處理器相關的struct proc_info_list結構地址。Head.S核心代碼如下:

  檢測機器碼類型是在彙編子函數__lookup_machine_type (同樣在文件head-common.S實現) 中完成的。與__lookup_processor_type類似,通過代碼:「bl __lookup_machine_type」來實現對它的調 用。該函數返回時,會將返回結構保存放在r5、r6 和r7三個寄存器中。其中r5寄存器返回一個用來描述機器(也就是開發板)的結構體地址,並對r5進行判斷,如果r5的值為0則說明不支持這種機器(開發板),將進入__error_a,列印出內核不支持u-boot傳入的機器碼的錯誤如圖2。r6保存了I/O基地址,r7 保存了 I/O的頁表偏移地址。 當檢測處理器類型和機器碼類型結束後,將調用__create_page_tables子函數來建立頁表,它所要做的工作就是將 RAM 基地址開始的1M 空間的物理地址映射到 0xC0000000開始的虛擬地址處。對本項目的開發板DM3730而言,RAM掛接到物理地址0x80000000處,當調用__create_page_tables 結束後 0x80000000 ~ 0x80100000物理地址將映射到 0xC0000000~0xC0100000虛擬地址處。當所有的初始化結束之後,使用如下代碼來跳到C 程序的入口函數start_kernel()處,開始之後的內核初始化工作: bSYMBOL_NAME(start_kernel) 。

  圖2 機器碼不匹配錯誤

  三、Linux內核啟動第二階段stage2

  從start_kernel函數開始

  Linux內核啟動的第二階段從start_kernel函數開始。start_kernel是所有Linux平臺進入系統內核初始化後的入口函數,它主要完成剩餘的與 硬體平臺相關的初始化工作,在進行一系列與內核相關的初始化後,調用第一個用戶進程- init 進程並等待用戶進程的執行,這樣整個 Linux內核便啟動完畢。該函數位於init/main.c文件中,主要工作流程如圖3所示:

  圖3 start_kernel流程圖

  該函數所做的具體工作有 :

  1) 調用setup_arch()函數進行與體系結構相關的第一個初始化工作;對不同的體系結構來說該函數有不同的定義。對於ARM平臺而言,該函數定義在 arch/arm/kernel/setup.c。它首先通過檢測出來的處理器類型進行處理器內核的初始化,然後 通過bootmem_init()函數根據系統定義的meminfo結構進行內存結構的初始化,最後調用 paging_init()開啟MMU,創建內核頁表,映射所有的物理內存和IO空間。

  2) 創建異常向量表和初始化中斷處理函數;

  3) 初始化系統核心進程調度器和時鐘中斷處理機制;

  4) 初始化串口控制臺(console_init);

  ARM-Linux 在初始化過程中一般都會初始化一個串口做為內核的控制臺,而串口Uart驅動卻把串口設備名寫死了,如本例中linux2.6.37串口設備名為ttyO0,而不是常用的ttyS0。有了控制臺內核在啟動過程中就可以通過串口輸出信息以便開發者或用戶了解系統的啟動進程。

  5) 創建和初始化系統cache,為各種內存調用機制提供緩存,包括;動態內存分配,虛擬文件系統(VirtualFile System)及頁緩存。

  6) 初始化內存管理,檢測內存大小及被內核佔用的內存情況;

  7) 初始化系統的進程間通信機制(IPC); 當以上所有的初始化工作結束後,start_kernel()函數會調用rest_init()函數來進行最後的初始化,包括創建系統的第一個進程-init進程來結束內核的啟動。

  掛載根文件系統並啟動init

  Linux內核啟動的下一過程是啟動第一個進程init,但必須以根文件系統為載體,所以在啟動init之前,還要掛載根文件系統。

  四、掛載根文件系統

  根文件系統至少包括以下目錄:

  /etc/:存儲重要的配置文件。

  /bin/:存儲常用且開機時必須用到的執行文件。

  /sbin/:存儲著開機過程中所需的系統執行文件。

  /lib/:存儲/bin/及/sbin/的執行文件所需的連結庫,以及Linux的內核模塊。

  /dev/:存儲設備文件。

  註:五大目錄必須存儲在根文件系統上,缺一不可。

  以只讀的方式掛載根文件系統,之所以採用只讀的方式掛載根文件系統是因為:此時Linux內核仍在啟動階段,還不是很穩定,如果採用可讀可寫的方式掛載根文件系統,萬一Linux不小心宕機了,一來可能破壞根文件系統上的數據,再者Linux下次開機時得花上很長的時間來檢查並修復根文件系統。

  掛載根文件系統的而目的有兩個:一是安裝適當的內核模塊,以便驅動某些硬體設備或啟用某些功能;二是啟動存儲於文件系統中的init服務,以便讓init服務接手後續的啟動工作。

  執行init服務

  Linux內核啟動後的最後一個動作,就是從根文件系統上找出並執行init服務。Linux內核會依照下列的順序尋找init服務:

  1)/sbin/是否有init服務

  2)/etc/是否有init服務

  3)/bin/是否有init服務

  4)如果都找不到最後執行/bin/sh

  找到init服務後,Linux會讓init服務負責後續初始化系統使用環境的工作,init啟動後,就代表系統已經順利地啟動了linux內核。啟動init服務時,init服務會讀取/etc/inittab文件,根據/etc/inittab中的設置數據進行初始化系統環境的工作。/etc/inittab定義init服務在linux啟動過程中必須依序執行以下幾個Script:

  /etc/rc.d/rc.sysinit /etc/rc.d/rc /etc/rc.d/rc.local

  /etc/rc.d/rc.sysinit主要的功能是設置系統的基本環境,當init服務執行rc.sysinit時 要依次完成下面一系列工作:

  (1)啟動udev

  (2)設置內核參數

  執行sysctl –p,以便從/etc/sysctl.conf設置內核參數

  (3)設置系統時間

  將硬體時間設置為系統時間

  (4)啟用交換內存空間

  執行swpaon –a –e,以便根據/etc/fstab的設置啟用所有的交換內存空間。

  (5)檢查並掛載所有文件系統

  檢查所有需要掛載的文件系統,以確保這些文件系統的完整性。檢查完畢後以可讀可寫的方式掛載文件系統。

  (6)初始化硬體設備

  Linux除了在啟動內核時以靜態驅動程序驅動部分的硬體外,在執行rc.sysinit時,也會試著驅動剩餘的硬體設備。rc.sysinit驅動的硬體設備包含以下幾項:

  a)定義在/etc/modprobe.conf的模塊

  b)ISA PnP的硬體設備

  c)USB設備

  (7)初始化串行埠設備

  Init服務會管理所有的串行埠設備,比如數據機、不斷電系統、串行埠控制臺等。Init服務則通過rc.sysinit來初始化linux的串行埠設備。當rc.sysinit發現linux才能在這/etc/rc.serial時,才會執行/etc/rc.serial,藉以初始化所有的串行埠設備。因此,你可以在/etc/rc.serial中定義如何初始化linux所有的串行埠設備。

  (8)清除過期的鎖定文件與IPC文件

  (9)建立用戶接口

  在執行完3個主要的RC Script後,init服務的最後一個工作,就是建立linux的用戶界面,好讓用戶可以使用linux。此時init服務會執行以下兩項工作

  (10)建立虛擬控制臺

  Init會在若干個虛擬控制臺中執行/bin/login,以便用戶可以從虛擬控制臺登陸linux。linux默認在前6個虛擬控制臺,也就是tty1~tty6,執行/bin/login登陸程序。當所有的初始化工作結束後,cpu_idle()函數會被調用來使系統處於閒置(idle)狀態並等待用戶程序的執行。至此,整個Linux內核啟動完畢。整個過程見圖4。

  圖4:linux內核啟動及文件系統加載全過程

相關焦點

  • 嵌入式Linux內核啟動主要分為這三個階段
    嵌入式linux內核的啟動全過程主要分為三個階段。***階段為內核自解壓過程,第二階段主要工作是設置ARM處理器工作模式、使能MMU、設置一級頁表等,而第三階段則主要為C代碼,包括內核初始化的全部工作,下面是詳細介紹。
  • ARM在嵌入式linux內核裁剪與移植的應用
    嵌入式linux是大勢所趨,其巨大的市場潛力與醞釀的無限商機必然會吸引眾多的廠商進入這一領域。本文引用地址:http://www.eepw.com.cn/article/149868.htm1 嵌入式linux作業系統Linux是一類Unix計算機作業系統的統稱。Linux作業系統的內核的名字也是Linux.Linux作業系統也是自由軟體和開放原始碼發展中最著名的例子。
  • 嵌入式linux內核的編譯步驟
    嵌入式linux內核的編譯步驟 華清遠見 發表於 2020-06-19 09:30:24   編譯嵌入式Linux內核都是通過make的不同命令來實現的,它的執行配置文件是Makefile。
  • PXA255的嵌入式Linux應用平臺的構建
    嵌入式Linux是由很多體積小且性能高的微內核系統組成,在內核代碼完全開放的前提下,不同領域和不同層次的用戶可以根據自己的應用需要很容易地對內核進行改造,在低成本的前提下,設計和開發出真正滿足自己需要的嵌入式系統。隨著電子市場的發展壯大,高性能低功耗的多媒體手提設備和無線設備越來越受到市場的歡迎,Intel XScale PXA255處理器正是針對這一情況推出的。
  • 帶你閱讀linux內核源碼:linux內核啟動過程分析
    進而你開始搜腸刮肚的想怎麼樣跟這美女套點近乎,建立起溝通的連廊,走進去她的內心,最終抱得美人歸?同樣的道理,當你接觸linux內核這位大美女的時候,你也會有上面的小心思,那我們就來看:linux內核從哪兒來? -- 內核是如何運行起來的,它的啟動過程是怎麼樣的?本文或者後續文章代碼均參考自:linux 4.9.230版本。
  • linux內核啟動流程
    Linux內核啟動及文件系統加載過程   當u-boot開始執行bootcmd命令,就進入Linux內核啟動階段,與u-boot類似,普通Linux內核的啟動過程也可以分為兩個階段,但針對壓縮了的內核如uImage就要包括內核自解壓過程了。本文以linux-2.6.37版源碼為例分三個階段來描述內核啟動全過程。
  • 嵌入式Linux學習方法 適合初學者設計學習計劃
    2.過程以及目的:  這一階段的學習主要是熟悉嵌入式的硬體,為以後的linux驅動打基礎。這一階段的學習主要是了解bootloader的作用,以及通用bootloader---u-boot的知識,剛開始學會用u-boot的各種命令。然後分析u-boot的啟動流程和Makefile以及u-boot加載內核過程。然後按照別人的步驟一步一步的移植內核,多做幾編以熟悉這個過程。然後在熟悉整個u-boot的基礎上,獨自完成u-boot的移植,並添加自己修改。
  • 工程師嵌入式Linux自學筆記及體會
    針對初學嵌入式的廣大朋友們,以下是我在初學接觸嵌入式的過程中整理處的一些資料信息,希望能為大家有所幫助。本文引用地址:http://www.eepw.com.cn/article/278048.htm  一個典型的桌面 Linux 系統包括 3 個主要的軟體層---linux 內核、C 庫和應用程式代碼。
  • 淺談Linux啟動過程
    和Window等其他作業系統一樣,linux的啟動也分為兩個階段:引導(boot)和啟動(startup)。GRUB的作用有以下幾個: - 加載作業系統的內核 - 擁有一個可以讓用戶選擇到底啟動哪個系統的的菜單界面 - 可以調用其他的啟動引導程序,來實現多系統引導GRUB1現在已經逐步被棄用,在大多數現代發行版上它已經被GRUB2所替換。GRUB2通過/boot/grub2/grub.cfg進行配置,最終GRUB定位和加載linux內核程序到內存中,並轉移控制權到內核程序。
  • 詳解bootloader的執行流程與ARM Linux啟動過程分析
    一個嵌入式 Linux 系統從軟體角度看可以分為四個部分:引導加載程序(bootloader), Linux 內核,文件系統,應用程式。 如果能清楚的了解 bootloader 執行流程和 Linux的啟動過程,將有助於明確開發過程中所需的工作,從而加速嵌入式系統的開發過程。而這正是本文的所要研究的內容。
  • 嵌入式Linux NFS 根文件系統的構建及研究
    摘要:在嵌入式Linux系統開發過程中,根文件系統是構建嵌入式Linux系統的重要組成部分。為了方便和簡化嵌入式Linux開發過程中的調試過程,主要研究了如何使用Busybox構建出基本的嵌入式Linux根文件系統,包括Busybox的配置、編譯和安裝。
  • 嵌入式Linux內核移植相關代碼分析
    本文通過整理之前研發的一個項目(ARM7TDMI +uCLinux),分析內核啟動過程及需要修改的文件,以供內核移植者參考。整理過程中也同時參考了眾多網友的帖子,在此謝過。由於整理過程匆忙,難免錯誤及講解的不夠清楚之處,請各位網友指正,這裡提前謝過。本文分以下部分進行介紹:1. Bootloader及內核解壓2. 內核啟動方式介紹3.
  • 詳細說明如何配置嵌入式linux 的nfs開發環境
    nfs作為一種將遠程主機上的分區(目錄)經網絡掛載到本地系統的一種機制,嵌入式開發者經常需要到。本文引用地址:http://www.eepw.com.cn/article/201609/303288.htmnfs有很多實際應用。下面是比較常見的一些:1. 多個機器共享一臺CDROM或者其他設備。這對於在多臺機器中安裝軟體來說更加便宜跟方便。2.
  • 嵌入式作業系統休眠喚醒後自動運行程序的方法
    為了提高系統的啟動速度,通常採用基於休眠技術的方式來實現嵌入式系統的快速啟動[1]。例如,在一些數位電視中,採用休眠技術以後的啟動時間要比原來的啟動時間約快1/3[2]。
  • 嵌入式linux新手入門手記-搭建基本工作平臺
    主要需要的功能是:USB接口單點觸控螢幕,1024*768 TFT-LCD顯示,GUI的用戶程序實現人機互動,100M乙太網,不需要3D以及圖形加速,256M DDR2,256M nand,一個SD卡槽,一個RS232接口。  首先,要進行嵌入式linux開發,當然需要搭建計算機的工作平臺,考慮工作的方便和效率,決定在計算機上安裝獨立linux系統,和window做成雙引導。
  • linux技術談|linux系統內核優化案例之網絡服務參數
    linux系統是一個何以做伺服器,還可以做工作站,更是嵌入式設備的功臣,今天的地球科技幾乎全部都搭建在linux系統之上。當然在桌面護作業系統領域,linux系統的ubuntu等等一批優秀方案正在成為windows現macos的掘墓者。無所不能的linux系統,在作為不同系統出現時,系統參數的個性化設置就成為linux調優的關鍵手段。
  • 基於S3C2440的嵌入式Linux根文件系統構建
    根文件系統一直是Linux系統不可或缺的組件,在嵌入式Lin-ux中,內核在啟動期間進行的最後操作之一就是安裝根文件系統。busybox是構建嵌入式Linux根文件系統的軟體,用它製作根文件系統簡單、方便,而且設置靈活。
  • 基於busybox的嵌入式Linux根文件系統的的製作方法
    根文件系統一直是Linux系統不可或缺的組件,在嵌入式Lin-ux中,內核在啟動期間進行的最後操作之一就是安裝根文件系統。Busybox是構建嵌入式Linux根文件系統的軟體,用它製作根文件系統簡單、方便,而且設置靈活。
  • ARM與嵌入式linux入門建議
    與此相對應的是,越來越多的電子廠商已經開始使用linux開發產品。舉個例子,Google近期開發的智慧型手機作業系統Android其實就是使用linux-2.6.23內核進行改進得到的。本文引用地址:http://www.eepw.com.cn/article/201705/359879.htm  第一,學習基本的裸機編程。
  • 微內核作業系統在嵌入式平臺上的應用
    所謂嵌入式系統是指為特定應用而設計的專用電腦系統,通常執行的是帶有特定要求的預先定義的任務。說到嵌入式系統上的作業系統,人們首先想到的是各種經過裁剪的Linux以及WinCE。但是Linux和WinCE本身作為單內核(monolithic kernel)作業系統,內核部分過於複雜,並不適用於嵌入式平臺。