国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

linux內(nèi)核啟動(dòng)以及文件系統(tǒng)的加載過(guò)程

原創(chuàng) 2016-11-09 13:26:24 499
摘要:當(dāng)u-boot 開(kāi)始執(zhí)行 bootcmd 命令,就進(jìn)入 Linux 內(nèi)核啟動(dòng)階段。普通 Linux 內(nèi)核的啟動(dòng)過(guò)程也可以分為兩個(gè)階段。本文以項(xiàng)目中使用的 linux-2.6.37 版源碼為例分三個(gè)階段來(lái)描述內(nèi)核啟動(dòng)全過(guò)程。第一階段為內(nèi)核自解壓過(guò)程,第二階段主要工作是設(shè)置ARM處理器工作模式、使能 MMU 、設(shè)置一級(jí)頁(yè)表等,而第三階段則主要為C代碼,包括內(nèi)核初始化的全部工作。一、 Linux 內(nèi)核自

當(dāng)u-boot 開(kāi)始執(zhí)行 bootcmd 命令,就進(jìn)入 Linux 內(nèi)核啟動(dòng)階段。普通 Linux 內(nèi)核的啟動(dòng)過(guò)程也可以分為兩個(gè)階段。本文以項(xiàng)目中使用的 linux-2.6.37 版源碼為例分三個(gè)階段來(lái)描述內(nèi)核啟動(dòng)全過(guò)程。第一階段為內(nèi)核自解壓過(guò)程,第二階段主要工作是設(shè)置ARM處理器工作模式、使能 MMU 、設(shè)置一級(jí)頁(yè)表等,而第三階段則主要為C代碼,包括內(nèi)核初始化的全部工作。

一、 Linux 內(nèi)核自解壓過(guò)程

在 linux 內(nèi)核啟動(dòng)過(guò)程中一般能看到內(nèi)核自解壓界面,本小節(jié)本文重點(diǎn)討論內(nèi)核的自解壓過(guò)程。

1.jpg

內(nèi)核壓縮和解壓縮代碼都在目錄     kernel/arch/arm/boot/compressed,編譯完成后將產(chǎn)生head.o、misc.o、piggy.gzip.o、vmlinux、decompress.o 這幾個(gè)文件。

head.o 是內(nèi)核的頭部文件,負(fù)責(zé)初始設(shè)置;    

misc.o 將主要負(fù)責(zé)內(nèi)核的解壓工作,它在head.o之后;    

piggy.gzip.o 是一個(gè)中間文件,其實(shí)是一個(gè)壓縮的內(nèi)核( kernel/vmlinux ),只不過(guò)沒(méi)有和初始化文件及解壓文件鏈接而已;    

vmlinux 是沒(méi)有(zImage是壓縮過(guò)的內(nèi)核)壓縮過(guò)的內(nèi)核,就是由 piggy.gzip.o、head.o、misc.o 組成的;

decompress.o 是為支持更多的壓縮格式而新引入的。

在  BootLoader完成系統(tǒng)的引導(dǎo)以后并將Linux內(nèi)核調(diào)入內(nèi)存之后,調(diào)用do_bootm_linux(),這個(gè)函數(shù)將跳轉(zhuǎn)到kernel的起始位置。如果kernel沒(méi)有被壓縮,就可以啟動(dòng)了。如果kernel被壓縮過(guò),則要進(jìn)行解壓,在壓縮過(guò)的kernel頭部有解壓程序。壓縮過(guò)的kernel入口第一個(gè)文件源碼位置在 arch/arm/boot/compressed/head.S。

它將調(diào)用函數(shù) decompress_kernel(),這個(gè)函數(shù)在文件arch/arm/boot/compressed/misc.c 中,decompress_kernel() 又調(diào)用  proc_decomp_setup(),arch_decomp_setup() 進(jìn)行設(shè)置,然后打印出信息“ Uncompressing Linux...  ”后,調(diào)用gunzip()     將內(nèi)核放于指定的位置。

