天天看點

Linux核心分析:Linux核心啟動流程分析

本文介紹了Linux核心的啟動流程。

(注:本文參考資料:朱有鵬嵌入式課程、大神部落格。本文為個人學習記錄,如有錯誤,歡迎指正。核心版本:九鼎公司移植的2.6.35.7)

1. Linux核心自解壓過程

uboot完成系統引導以後,執行環境變量bootm中的指令;即,将Linux核心調入記憶體中并調用do_bootm函數啟動核心,跳轉至kernel的起始位置。如果核心沒有被壓縮,則直接啟動;如果核心被壓縮過,則需要進行解壓,被壓縮過的kernel頭部有解壓程式。

壓縮過的kernel入口第一個檔案源碼位置在/kernel/arch/arm/boot/compressed/head.S。它将調用decompress_kernel()函數進行解壓,解壓完成後,列印出資訊“Uncompressing Linux...done,booting the kernel”。解壓縮完成後,調用gunzip()函數(或unlz4()、或bunzip2()、或unlz())将核心放于指定位置,開始啟動核心。

P.S.:核心格式類型詳見。

2. Linux核心啟動準備階段

由核心連結腳本/kernel/arch/arm/kernel/vmlinux.lds可知,核心入口函數為stext(/kernel/arch/arm/kernel/head.S)。核心解壓完成後,解壓縮代碼調用stext函數啟動核心。

P.S.:核心連結腳本vmlinux.lds在核心配置過程中産生,由/kernel/arch/arm/kernel/vmlinux.lds.S檔案生成。原因是,核心連結腳本為适應不同平台,有條件編譯的需求,故由一個彙編檔案來完成連結腳本的制作。

ENTRY(stext)
setmodePSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode@ and irqs disabled
mrcp15, 0, r9, c0, c0            @ 獲得處理器ID,并存儲在r9寄存器中
bl__lookup_processor_type        @ 結果傳回:描述處理器結構體的位址 r5=procinfo ,處理器ID号 r9=cpuid
movsr10, r5                      @ invalid processor (r5=0)?判斷核心是否支援該處理器
beq__error_p                     @ yes, error 'p'
bl__lookup_machine_type          @結果傳回:描述機器(開發闆)的結構體位址  r5=machinfo
movsr8, r5                       @ invalid machine (r5=0)?判斷核心是否支援該機器(開發闆)
beq__error_a                     @ yes, error 'a'
bl__vet_atags                    @檢查uboot給核心的傳參ATAGS格式是否正确
bl__create_page_tables           @建立虛拟位址映射頁表

ldrr13, __switch_data            @ address to jump to after      

(1)關閉IRQ、FIQ中斷,進入SVC模式。調用setmode宏實作;

(2)校驗處理器ID,檢驗核心是否支援該處理器;若不支援,則停止啟動核心。調用__lookup_processor_type函數實作;

(3)校驗機器碼,檢驗核心是否支援該機器;若不支援,則停止啟動核心。調用__lookup_machine_type函數實作;

(4)檢查uboot向核心傳參ATAGS格式是否正确,調用__vet_atars函數實作;

(5)建立虛拟位址映射頁表。此處建立的頁表為粗頁表,在核心啟動前期使用。Linux對記憶體管理有更精細的要求,随後會重建立立更精細的頁表。調用__create_page_tables函數實作。

(6)跳轉執行__switch_data函數,其中調用__mmap_switched完成最後的準備工作。

        1)複制資料段、清除bss段,目的是建構C語言運作環境;

        2)儲存處理器ID号、機器碼、uboot向核心傳參位址;

        3)b   start_kernel跳轉至核心初始化階段。

__switch_data:
.long__mmap_switched
..........................................................
__mmap_switched:
adrr3, __switch_data + 4

ldmiar3!, {r4, r5, r6, r7}
cmpr4, r5@ Copy data segment if needed
1:cmpner5, r6
ldrnefp, [r4], #4
strnefp, [r5], #4
bne1b

movfp, #0@ Clear BSS (and zero fp)
1:cmpr6, r7
strccfp, [r6],#4
bcc1b

 ARM(ldmiar3, {r4, r5, r6, r7, sp})
 THUMB(ldmiar3, {r4, r5, r6, r7})
 THUMB(ldrsp, [r3, #16])
strr9, [r4]@ Save processor ID
strr1, [r5]@ Save machine type
strr2, [r6]@ Save atags pointer
bicr4, r0, #CR_A@ Clear 'A' bit
stmiar7, {r0, r4}@ Save control register values
bstart_kernel
ENDPROC(__mmap_switched)      

3. Linux核心初始化階段

此階段從start_kernel函數開始。start_kernel函數是所有Linux平台進入系統核心初始化的入口函數。它的主要工作是完成剩餘與硬體平台相關的初始化工作,在進行一系列與核心相關的初始化之後,調用第一個使用者程序init并等待其執行。至此,整個核心啟動完成。

3.1 start_kernel函數的主要工作

start_kernel函數主要完成核心相關的初始化工作。具體包括以下部分:

(1)核心架構 、通用配置相關初始化 

(2) 記憶體管理相關初始化

(3)程序管理相關初始化 

(4)程序排程相關初始化

(5)網絡子系統管理

(6)虛拟檔案系統

(7)檔案系統 

start_kernel函數詳解。

3.2 start_kernel函數流中的關鍵函數

Linux核心分析:Linux核心啟動流程分析

(1)setup_arch(&command_line)函數

核心架構相關的初始化函數,是非常重要的一個初始化步驟。其中,包含了處理器相關參數的初始化、核心啟動參數(tagged list)的擷取和前期處理、記憶體子系統的早期初始化。

command_line實質是uboot向核心傳遞的指令行啟動參數,即uboot中環境變量bootargs的值。若uboot中bootargs的值為空,command_line = default_command_line,即為核心中的預設指令行參數,其值在.config檔案中配置,對應CONFIG_CMDLINE配置項。

(2)setup_command_line、parse_early_param以及parse_args函數

這些函數都是在完成指令行參數的解析、儲存。譬如,cmdline = console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3;解析為一下四個參數:

  • console=ttySAC2,115200      //指定控制台的序列槽裝置号,及其波特率
  • root=/dev/mmcblk0p2 rw    //指定根檔案系統rootfs的路徑
  • init=/linuxrc                           //指定第一個使用者程序init的路徑
  • rootfstype=ext3                    //指定根檔案系統rootfs的類型

(3)sched_init函數

初始化程序排程器,建立運作隊列,設定目前任務的空線程。

(4)rest_init函數

rest_init函數的主要工作如下:

    1)調用kernel_thread函數啟動了2個核心線程,分别是:kernel_init和kthreadd。kernel_init線程中調用prepare_namespace函數挂載根檔案系統rootfs;然後調用init_post函數,執行根檔案系統rootfs下的第一個使用者程序init。使用者程序有4個備選方案,若command_line中init的路徑錯誤,則會執行備用方案。第一備用:/sbin/init,第二備用:/etc/init,第三備用:/bin/init,第四備用:/bin/sh。

    2)調用schedule函數開啟核心排程系統;

    3)調用cpu_idle函數,啟動空閑程序idle,完成核心啟動。