1. 程式人生 > >linux 核心啟動流程(涉及到根檔案系統的問題)

linux 核心啟動流程(涉及到根檔案系統的問題)

Linux核心啟動及檔案系統載入過程

u-boot開始執行bootcmd命令。就進入Linux核心啟動階段,與u-boot類似,普通Linux核心的啟動過程也能夠分為兩個階段,但針對壓縮了的核心如uImage就要包含核心自解壓過程了。本文以linux-2.6.37版原始碼為例分三個階段來描寫敘述核心啟動全過程。

第一階段為核心自解壓過程。第二階段主要工作是設定ARM處理器工作模式、使能MMU、設定一級頁表等。而第三階段則主要為C程式碼,包含核心初始化的所有工作,以下是具體介紹。

/******************************************************************************************************************************************/


/******************************************************************************************************************************************/

一、Linux核心自解壓過程

linux核心啟動過程中一般能看到圖1核心自解壓介面,本小節本文重點討論核心的自解壓過程。

                

圖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是為支援很多其它的壓縮格式而新引入的。

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行,啟動核心

call_kernel: bl    cache_clean_flush
             bl    cache_off
             mov       r0, #0                   @ must be zero
             mov       r1, r7                   @ restore architecture number
             mov       r2, r8                   @ restore atags pointer
             mov       pc, r4                   @ call kernel

當中r4中已經在head.S的第180行處預置為核心映象的地址。例如以下程式碼:

#ifdef CONFIG_AUTO_ZRELADDR
             @determine final kernel image address
             mov       r4, pc
             and r4, r4, #0xf8000000
             add r4, r4, #TEXT_OFFSET
#else
             ldr   r4, =zreladdr
#endif

這樣就進入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_pr8儲存了頁表的標誌位,r9 儲存了處理器的ID 號,r10儲存了與處理器相關的struct proc_info_list結構地址。

Head.S核心程式碼例如以下:

ENTRY(stext)
setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @設定SVC模式關中斷
      mrc p15, 0, r9, c0, c0        @ 獲得處理器ID。存入r9暫存器
      bl    __lookup_processor_type        @ 返回值r5=procinfo r9=cpuid
      movs      r10, r5                       
 THUMB( it eq )        @ force fixup-able long branch encoding
      beq __error_p                   @假設返回值r5=0,則不支援當前處理器'
      bl    __lookup_machine_type         @ 呼叫函式,返回值r5=machinfo
      movs      r8, r5            @ 假設返回值r5=0。則不支援當前機器(開發板)
THUMB( it   eq )             @ force fixup-able long branch encoding
      beq __error_a                   @ 機器碼不匹配,轉__error_a並列印錯誤資訊
      bl    __vet_atags
#ifdef CONFIG_SMP_ON_UP    @ 假設是多核處理器進行對應設定
      bl    __fixup_smp
#endif
      bl    __create_page_tables  @最後開始建立頁表

檢測機器碼型別是在彙編子函式__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)初始化序列port裝置

Init服務會管理全部的序列port裝置。比方調變解調器、不斷電系統、序列port控制檯等。Init服務則通過rc.sysinit來初始化linux的序列port裝置。

rc.sysinit發現linux才幹在這/etc/rc.serial時。才會執行/etc/rc.serial。藉以初始化全部的序列port裝置。因此,你能夠在/etc/rc.serial中定義怎樣初始化linux全部的序列port裝置。

(8)清除過期的鎖定檔案與IPC檔案

(9)建立使用者介面

在執行完3個基本的RC Script後,init服務的最後一個工作,就是建立linux的使用者介面,好讓使用者能夠使用linux。此時init服務會執行下面兩項工作:

(10)建立虛擬控制檯

Init會在若干個虛擬控制檯中執行/bin/login。以便使用者能夠從虛擬控制檯登陸linuxlinux預設在前6個虛擬控制檯。也就是tty1~tty6,執行/bin/login登陸程式。當全部的初始化工作結束後。cpu_idle()函式會被呼叫來使系統處於閒置(idle)狀態並等待使用者程式的執行。至此。整個Linux核心啟動完成。

整個過程見圖4。

      

                          圖4:linux核心啟動及檔案系統載入全過程

相關推薦

linux 核心啟動流程涉及檔案系統的問題

Linux核心啟動及檔案系統載入過程 當u-boot開始執行bootcmd命令。就進入Linux核心啟動階段,與u-boot類似,普通Linux核心的啟動過程也能夠分為兩個階段,但針對壓縮了的核心如uImage就要包含核心自解壓過程了。本文以linux-2.6.37版原始

linux核心構建最小的檔案系統-一步一步精簡

linux核心init程序函式的部分程式碼如下: 01 if (execute_command) 02 run_init_process(execute_command); 03 04 run_init_process("/sbin/init");

Linux核心啟動流程分析(一)

1. 依據arch/arm/kernel/vmlinux.lds 生成linux核心原始碼根目錄下的vmlinux,這個vmlinux屬於未壓縮,帶除錯資訊、符號表的最初的核心,大小約23MB;  命令:arm-linux-gnu-ld -o vmlinux -T a

國嵌視訊學習---linux核心啟動流程

一、核心檔案uImage的構成 uImage:Uboot header和zImage zImage:解壓程式碼和壓縮後的vmlinux映象 二、zImage核心的構成 其中解壓程式碼由Head.s和misc.s組成。 三、vmlinux核心構成 1.啟動程式碼部分:

Beaglebone Black——理論篇beaglebone black啟動——從串列埠獲得SPL、U-BOOT,TFTP伺服器獲得核心,NFS伺服器掛載檔案系統

          一般來講啟動一個系統所需的bootloader(SPL/MLO、u-boot.img)和根檔案系統(/boot下包含核心zImage)要麼是放在NAND Flash,或者是SD卡,或者是eMMC,或者是USB中,那麼還有一種方式,就是所需要的這些檔案全部

linux核心啟動流程

Linux核心啟動流程  arch/arm/kernel/head-armv.S  該檔案是核心最先執行的一個檔案,包括核心入口ENTRY(stext)到start_kernel間的初始化程式碼,主要作用是檢查CPU ID, Architecture Type,初始化BSS

linux核心啟動流程(文章最後流程圖)

本文以Linux3.14版本原始碼為例分析其啟動流程。各版本啟動程式碼略有不同,但核心流程與思想萬變不離其宗。 核心映像被載入到記憶體並獲得控制權之後,核心啟動流程開始。通常,核心映像以壓縮形式儲存,並不是一個可以執行的核心。因此,核心階段的首要工作是自解壓核心映像。

Linux核心排程分析轉,侵刪

多工 併發和並行 Linux作為一個多工作業系統,必須支援程式的併發執行。 分類 非搶佔式多工    除非任務自己結束,否則將會一直執行。 搶佔式多工(Linux) 這種情況下,由排程程式來決定什麼時候停止一個程序的執行,這個強制的掛起動作即為**“搶佔”**。採用搶佔式多工

開發板通過網路載入核心、裝置樹、檔案系統

開發板要通過網路載入核心、裝置樹、檔案系統,首先要搭建好tftp和nfs服務1.搭建tftp伺服器tftp服務安裝//----虛擬機器上安裝tftp服務$ sudo dpkg   -s   tftpd-hpa   //檢查是否安裝tftp server$ sudo apt-g

Linux學習之目錄結構和檔案系統

目錄結構和根檔案系統 1、Linux結構 2、Linux檔案系統 (1)程式編譯方式 1、動態連結編譯:動態編譯的可執行檔案需要附帶一個動態連結庫,執行時呼叫連結庫中的庫檔案。 2、靜態連結編譯:在編譯時將庫檔案提出來放在該執行檔案中,不依賴動態連結庫 注意:庫檔案只有被呼叫

如何把核心、uboot、和檔案系統下載到開發板中

1、首先準備好所需要的檔案:u-boot、uImage、rootfs.img。 2、下載u-boot: tftp   0x40000000   u-boot.bin nand   erase  0    0x60000 nand    write   ox40000000

linux核心分析——CFS完全公平排程演算法

1.1 CFS原理     cfs定義了一種新的模型,它給cfs_rq(cfs的run queue)中的每一個程序安排一個虛擬時鐘,vruntime。如果一個程序得以執行,隨著時間的增長(也就是一個個tick的到來),其vruntime將不斷增大。沒有得到執行的程序vruntime不變。    而排程器總是選

使用NFS啟動Tiny4412開發板檔案系統

轉載地址: https://www.cnblogs.com/AP0904225/p/6701442.html 1、Ubuntu14.04上搭建NFS服務 1.1、安裝NFS服務          $ sudo ap

LINUX核心設計與實現之虛擬檔案系統

VFS作為核心子系統,為使用者空間程式提供了檔案系統的操作介面.VFS是使用者空間到具體檔案系統(如EXT3)的一個介面中間層. 12.1 通用檔案系統介面 VFS最大的意義就是使用使用者空間可以直接使用open()、read()和write()等等函式而不需要考慮具體的

linux的PC上掛載jffs2檔案系統映像

我們在除錯硬體板時,經常需要做多個jffs2的根檔案系統映像,有時也要對比其他途徑得到的可用的jffs2根檔案系統映像。但jffs2的檔案系統映像不象光碟的映像檔案一樣可以通過loop裝置來掛載,總不可能一個個燒錄到硬體板去看吧。 後來到網上google了一把

十三linux檔案系統詳解基於ext2檔案系統

  我們知道,一個磁碟可以劃分成多個分割槽,每個分割槽必須先用格式化工具(例如某種mkfs命令)格式化成某種格式的檔案系統,然後才能儲存檔案,格式化的過程會在磁碟上寫一些管理儲存佈局的資訊。下圖是一個磁碟分割槽格式化成ext2檔案系統後的儲存佈局:

[hadoop]HDFSHadoop分散式檔案系統

Hadoop的起源: Hadoop是Google的集群系統的開源實現 Google集群系統:GFS(Google File System)、 MapReduce、BigTableHadoop主要由HDFS(Hadoop Distributed File System Ha

android sdcard儲存方案一基於fuse檔案系統

 一、 啟動三個相關service 按啟動順序,如下: service vold /system/bin/vold     class core     socket vold stream 0660 root mount service installd /sy

Java RMI 實現一個簡單的GFS谷歌檔案系統——介紹篇

本系列主要是使用Java RMI實現一個簡單的GFS(谷歌檔案系統,google file system),首先整體簡單介紹下該專案。 > [為了更好的閱讀以及檢視其他篇章,請檢視原文:[https://www.cnblogs.com/maogen/p/gfs_0.html](https://www.cnb

Java RMI 實現一個簡單的GFS谷歌檔案系統——背景與設計篇

[TOC] 本系列主要是使用Java RMI 實現一個簡單的GFS(谷歌檔案系統,google file system),這裡介紹GFS背景以及系統的設計相關。 > [為了更好的閱讀以及檢視其他篇章,請檢視原文:[https://www.cnblogs.com/maogen/p/gfs_1.html](h