下面簡(jiǎn)單介紹一下解壓縮過(guò)程,也就是函數(shù)decompress_kernel實(shí)現(xiàn)的功能:解壓縮代碼位于 kernel/lib/inflate.c,inflate.c是從gzip 源程序中分離出來(lái)的,包含了一些對(duì)全局?jǐn)?shù)據(jù)的直接引用,在使用時(shí)需要直接嵌入到代碼中。gzip壓縮文件時(shí)總是在前32K字節(jié)的范圍內(nèi)尋找重復(fù)的字符串進(jìn)行編碼, 在解壓時(shí)需要一個(gè)至少為     32K     字節(jié)的解壓緩沖區(qū),它定義為     window[WSIZE] 。inflate.c 使用 get_byte()     讀取輸入文件,它被定義成宏來(lái)提高效率。輸入緩沖區(qū)指針必須定義為inptr,inflate.c中對(duì)之有減量操作。 inflate.c調(diào)用 flush_window()來(lái)輸出 window 緩沖區(qū)中的解壓出的字節(jié)串,每次輸出長(zhǎng)度用 outcnt變量表示。在 flush_window()  中,還必須對(duì)輸出字節(jié)串計(jì)算CRC并且刷新crc變量。在調(diào)用  gunzip() 開(kāi)始解壓之前,調(diào)用 makecrc() 初始化CRC     計(jì)算表。最后 gunzip()  返回  0 表示解壓成功。我們?cè)趦?nèi)核啟動(dòng)的開(kāi)始都會(huì)看到這樣的輸出:  

UncompressingLinux...done, booting the kernel.  

這也是由 decompress_kernel函數(shù)輸出的,執(zhí)行完解壓過(guò)程,再返回到head.S中的583行,啟動(dòng)內(nèi)核

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 中已經(jīng)在head.S的第180行處預(yù)置為內(nèi)核鏡像的地址,如下代碼:  

#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

這樣就進(jìn)入Linux內(nèi)核的第一階段,我們也稱之為stage1     。

二、 Linux 內(nèi)核啟動(dòng)第一階段  stage1       

承接上文,這里所以說(shuō)的第一階段 stage1 就是內(nèi)核解壓完成并出現(xiàn) Uncompressing Linux...done,booting the kernel. 之后的階段。該部分代碼實(shí)現(xiàn)在arch/arm/kernel 的 head.S中,該文件中的匯編代碼通過(guò)查找處理器內(nèi)核類型和機(jī)器碼類型調(diào)用相應(yīng)的初始化函數(shù),再建 立頁(yè)表,最后跳轉(zhuǎn)到start_kernel() 函數(shù)開(kāi)始內(nèi)核的初始化工作。檢測(cè)處理器類型是在匯編子函數(shù)__lookup_processor_type     中完成的,通過(guò)以下代碼可實(shí)現(xiàn)對(duì)它的調(diào)用: bl__lookup_processor_type  (在文件head-commom.S     實(shí)現(xiàn))。 __lookup_processor_type調(diào)用結(jié)束返回原程序時(shí),會(huì)將返回結(jié)果保存到寄存器中。其中r5 寄存器返回一個(gè)用來(lái)描述處理器的結(jié)構(gòu)體地址,并對(duì)r5進(jìn)行判斷,如果r5的值為0則說(shuō)明不支持這種處理器,將進(jìn)入 __error_p 。r8 保存了頁(yè)表的標(biāo)志位, r9 保存了處理器的 ID 號(hào),r10保存了與處理器相關(guān)的struct proc_info_list結(jié)構(gòu)地址。Head.S 核心代碼如下:  

ENTRY(stext)
setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @設(shè)置SVC模式關(guān)中斷
  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,則不支持當(dāng)前處理器'  bl    __lookup_machine_type       @ 調(diào)用函數(shù),返回值r5=machinfo
  movs    r8, r5        @ 如果返回值r5=0,則不支持當(dāng)前機(jī)器(開(kāi)發(fā)板)
THUMB( it   eq )         @ force fixup-able long branch encoding
  beq __error_a             @ 機(jī)器碼不匹配,轉(zhuǎn)__error_a并打印錯(cuò)誤信息
  bl    __vet_atags
#ifdef CONFIG_SMP_ON_UP    @ 如果是多核處理器進(jìn)行相應(yīng)設(shè)置
  bl    __fixup_smp#endif
  bl    __create_page_tables  @最后開(kāi)始創(chuàng)建頁(yè)表

    檢測(cè)機(jī)器碼類型是在匯編子函數(shù)__lookup_machine_type (同樣在文件head-common.S 實(shí)現(xiàn)) 中完成的。與 __lookup_processor_type類似,通過(guò)代碼:“ bl __lookup_machine_type”來(lái)實(shí)現(xiàn)對(duì)它的調(diào) 用。該函數(shù)返回時(shí),會(huì)將返回結(jié)構(gòu)保存放在r5、r6 和  r7三個(gè)寄存器中。其中r5 寄存器返回一個(gè)用來(lái)描述機(jī)器(也就是開(kāi)發(fā)板)的結(jié)構(gòu)體地址,并對(duì)r5進(jìn)行判斷,如果r5的值為0 ,則說(shuō)明不支持這種機(jī)器(開(kāi)發(fā)板),將進(jìn)入__error_a, 打印出內(nèi)核不支持u-boot傳入的機(jī)器碼的錯(cuò)誤如圖2。  r6保存了 I/O基地址, r7保存了 I/O 的頁(yè)表偏移地址。 

當(dāng)檢測(cè)處理器類型和機(jī)器碼類型結(jié)束后,將調(diào)用 __create_page_tables子函數(shù)來(lái)建立頁(yè)表,它所要做的工作就是將 RAM 基地址開(kāi)始的1M 空間的物理地址映射到 0xC0000000開(kāi)始的虛擬地址處。對(duì)本項(xiàng)目的開(kāi)發(fā)板DM3730 而言,RAM  掛接到物理地址  0x80000000  處,當(dāng)調(diào)用  __create_page_tables  結(jié)束后 0x80000000 ~ 0x80100000 物理地址將映射到  0xC0000000~0xC0100000 虛擬地址處。當(dāng)所有的初始化結(jié)束之后,使用如下代碼來(lái)跳到 C 程序的入口函數(shù)start_kernel()處,開(kāi)始之后的內(nèi)核初始化工作:bSYMBOL_NAME(start_kernel)  。

三、Linux內(nèi)核啟動(dòng)第二階段 stage2              

 從start_kernel函數(shù)開(kāi)始       

Linux內(nèi)核啟動(dòng)的第二階段從start_kernel函數(shù)開(kāi)始。start_kernel 是所有Linux 平臺(tái)進(jìn)入系統(tǒng)內(nèi)核初始化后的入口函數(shù),它主要完成剩余的與 硬件平臺(tái)相關(guān)的初始化工作,在進(jìn)行一系列與內(nèi)核相關(guān)的初始化后,調(diào)用第一個(gè)用戶進(jìn)程-init進(jìn)程并等待用戶進(jìn)程的執(zhí)行,這樣整個(gè)Linux內(nèi)核便啟動(dòng)完畢。該函數(shù)位于 init/main.c文件中,主要工作流程如圖 所示:       

uyEZ3m.jpg                                                         

該函數(shù)所做的具體工作有 :

1) 調(diào)用 setup_arch() 函數(shù)進(jìn)行與體系結(jié)構(gòu)相關(guān)的第一個(gè)初始化工作;對(duì)不同的體系結(jié)構(gòu)來(lái)說(shuō)該函數(shù)有不同的定義。對(duì)于ARM平臺(tái)而言,該函數(shù)定義在arch/arm/kernel/setup.c 。它首先通過(guò)檢測(cè)出來(lái)的處理器類型進(jìn)行處理器內(nèi)核的初始化,然后 通過(guò)bootmem_init()函數(shù)根據(jù)系統(tǒng)定義的 meminfo結(jié)構(gòu)進(jìn)行內(nèi)存結(jié)構(gòu)的初始化,最后調(diào)用paging_init()開(kāi)啟MMU,創(chuàng)建內(nèi)核頁(yè)表,映射所有的物理內(nèi)存和 IO空間。        

2) 創(chuàng)建異常向量表和初始化中斷處理函數(shù);   

3) 初始化系統(tǒng)核心進(jìn)程調(diào)度器和時(shí)鐘中斷處理機(jī)制;   

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

ARM-Linux 在初始化過(guò)程中一般都會(huì)初始化一個(gè)串口做為內(nèi)核的控制臺(tái),而串口Uart驅(qū)動(dòng)卻把串口設(shè)備名寫(xiě)死了,如本例中 linux2.6.37串口設(shè)備名為 ttyO0,而不是常用的ttyS0。有了控制臺(tái)內(nèi)核在啟動(dòng)過(guò)程中就可以通過(guò)串口輸出信息以便開(kāi)發(fā)者或用戶了解系統(tǒng)的啟動(dòng)進(jìn)程。        

5) 創(chuàng)建和初始化系統(tǒng) cache,為各種內(nèi)存調(diào)用機(jī)制提供緩存,包括;動(dòng)態(tài)內(nèi)存分配,虛擬文件系統(tǒng)(VirtualFile System )及頁(yè)緩存。        

6) 初始化內(nèi)存管理,檢測(cè)內(nèi)存大小及被內(nèi)核占用的內(nèi)存情況;   

7) 初始化系統(tǒng)的進(jìn)程間通信機(jī)制(IPC); 當(dāng)以上所有的初始化工作結(jié)束后, start_kernel() 函數(shù)會(huì)調(diào)用 rest_init()  函數(shù)來(lái)進(jìn)行最后的初始化,包括創(chuàng)建系統(tǒng)的第一個(gè)進(jìn)程-init  進(jìn)程來(lái)結(jié)束內(nèi)核的啟動(dòng)。       

掛載根文件系統(tǒng)并啟動(dòng) init              

Linux 內(nèi)核啟動(dòng)的下一過(guò)程是啟動(dòng)第一個(gè)進(jìn)程 init ,但必須以根文件系統(tǒng)為載體,所以在啟動(dòng)init 之前,還要掛載根文件系統(tǒng)。       

四、掛載根文件系統(tǒng)       

根文件系統(tǒng)至少包括以下目錄:  

       /etc/ :存儲(chǔ)重要的配置文件。       

       /bin/  :存儲(chǔ)常用且開(kāi)機(jī)時(shí)必須用到的執(zhí)行文件。       

   /sbin/ :存儲(chǔ)著開(kāi)機(jī)過(guò)程中所需的系統(tǒng)執(zhí)行文件。       

   /lib/   :存儲(chǔ)/bin/及/sbin/的執(zhí)行文件所需的鏈接庫(kù),以及Linux的內(nèi)核模塊。       

      /dev/ :存儲(chǔ)設(shè)備文件。       

  注:五大目錄必須存儲(chǔ)在根文件系統(tǒng)上,缺一不可。  

以只讀的方式掛載根文件系統(tǒng),之所以采用只讀的方式掛載根文件系統(tǒng)是因?yàn)椋捍藭r(shí)Linux內(nèi)核仍在啟動(dòng)階段,還不是很穩(wěn)定,如果采用可讀可寫(xiě)的方式掛載根文件系統(tǒng),萬(wàn)一Linux不小心宕機(jī)了,一來(lái)可能破壞根文件系統(tǒng)上的數(shù)據(jù),再者Linux下次開(kāi)機(jī)時(shí)得花上很長(zhǎng)的時(shí)間來(lái)檢查并修復(fù)根文件系統(tǒng)。       

    掛載根文件系統(tǒng)的而目的有兩個(gè):一是安裝適當(dāng)?shù)膬?nèi)核模塊,以便驅(qū)動(dòng)某些硬件設(shè)備或啟用某些功能;二是啟動(dòng)存儲(chǔ)于文件系統(tǒng)中的init 服務(wù),以便讓 init服務(wù)接手后續(xù)的啟動(dòng)工作。    

執(zhí)行 init 服務(wù)       

Linux內(nèi)核啟動(dòng)后的最后一個(gè)動(dòng)作,就是從根文件系統(tǒng)上找出并執(zhí)行init服務(wù)。 Linux內(nèi)核會(huì)依照下列的順序?qū)ふ襥nit服務(wù):       

1)       /sbin/ 是否有 init 服務(wù)       

2)       /etc/ 是否有init 服務(wù)       

3)       /bin/ 是否有 init 服務(wù)       

4)如果都找不到最后執(zhí)行/bin/sh              

找到 init服務(wù)后,  Linux會(huì)讓 init 服務(wù)負(fù)責(zé)后續(xù)初始化系統(tǒng)使用環(huán)境的工作, init啟動(dòng)后,就代表系統(tǒng)已經(jīng)順利地啟動(dòng)了linux內(nèi)核。

啟動(dòng)init服務(wù)時(shí),init服務(wù)會(huì)讀取/etc/inittab文件,根據(jù)/etc/inittab中的設(shè)置數(shù)據(jù)進(jìn)行初始化系統(tǒng)環(huán)境的工作。 /etc/inittab定義 init 服務(wù)在 linux啟動(dòng)過(guò)程中必須依序執(zhí)行以下幾個(gè)Script              :       

        /etc/rc.d/rc.sysinit              

        /etc/rc.d/rc              

  /etc/rc.d/rc.local       

  /etc/rc.d/rc.sysinit主要的功能是設(shè)置系統(tǒng)的基本環(huán)境,當(dāng)init服務(wù)執(zhí)行rc.sysinit時(shí) 要依次完成下面一系列工作:       

(1)啟動(dòng)udev              

(2)設(shè)置內(nèi)核參數(shù)  

執(zhí)行sysctl –p ,以便從       /etc/sysctl.conf 設(shè)置內(nèi)核參數(shù)       

(3)設(shè)置系統(tǒng)時(shí)間  

將硬件時(shí)間設(shè)置為系統(tǒng)時(shí)間  

(4)啟用交換內(nèi)存空間  

執(zhí)行 swpaon –a –e,以便根據(jù)/etc/fstab的設(shè)置啟用所有的交換內(nèi)存空間。       

(5)檢查并掛載所有文件系統(tǒng)  

檢查所有需要掛載的文件系統(tǒng),以確保這些文件系統(tǒng)的完整性。檢查完畢后以可讀可寫(xiě)的方式掛載文件系統(tǒng)。  

(6)初始化硬件設(shè)備  

      Linux除了在啟動(dòng)內(nèi)核時(shí)以靜態(tài)驅(qū)動(dòng)程序驅(qū)動(dòng)部分的硬件外,在執(zhí)行rc.sysinit 時(shí),也會(huì)試著驅(qū)動(dòng)剩余的硬件設(shè)備。  r    c.sysinit 驅(qū)動(dòng)的硬件設(shè)備包含以下幾項(xiàng):       

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

  b)       ISA PnP的硬件設(shè)備       

  c)       USB設(shè)備       

(7)初始化串行端口設(shè)備  

        Init服務(wù)會(huì)管理所有的串行端口設(shè)備,比如調(diào)制解調(diào)器、不斷電系統(tǒng)、串行端口控制臺(tái)等。Init  服務(wù)則通過(guò)rc.sysinit來(lái)初始化linux 的串行端口設(shè)備。當(dāng)rc.sysinit 發(fā)現(xiàn) linux 才能在這  /etc/rc.serial 時(shí),才會(huì)執(zhí)行 /etc/rc.serial ,借以初始化所有的串行端口設(shè)備。因此,你可以在 /etc/rc.serial 中定義如何初始化 linux所有的串行端口設(shè)備。       

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

(9)建立用戶接口  

在執(zhí)行完3個(gè)主要的 RC Script 后, init服務(wù)的最后一個(gè)工作,就是建立linux的用戶界面,好讓用戶可以使用 linux  。此時(shí)init  服務(wù)會(huì)執(zhí)行以下兩項(xiàng)工作:       

(10)建立虛擬控制臺(tái)  

        Init 會(huì)在若干個(gè)虛擬控制臺(tái)中執(zhí)行  /bin/login,以便用戶可以從虛擬控制臺(tái)登陸  linux 。 linux 默認(rèn)在前6個(gè)虛擬控制臺(tái),也就是 tty1~tty6 ,執(zhí)行  /bin/logi 登陸程序。當(dāng)所有的初始化工作結(jié)束后,cpu_idle()函數(shù)會(huì)被調(diào)用來(lái)使系統(tǒng)處于閑置(  idle)狀態(tài)并等待用戶程序的執(zhí)行。至此,整個(gè)Linux內(nèi)核啟動(dòng)完畢。  

2.jpg

發(fā)佈手記

熱門詞條